HTML form not submitting

I am trying to make a login form in html. But, when I click submit, the JavaScript code doesn’t execute. Here is my JavaScript/jquery code:

$('#login').submit(function(e) {
    e.preventDefault();
    alert('test');
    $('#submit_login').prop('dissabled', true);
    $('span[id$="error"] ').html('').css('desplay', 'none');
    $('#form_message').html('').css('desplay', 'none');
    
    //get the form data
    formData = $(this).serializeArray();
    $.post('handlers/user_handler.php', formData, function(data) {
        let result = JSON.parse(data);
        if(result.status === 'error') {
            $('#' + result.control + '_error').html(result.message).css('desplay', 'inline-block');
        } else {
            window.location = 'http://localhost/Official%20Lasereyes%20Website/public/index.php';
        }
    });
});

Here is my html:

    <form id='login'>
<div class='form_wrapper'>
<div class='control_wrapper'>
<lable for='username'>Email:</lable><br>
<input type='email' id='username' class='form_control' name='username' aria-lable='Type your email address' autocomplete='username' required><br>
<span id='Username_error' class='error error_message'></span>
</div>
<div class='control_wrapper'>
<lable for='password'>Password:</lable><br>
<input id='password' name='password' type='password' minlength='8' aria-lable='Type your password.' autocomplete='current-password' required><br>
<input type='checkbox' id='password_toggle'><lable for='password_toggle' class='lable_horizontal'>Show Password</lable>
</div>
<span id='password_error' class='error error_message'></span>
</div>
<button id='submit_login' class='btn btn_form' type='submit'>Login</button>
<span id='form_error' class='error error_message form_error'></span>
<span id='form_message' class='form_message'></span>
<input type='hidden' id='user_verb' name='user_verb' value='login'>
<input type='hidden' id='token' name='token' value='<?php echo $_SESSION['token']; ?>'>
</div>
</form>

Even stranger, the data from the form appears in a query string apereas in the URL. The PHP login function ran, but the JavaScript that sent the code to PHP didn’t. Can someone help me with this?
query string

Supabase : Migrate storage objects

I tried to run this script from supabase to migrate storage objects.
The goal is to copy storage objects from one project to another project.

https://supabase.com/docs/guides/platform/migrating-within-supabase/backup-restore#migrate-storage-objects

// npm install @supabase/supabase-js@1
const { createClient } = require('@supabase/supabase-js')
const OLD_PROJECT_URL = 'https://xxx.supabase.co'
const OLD_PROJECT_SERVICE_KEY = 'old-project-service-key-xxx'
const NEW_PROJECT_URL = 'https://yyy.supabase.co'
const NEW_PROJECT_SERVICE_KEY = 'new-project-service-key-yyy'
;(async () => {
  const oldSupabaseRestClient = createClient(OLD_PROJECT_URL, OLD_PROJECT_SERVICE_KEY, {
    db: {
      schema: 'storage',
    },
  })
  const oldSupabaseClient = createClient(OLD_PROJECT_URL, OLD_PROJECT_SERVICE_KEY)
  const newSupabaseClient = createClient(NEW_PROJECT_URL, NEW_PROJECT_SERVICE_KEY)
  // make sure you update max_rows in postgrest settings if you have a lot of objects
  // or paginate here
  const { data: oldObjects, error } = await oldSupabaseRestClient.from('objects').select()
  if (error) {
    console.log('error getting objects from old bucket')
    throw error
  }
  for (const objectData of oldObjects) {
    console.log(`moving ${objectData.id}`)
    try {
      const { data, error: downloadObjectError } = await oldSupabaseClient.storage
        .from(objectData.bucket_id)
        .download(objectData.name)
      if (downloadObjectError) {
        throw downloadObjectError
      }
      const { _, error: uploadObjectError } = await newSupabaseClient.storage
        .from(objectData.bucket_id)
        .upload(objectData.name, data, {
          upsert: true,
          contentType: objectData.metadata.mimetype,
          cacheControl: objectData.metadata.cacheControl,
        })
      if (uploadObjectError) {
        throw uploadObjectError
      }
    } catch (err) {
      console.log('error moving ', objectData)
      console.log(err)
    }
  }
})()

But I get this error:

error getting objects from old bucket
node:internal/process/promises:389
      new UnhandledPromiseRejection(reason);
      ^

UnhandledPromiseRejection: This error originated either by throwing inside of an async function without a catch block, or by rejecting a promise which was not handled with .catch(). The promise rejected with the reason "#<Object>".
    at throwUnhandledRejectionsMode (node:internal/process/promises:389:7)
    at processPromiseRejections (node:internal/process/promises:470:17)
    at process.processTicksAndRejections (node:internal/process/task_queues:96:32) {
  code: 'ERR_UNHANDLED_REJECTION'
}

JavaScript – synchronous execution of code

I tried to demonstrate to someone how JavaScript executes code synchronously, and it didn’t work the way I expected. So I’m trying to figure out why my little snippet didn’t do what I expected it to do.

The example I put together was this:

console.log('First line of code');
for (let i = 0; i < 10000000000; i++) {
    let j = i + i;
}
console.log('Last line of code');

I figured the first console log would execute, then the for loop would execute, creating a pause (can’t use setTimeout for pause since it is asynchronous), and then the second console log would execute. I put the pause in because any simple code I put together would appear to execute simultaneously.

What actually happened when I executed it was that the pause occurred first, then both console logs appeared simultaneously.

Just to make sure I wasn’t going crazy, I did the two console logs again, but with a for loop of 0 through 10 that printed a console log for each iteration. That went as expected – first console log, then the console logs for the for loop, then the second console log.

For my first example, why the heck didn’t the first console log print out before the for loop executed? Why did it not print out until after the for loop appeared finished?

Emotion Css, new is getting outputted in the almost every time when i click on component’s style

Emotion Css, new <style> is getting outputted in the <head> almost every time when I click on component. For example when I click on a button I have a ripple effect, where the ripple starts from where the user clicks. If I change the location where the user is clicking every time a new style tag gets added. Is there any optimisation I can do to avoid this?

<style data-emotion="css" data-s="">
  .css-ed674j-ripple_effect {
    -webkit-transform: scale(0);
    -moz-transform: scale(0);
    -ms-transform: scale(0);
    transform: scale(0);
    border-radius: 100%;
    position: absolute;
    -webkit-animation-name: animation-h7qd33;
    animation-name: animation-h7qd33;
    -webkit-animation-duration: 850ms;
    animation-duration: 850ms;
    opacity: 0.75;
    background-color: var(--color-accent-100);
    top: -59.296875px;
    left: 36px;
    width: 169.59375px;
    height: 169.59375px;
  }
</style>

<style data-emotion="css" data-s="">
  @-webkit-keyframes animation-h7qd33 {
    to {
      opacity: 0;
      -webkit-transform: scale(2);
      -moz-transform: scale(2);
      -ms-transform: scale(2);
      transform: scale(2);
    }
  }
</style>

<style data-emotion="css" data-s="">
  @keyframes animation-h7qd33 {
    to {
      opacity: 0;
      -webkit-transform: scale(2);
      -moz-transform: scale(2);
      -ms-transform: scale(2);
      transform: scale(2);
    }
  }
</style>

enter image description here

Override keepalive timeout AWS ingress controller

I am running a Aws EKS cluster with Load balancer and Ingress Controller. When I call an API in NodeJs service which I am running as pod in EKS cluster its returning 499 error. When The API call takes below 60 seconds its returning data but when its above 60 seconds its errors out with status code 499.

I have override service, ingress keepalive-timeout, loadbalancer timeout to 600 seconds

LoadBalancer Data

{
    "LoadBalancerAttributes": {
        "CrossZoneLoadBalancing": {
            "Enabled": false
        },
        "AccessLog": {
            "Enabled": false
        },
        "ConnectionDraining": {
            "Enabled": true,
            "Timeout": 600
        },
        "ConnectionSettings": {
            "IdleTimeout": 600
        },
        "AdditionalAttributes": [
            {
                "Key": "elb.http.desyncmitigationmode",
                "Value": "defensive"
            }
        ]
    }
}

Ingress Controller Data

worker_shutdown_timeout 240s ;
        keepalive_timeout  75s;
        client_header_timeout           600s;
        client_body_timeout             600s;
        ssl_session_timeout 10m;
                keepalive_timeout  60s;
                        proxy_connect_timeout                   600s;
                        proxy_send_timeout                      600s;
                        proxy_read_timeout                      600s;
                        proxy_next_upstream                     error timeout;
                        proxy_next_upstream_timeout             0;
                        proxy_connect_timeout                   600s;
                        proxy_send_timeout                      600s;
                        proxy_read_timeout                      600s;
                        proxy_next_upstream                     error timeout;
                        proxy_next_upstream_timeout             0;
                        proxy_connect_timeout                   600s;
                        proxy_send_timeout                      600s;
                        proxy_read_timeout                      600s;
                        proxy_next_upstream                     error timeout;
                        proxy_next_upstream_timeout             600;
                        proxy_connect_timeout                   600s;
                        proxy_send_timeout                      600s;
                        proxy_read_timeout                      600s;
                        proxy_next_upstream                     error timeout;
                        proxy_next_upstream_timeout             0;
                        proxy_connect_timeout                   600s;
                        proxy_send_timeout                      600s;
                        proxy_read_timeout                      600s;
                        proxy_next_upstream                     error timeout;
                        proxy_next_upstream_timeout             0;
                        proxy_connect_timeout                   600s;
                        proxy_send_timeout                      600s;
                        proxy_read_timeout                      600s;
                        proxy_next_upstream                     error timeout;
                        proxy_next_upstream_timeout             0;
                        proxy_connect_timeout                   600s;
                        proxy_send_timeout                      600s;
                        proxy_read_timeout                      600s;
                        proxy_next_upstream                     error timeout;
                        proxy_next_upstream_timeout             0;
                        proxy_connect_timeout                   600s;
                        proxy_send_timeout                      600s;
                        proxy_read_timeout                      600s;
                        proxy_next_upstream                     error timeout;
                        proxy_next_upstream_timeout             600;
                        proxy_connect_timeout                   600s;
                        proxy_send_timeout                      600s;
                        proxy_read_timeout                      600s;
                        proxy_next_upstream                     error timeout;
                        proxy_next_upstream_timeout             0;
                        proxy_connect_timeout                   600s;
                        proxy_send_timeout                      600s;
                        proxy_read_timeout                      600s;
                        proxy_next_upstream                     error timeout;
                        proxy_next_upstream_timeout             0;
                        proxy_connect_timeout                   600s;
                        proxy_send_timeout                      600s;
                        proxy_read_timeout                      600s;
                        proxy_next_upstream                     error timeout;
                        proxy_next_upstream_timeout             0;
                        proxy_connect_timeout                   600s;
                        proxy_send_timeout                      600s;
                        proxy_read_timeout                      600s;
                        proxy_next_upstream                     error timeout;
                        proxy_next_upstream_timeout             0;
                keepalive_timeout 0;

I noted that ingress have 3 keepalive_timeout but only top I can override.

what is the solution to extend the API call to wait beyond 1 minute(60 seconds)?

Drag and Drop Elements Independently in Vanilla JS

I’m new to Javascript, but getting close to finishing the vanilla JS for a mock desktop UI project: opening asides as “windows” that can be reordered, selected, and moved independently anywhere on the page. (I’m building a blog that mimics a desktop.)

I’m having trouble on that last part: getting my drag & drop functions to target one aside at a time. I have three asides that I want to drag, drop, then fix to the page while other asides are moved. The first drag & drop often works, but subsequent moves will capture and move all of the other open “windows” together.

Here is my project Fiddle: https://jsfiddle.net/kze09617/#&togetherjs=dd5Ez1bMHO
To open windows, click the toggle buttons.

I think the issue might be in how I’ve ordered or layered my functions; I’m brand new to JS, but am trying to learn the ropes by reinventing wheels.

I have set a function to add the class “.active” (which adds position: absolute and transform: translateZ(10) to each aside onclick) and remove it from the others:

function bringForward(elem) {
    var windows = document.querySelectorAll("aside");
    for (i = 0; i < windows.length; i++) {
        windows[i].classList.remove('active');
        windows[i].style.draggable === "false"
        windows[i].style.position === "fixed";
    }
elem.classList.add('active');
elem.style.draggable === "true";
elem.style.position === "absolute";
}

And set event listeners, so if a window is clicked again, it will allow drag & drop. Attempting to prevent my collective moving issue, I separated each drag & drop function by window, but it didn’t work. This makes me think the issue could be in the drag & drop functions themselves:

 windowOne.addEventListener('click', function() {
     
    if (windowOne.classList.contains('active')) {
    function dragStart (e) {
        var style = window.getComputedStyle(e.target, null);
        e.dataTransfer.setData("text/plain", 
            (parseInt(style.getPropertyValue("left"), 10) - e.clientX) + ',' + (parseInt(style.getPropertyValue("top"), 10) - e.clientY));
    
    }
    
    function dragOver (e) {
        e.preventDefault();
        return false;
    }
    
    function dropDown (e) {
        var offset = e.dataTransfer.getData("text/plain").split(',');
        var dm = document.getElementById('window1');
        dm.style.left = (e.clientX + parseInt(offset[0],10)) + 'px';
        dm.style.top = (e.clientY + parseInt(offset[1],10)) + 'px';
        e.preventDefault();
        return false;
    }
    
    var dm = document.getElementById('window1');
    dm.addEventListener('dragstart', dragStart, false);
    document.body.addEventListener('dragover', dragOver, false);
    document.body.addEventListener('drop', dropDown, false);
    }
    else {
        windowOne.classList.add('active'); 
        }
    
    })


windowTwo.addEventListener('click', function() {
 
    if (windowTwo.classList.contains('active')) {
function dragStart2 (e) {
    var style = window.getComputedStyle(e.target, null);
    e.dataTransfer.setData("text/plain", 
        (parseInt(style.getPropertyValue("left"), 10) - e.clientX) + ',' + (parseInt(style.getPropertyValue("top"), 10) - e.clientY));

}

function dragOver2 (e) {
    e.preventDefault();
    return false;
}

function dropDown2 (e) {
    var offset2 = e.dataTransfer.getData("text/plain").split(',');
    var dm2 = document.getElementById('window2');
    dm2.style.left = (e.clientX + parseInt(offset2[0],10)) + 'px';
    dm2.style.top = (e.clientY + parseInt(offset2[1],10)) + 'px';
    e.preventDefault();
    return false;
}

var dm2 = document.getElementById('window2');
dm2.addEventListener('dragstart', dragStart2, false);
document.body.addEventListener('dragover', dragOver2, false);
document.body.addEventListener('drop', dropDown2, false);
}
else {
    windowTwo.classList.add('active'); 
    }

})


windowThree.addEventListener('click', function() {
 
    if (windowThree.classList.contains('active')) {
function dragStart3 (e) {
    var style = window.getComputedStyle(e.target, null);
    e.dataTransfer.setData("text/plain", 
        (parseInt(style.getPropertyValue("left"), 10) - e.clientX) + ',' + (parseInt(style.getPropertyValue("top"), 10) - e.clientY));

}

function dragOver3 (e) {
    e.preventDefault();
    return false;
}

function dropDown3 (e) {
    var offset3 = e.dataTransfer.getData("text/plain").split(',');
    var dm3 = document.getElementById('window3');
    dm3.style.left = (e.clientX + parseInt(offset3[0],10)) + 'px';
    dm3.style.top = (e.clientY + parseInt(offset3[1],10)) + 'px';
    e.preventDefault();
    return false;
}

var dm3 = document.getElementById('window3');
dm3.addEventListener('dragstart', dragStart3, false);
document.body.addEventListener('dragover', dragOver3, false);
document.body.addEventListener('drop', dropDown3, false);

}
else {
    windowThree.classList.add('active'); 
    }

})

Any advice is appreciated – I really hope I can make this work.

How to centrize the one element only in the div?

<div style="justify-content:center;border:1px solid;width:80px;display:flex;align-items: baseline">
<div style="font-size:2rem">12</div>
<div style="font-size:1rem">pt</div>
<div>

it shows the centered 12pt,but what I want to do is centerize 12 not including pt.

I can use layout:fix and set the pt to the slightly left.

However 12 could be changed like 123 1234 so pt position can not be fixed.

How can I centerize only the number?

PostgreSQL handle positional parameters

I’m using MySQL for years with Nodejs and I’m interested about switching to PostgreSQL. However something really scares me : positional parameters.

I remember when I switched from positional to named parameters in MySQL, this has been a real relief.

How do people handle positional parameter in PostgreSQL ? What when an errors tells you “Parameter $36 is incorrect”? Or when number of parameters doesn’t match row count? I remember really having hard times troubleshooting this kind of errors back in the day and Y don’t want to revive this…
We also usually push array of objects or directly object to backend. Having to convert them to array every time with Object.values() feels weird

Any advise or is there something I’m missing? I can’t believe PostgreSQL is so popular and people keep struggling with this.

Leaflet map: Custom popup layer always appears under Leaflet buttons, despite z-index attempts

I’m working on a web app using Leaflet and built a custom destination tile/popup outside of Leaflet’s rendering system, to keep the layout more flexible. The tile appears correctly, but it always gets overlaid by Leaflet’s default controls (e.g., zoom buttons, filters, etc.).

What I’ve tried

  • z-index on almost every element
  • Adjusting position (absolute, relative, fixed, etc.)
  • Nesting the custom element differently
  • Adding/removing pointer-events
  • Changing render order in HTML
  • Renaming/restructuring CSS classes

I’ve also consulted CSS pros with 10+ years of experience and multiple LLMs (Claude 3, Gemini 2.5, Grok, OpenAI o1/o3), but no working solution so far.

Feedback from CSS expert

“To make this work, the custom popup would need to be placed inside the leaflet-pane leaflet-map-pane container — but that isn’t possible in our setup.”

My current theory

Since the popup isn’t part of Leaflet’s internal layer structure (i.e., not added via L.popup or similar), it’s being rendered in a separate DOM context. As a result, even with higher z-index values, the Leaflet controls still appear on top.

Expected behavior

  • The custom popup (destination tile) should appear above all Leaflet controls.

Actual behavior

  • The popup always renders below Leaflet buttons/controls, no matter what z-index I apply.

Reproducible Example

Question

Is there a clean way to ensure an external popup (not managed by Leaflet itself) can appear above Leaflet controls, without moving it into Leaflet’s pane system?

Any CSS trick, layering hack, or workaround appreciated. JS-based solutions are also fine if necessary.

Nextjs 15 optional catch all segment

enter image description here

okay so i have a file named page.tsx in here, im using nextjs 15 latest, im following a tutorial which is also for nextjs 15, and mine has been working similarly as his, but unlike him, when i wrap the […slug] inside a square bracket([]), all of his route, including the base http://localhost:3000/docs became available, while any routes that i have started with docs result in error 404?

Cant use react-photo-view to display photo

I am not able to display my photos using the react-photo-view library.

My code is as follow and its not working. If i remove the photo viewer and PhotoProvider, my image will be displayed. Is the library not compatible with NextJS?:

import parse from 'html-react-parser';
import Image from 'next/image';
import { PhotoProvider, PhotoView } from 'react-photo-view';

import 'react-photo-view/dist/react-photo-view.css';

import { ImageProps } from './types';

export default async function ImageProps(props: ImageProps) {
  const { title, content, image, category } = props;

  return (
    <>
      <div className="summary text-[14px] my-[10px]">
        <PhotoProvider>
          <PhotoView src={image.id}>
            <Image
              src={image.id}
              className="w-full h-auto object-contain mb-4"
              alt="Slide Image"
            />
          </PhotoView>
        </PhotoProvider>
      </div>
    </>
  );
}

SharePoint 2019 – List View Command Bar with BaseDialog and DropDown List

Hi experts: When I customize a List View Extension – Command Bar on SharePoint 2019 On-Premises and click on my custom Command Bar, my BaseDialog – Panel appears. I have successfully retrieved the options from a Choice field in the list and displayed them in the DropDown List. Unfortunately, when I select a text, I am unable to retrieve the selected text.

I have also attached a link to an image related to the issue. As follows:

enter image description here

Here is my entire code. I have spent a long time but still cannot find the issue. I wonder if anyone with experience can provide some directions or a solution to help me resolve this problem.

I really appreciate everyone’s help. Thanks again!

A. Explanation:

(1) At the start of the program execution, the onOpen() function is called to retrieve the options from the backend Choice field in the specific list and then insert the extracted options into the DropDown List.


componentDidMount() {
    console.log('componentDidMount has been called');
    if (this.props.onOpen) {
      this.props.onOpen();
    }
  }

public onOpen(): void {
    console.log('onOpen() ==> I am here.');
    this.loadChoiceFieldOptions(); // Load machine options
  }

private async loadChoiceFieldOptions(): Promise<void> {
    console.log('loadChoiceFieldOptions() ==> I am here.');
    try {
      const listId = '5dadf73c-117d-44ea-a755-23cb4786ea95'; // 換成你的清單 GUID
      const fieldInternalName = '_x6a5f__x53f0__x7de8__x865f_'; // <-- 換成你的欄位 Internal Name
  
      const field = await sp.web.lists.getById(listId)
        .fields.getByInternalNameOrTitle(fieldInternalName)
        .get();
      const choices: string[] = (field as any).Choices; // ← 這樣就不報錯了
  
      this.machineOptions = choices.map(choice => ({
        key: choice,
        text: choice
      }));
  
      this.render(); // Re-render to update the machine options
    } catch (error) {
      console.error('載入機台選項錯誤:', error);
      this.machineOptions = [];
      this.render(); // Re-render to update the message
    }
  }


(2) In the render function, I generate a DropDown List.


<Dropdown
          label="機台編號"
          options={[defaultOption, ...this.filterMachineOptions()]}
          onSelect={(
            event: React.FormEvent<HTMLDivElement>, 
            option?: IDropdownOption
          ) => {
            console.log('Dropdown onChange fired!', option);
          }}
          selectedKey={this.state.machineId || 'placeholder'}
        />


(3) However, in the RdasSearchPanel class, there is another render process (I am not sure if this is incorrect)


export default class RdasSearchPanel extends BaseDialog {
  private machineOptions: IDropdownOption[] = [];

  public render(): void {
    ReactDOM.render(
      <RdasSearchPanelContent 
        onDismiss={() => this.close()} 
        onOpen={() => this.onOpen()} 
        machineOptions={this.machineOptions}
      />, 
      this.domElement
    );
  }


B. Here is my entire code.

import * as React from 'react';
import * as ReactDOM from 'react-dom';
import { BaseDialog } from '@microsoft/sp-dialog';
import { Panel, PanelType, TextField, PrimaryButton, MessageBar, MessageBarType, Dropdown, IDropdownOption } from 'office-ui-fabric-react';
import { sp } from '@pnp/sp/presets/all';

export interface IRdasSearchPanelState {
  projectCode: string;
  lotNumber: string;
  machineId: string;
  items: any[];
  message: string;
  machineOptions: IDropdownOption[];
  searchQuery: string;
}

class RdasSearchPanelContent extends React.Component<any, IRdasSearchPanelState> {
  constructor(props: any) {
    super(props);
    this.state = {
      projectCode: '',
      lotNumber: '',
      machineId: '',
      items: [],
      message: '',
      machineOptions: props.machineOptions || [],
      searchQuery: ''
    };
  }

  componentDidMount() {
    console.log('componentDidMount has been called');
    if (this.props.onOpen) {
      this.props.onOpen();
    }
  }

  componentDidUpdate(prevProps: any) {
    if (prevProps.machineOptions !== this.props.machineOptions) {
      this.setState({ machineOptions: this.props.machineOptions });
    }
  }

  handleInputChange = (event: React.FormEvent<HTMLInputElement | HTMLTextAreaElement>): void => {
    const target = event.target as HTMLInputElement;
    const name = target.name;
    const value = target.value;
    this.setState({ [name]: value } as unknown as Pick<IRdasSearchPanelState, keyof IRdasSearchPanelState>);
  };

  handleDropdownChange = (event: React.FormEvent<HTMLDivElement>, option?: IDropdownOption): void => {
    console.log('handleDropdownChange ==> I am here.');
    if (option && option.key !== 'placeholder') {
      this.setState({ machineId: option.key as string });
    }
  };

  filterMachineOptions = (): IDropdownOption[] => {
    return this.state.machineOptions.filter(option => option.text.toLowerCase().includes(this.state.searchQuery.toLowerCase()));
  };

  searchListItems = async (): Promise<void> => {
    try {
      const { projectCode, lotNumber, machineId } = this.state;
      const baseUrl = window.location.origin;
      const url = `${baseUrl}/Lists/List4/AllItems.aspx?useFiltersInViewXml=1&FilterField1=LinkTitle&FilterValue1=${projectCode}&FilterField2=_x6279__x865f_&FilterValue2=${lotNumber}&FilterField3=_x6a5f__x53f0__x7de8__x865f_&FilterValue3=${machineId}`;
      window.location.href = url;
    } catch (error) {
      console.error('Error searching list items: ', error);
      this.setState({
        message: 'Error searching list items'
      });
    }
  };

  render() {
    const defaultOption: IDropdownOption = { key: 'placeholder', text: '請選擇機台' };

    return (
      <Panel
        isLightDismiss={false}
        isOpen={true}
        type={PanelType.medium}
        onDismissed={this.props.onDismiss}
      >
        <h2>Hello there from my custom Panel</h2>

        <TextField 
          label="提案序號" 
          name="projectCode"
          onKeyUp={this.handleInputChange} 
        />

        <TextField 
          label="批號" 
          name="lotNumber"
          onKeyUp={this.handleInputChange} 
        />

        <TextField 
          label="Search Machine"
          onKeyUp={(event) => this.setState({ searchQuery: (event.target as HTMLInputElement).value })}
        />

        <Dropdown
          label="機台編號"
          options={[defaultOption, ...this.filterMachineOptions()]}
          onSelect={(
            event: React.FormEvent<HTMLDivElement>, 
            option?: IDropdownOption
          ) => {
            console.log('Dropdown onChange fired!', option);
          }}
          selectedKey={this.state.machineId || 'placeholder'}
        />

        <PrimaryButton 
          text='Search' 
          onClick={this.searchListItems} 
        />
        {this.state.message && 
          <MessageBar 
            messageBarType={MessageBarType.info}
            isMultiline={false}
          >
            {this.state.message}
          </MessageBar>
        }
        {this.state.items.length > 0 && 
          <ul>
            {this.state.items.map(item => (
              <li key={item.Id}>{item.Title}</li>
            ))}
          </ul>
        }

        <div>
          <p>Project Code: {this.state.projectCode}</p>
          <p>Lot Number: {this.state.lotNumber}</p>
          <p>Machine ID: {this.state.machineId}</p>
        </div>
      </Panel>
    );
  }
}

export default class RdasSearchPanel extends BaseDialog {
  private machineOptions: IDropdownOption[] = [];

  public render(): void {
    ReactDOM.render(
      <RdasSearchPanelContent 
        onDismiss={() => this.close()} 
        onOpen={() => this.onOpen()} 
        machineOptions={this.machineOptions}
      />, 
      this.domElement
    );
  }

  public onOpen(): void {
    console.log('onOpen() ==> I am here.');
    this.loadChoiceFieldOptions(); // Load machine options
  }

  private async loadChoiceFieldOptions(): Promise<void> {
    console.log('loadChoiceFieldOptions() ==> I am here.');
    try {
      const listId = '5dadf73c-117d-44ea-a755-23cb4786ea95'; // 換成你的清單 GUID
      const fieldInternalName = '_x6a5f__x53f0__x7de8__x865f_'; // <-- 換成你的欄位 Internal Name
  
      const field = await sp.web.lists.getById(listId)
        .fields.getByInternalNameOrTitle(fieldInternalName)
        .get();
      const choices: string[] = (field as any).Choices; // ← 這樣就不報錯了
  
      this.machineOptions = choices.map(choice => ({
        key: choice,
        text: choice
      }));
  
      this.render(); // Re-render to update the machine options
    } catch (error) {
      console.error('載入機台選項錯誤:', error);
      this.machineOptions = [];
      this.render(); // Re-render to update the message
    }
  }
}

How to stop a hover in moving the image options? [closed]

I have a project and I have a trouble to fix this, my problem here is that my image options is moving up and down whenever I point my cursor to my image options it affects my descriptions. What should I do to fix this? I’m just starting out, so I appreciate your understanding.

I just want to fix image option to move up and down whenever I point the other images and to not affect the descriptions.
Here is the output

Here is my code.

:root {
  --text-dark: #000000;
  --text-light: #727274;
  --extra-light: #f3f4f6;
  --max-width: 1200px;
  --header-font: "Montserrat", sans-serif;
  --text-font: "Poppins", sans-serif;
}

body {
  margin: 0;
  padding: 0;
  background-image: url('backg5.png');
  background-size: cover;
  background-position: center 10px;
  background-attachment: fixed;
  color: white;
  overflow-x: hidden;
  padding-top: 80px; 
}

header {
  display: flex;
  position: fixed;
  justify-content: center;
  align-items: center;
  top: 0;
  width: 100%;
  z-index: 1000;
  transition: top 0.3s ease-in-out;
  color: white;
  background: hsl(0, 100%, 100%);
  box-shadow: -4px 0 6px rgba(0, 0, 0, 0.1), 4px 0 6px rgba(0, 0, 0, 0.1);
  padding: 10px 15px;
}

.logo {
  display: flex;
  justify-content: center;
  align-items: center;
  flex: 1;
}

.logo img {
  max-width: 120px;
  margin-top: 5px;
}

.product-container {
  position: absolute;
  display: inline-block;
  align-items: center;
  justify-content: flex-start;
  transform: translateY(-50%);
  left: 40px;
  top: 50%;
}

.product-icon {
  font-size: 30px;
  color: green;
}

.dropdown {
  position: relative;
  display: inline-block;
  margin-left: auto;
}

.dropdown-content {
  display: none;
  position: absolute;
  margin-left: auto;
  background-color: #f9f9f9;
  width: 1260px;
  top: 71%;
  padding: 0;
  left: 0;
  z-index: 1000;
  transition: opacity 0.3s ease, visibility 0.3s ease;
  border-radius: 8px;
  max-height: 160px;
  overflow-y: auto;
}

.dropdown:hover .dropdown-content {
  display: block;
  padding-top: 3px;
  background-color: #f9f9f9;
  box-shadow: 0px 4px 10px rgba(0, 0, 0, 0.15);
  opacity: 1;
  visibility: visible;
}

.dropdown-content:hover {
  display: block;
  opacity: 1;
  visibility: visible;
}

.dropdown:hover .dropdown-content {
  background-color: whitesmoke;
}

.container {
  display: flex;
  overflow-x: auto;
  margin: 10px;
  scroll-padding: 0px;
  gap: 10px;
  max-height: 100vh;
  overflow-y: hidden;
}

.container::-webkit-scrollbar {
  display: block;
}

.container::-webkit-scrollbar-track {
  background-color: #AAB99A;
  border-radius: 10px;
}

#Container {
  display: flex;
  scroll-snap-type: x mandatory;
}

.item {
  flex: 0 0 auto;
}

.category {
  width: 100%;
  max-width: 300px;
  height: auto;
  border-radius: 5px;
}

.product-category {
  width: 100px;
  height: 100px;
  object-fit: contain;
  gap: 2px;
  border-radius: 10px;
  transition: transform 0.3s ease, box-shadow 0.3s ease;
  margin-bottom: 10px;
}

.product-category:hover {
  transform: scale(1.1);
  padding: 5px;
  z-index: 10;
}

.page-body {
  font-family: var(--header-font);
  margin: 0;
  display: flex;
  justify-content: center;
  align-items: center;
  min-height: 90vh;
  padding: 5px;
}

.page-container {
  background-color: #fff;
  display: flex;
  flex-direction: column;
  border-radius: 10px;
  box-shadow: 0 5px 15px rgba(0,0,0,0.1);
  padding: 30px;
  max-width: 1000px;
  width: 100%;
  margin-bottom: 0;
  padding-bottom: 0;
  gap: 10px;
}

@media (min-width: 768px) {
  .page-container {
    flex-direction: row;
  }
}

.image-gallery {
  flex: 0.9;
  text-align: left;
}

.image-gallery img {
  max-width: 100%;
  height: auto;
}

.image-wrapper {
  max-width: 450px;
  width: 100%;
  display: flex;
  flex-direction: column;
  align-items: center;
}

.thumbnail-wrapper {
  display: flex;
  align-items: center;
  justify-content: center;
  position: relative;
  overflow: hidden;
  padding: 5px 0;
  width: 100%;
}

.thumbnail-row {
  display: flex;
  gap: 4px; 
  overflow-x: auto;
  height: 80px;
  scroll-behavior: smooth;
  pointer-events: auto;
  scrollbar-width: none;
  scroll-snap-type: x mandatory;
  padding-top: 8px;
  padding-left: 0;
  padding-right: 0;
  align-items: center;
}

.thumbnail-row::-webkit-scrollbar {
  display: none;
}

.thumbnail-row img {
  width: 75px;
  height: 75px;
  border: 2px solid transparent;
  transition: border 0.2s ease, transform 0.2s ease;
  cursor: pointer;
  pointer-events: auto;
  scroll-snap-align: start;
  box-sizing: border-box;
  object-fit: cover;
  flex-shrink: 0;
  margin-left: 0;
  margin-right: 0;
  position: relative;
  z-index: 1;
  display: block;
}

.thumbnail-row img:hover {
  border-color: #5F8B4C;
  z-index: 1;
}

/* Selected state */
.thumbnail-row img.selected {
  border-color: #5F8B4C;
}

.arrow {
  position: absolute;
  top: 50%;
  transform: translateY(-50%);
  border: none;
  font-size: 30px;
  padding: 8px;
  cursor: pointer;
  z-index: 10;
  color: white;
  background-color: rgba(87, 83, 83, 0.428);
 
}

.arrow img {
  width: 25px;
  height: 25px;
  pointer-events: none;
}

.arrow.left {
  position: absolute;
  left: -0px;
}

.arrow.right {
  position: absolute;
  right: -0px;
}

.product-details {
  flex: 1;
  padding-top: 10px;
  display: flex;
  flex-direction: column;
  justify-content: flex-start;
  align-items: flex-start;
  text-align: left;
  gap: 10px;
  margin-top: 0;
  padding-left: 0;
}

@media (min-width: 768px) {
  .product-details {
    padding-left: 0px;
    padding-top: 0;
    flex-direction: column;
  }
}

.product-details h2 {
  color: var(--text-dark);
  font-family: var(--header-font);
  text-align: left;
}

.price {
  color: #5F8B4C;
  font-weight: bold;
  font-family: var(--header-font);
  font-size: large;
  text-align: left;
}

.description {
  margin: 15px 0;
  color: #666;
  font-family: var(--text-font);
  font-size: 16px;
  text-align: left;
}

.color-name {
  color: var(--text-dark);
  font-weight: bold;
  text-align: left;
}

.colors {
  margin: 10px 0;
  flex-wrap: wrap;
  gap: 10px;
  overflow: hidden;
  text-align: left;
}

.color-option {
  display: inline-block;
  width: 30px;
  height: 30px;
  border-radius: 50%;

  margin-right: 10px;
  cursor: pointer;
  transition: transform 0.3s ease;
  transform-origin: center center;
  border: 2px solid #EFF3EA;
  flex-shrink: 0; 
}

.color-option:hover {
  outline: 2px solid #EFF3EA;
  outline-offset: 0px;
  transform: scale(1.2);
}


<!DOCTYPE html>
<html lang="en">

<head>
  <meta charset="UTF-8" />
  <meta name="viewport" content="width=device-width, initial-scale=1.0" />
  <title>GOOJODOQ</title>

 
  <link rel="stylesheet" href="productin2.css" />

  <!-- External Scripts -->
  <script src="https://unpkg.com/scrollreveal"></script>
  <script src="main.js" defer></script>

 
  <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin />
  <link href="https://fonts.googleapis.com/css2?family=Antonio:wght@700&family=Russo+One&display=swap" rel="stylesheet" />
  <link href="https://fonts.googleapis.com/css2?family=Teko:wght@400;700&family=Oswald:wght@400;700&family=Rajdhani:wght@400;700&display=swap" rel="stylesheet" />
  <link href="https://fonts.googleapis.com/css2?family=Poppins:wght@400;700&family=Montserrat:wght@400;700&family=Raleway:wght@400;700&display=swap" rel="stylesheet" />

  <!-- Font Awesome -->
  <link href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.2/css/all.min.css" rel="stylesheet" />
</head>

<body>


  <header>
    <div class="logo">
      <img src="goojodoq-logo.png" alt="Goojodoq Logo" />
    </div>
    
    <div class="product-container">
      <!-- Product Dropdown -->
      <div class="dropdown">
        <a href="#" id="product-link">
          <i class="fas fa-shopping-cart product-icon"></i>
        </a>
        <div class="dropdown-content">
          <div class="container" id="Container">
            <!-- Product Items -->
            <div class="item"><img src="fan.png" alt="Fan" class="product-category" /></div>
            <div class="item"><img src="mouse.png" alt="Mouse" class="product-category" /></div>
            <div class="item"><img src="stylus_pen.png" alt="Stylus Pen" class="product-category" /></div>
            <div class="item"><img src="USB.png" alt="USB" class="product-category" /></div>
            <div class="item"><img src="keyboard.png" alt="Keyboard" class="product-category" /></div>
            <div class="item"><img src="speaker.png" alt="Speaker" class="product-category" /></div>
            <div class="item"><img src="fan.png" alt="Fan" class="product-category" /></div>
            <div class="item"><img src="mouse.png" alt="Mouse" class="product-category" /></div>
            <div class="item"><img src="stylus_pen.png" alt="Stylus Pen" class="product-category" /></div>
            <div class="item"><img src="USB.png" alt="USB" class="product-category" /></div>
            <div class="item"><img src="keyboard.png" alt="Keyboard" class="product-category" /></div>
            <div class="item"><img src="speaker.png" alt="Speaker" class="product-category" /></div>
          </div>
        </div>
      </div>
    </div>
  </header>

  
  <main>
    <div class="page-body">
      <div class="page-container">
        <!-- Image Gallery -->
        <div class="image-gallery">
          <div class="image-wrapper">
            <img id="mainImage" src="/pictures/1.jpg" alt="Headphones" />
            <div class="thumbnail-wrapper">
              <button class="arrow left" onclick="scrollThumbnails(-1)">&#8249;</button>
              <div class="thumbnail-row" id="thumbnailRow">
                <img src="/pictures/1.jpg" onclick="changeImage(this)" />
                <img src="/pictures/2.jpg" onclick="changeImage(this)" />
                <img src="/pictures/3.jpg" onclick="changeImage(this)" />
                <img src="/pictures/4.jpg" onclick="changeImage(this)" />
                <img src="/pictures/5.jpg" onclick="changeImage(this)" />
                <img src="/pictures/7.jpg" onclick="changeImage(this)" />
                
              </div>
              <button class="arrow right" onclick="scrollThumbnails(1)">&#8250;</button>
            </div>
          </div>
        </div>

        <!-- Product Details Section -->
        <div class="product-details">
          <h2>Beats Solo3 Wireless</h2>
          <div class="price">$999.99</div>
          <div class="description">
            Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.
          </div>
          <div class="colors">
            <div class="color-name">Available Colors</div><br>
            <div class="color-option" style="background-color: white;"></div>
            <div class="color-option" style="background-color: pink;"></div>
            <div class="color-option" style="background-color: purple;"></div>
          </div>
        </div>

      </div>
    </div>
  </main>

</body>
</html>
document.addEventListener("DOMContentLoaded", function () {
    let lastScrollTop = 0;
    const header = document.querySelector("header");
  
    window.addEventListener("scroll", function () {
      const currentScroll = window.pageYOffset || document.documentElement.scrollTop;
      header.style.top = currentScroll > lastScrollTop ? "-100px" : "0";
      lastScrollTop = currentScroll <= 0 ? 0 : currentScroll;
    });
  
    const container = document.getElementById("Container");
    if (container) {
      container.addEventListener("wheel", function (event) {
        event.preventDefault();
        this.scrollLeft += event.deltaY;
      });
    }
  
    function changeImage(imgElement) {
        const mainImage = document.getElementById("mainImage");
        if (mainImage) {
          mainImage.src = imgElement.src;
        }
      
        document.querySelectorAll(".thumbnail-row img").forEach(img => {
          img.classList.remove("selected");
        });
      
        imgElement.classList.add("selected");
      }
  
    function changeQty(change) {
      const qtyElement = document.getElementById("quantity");
      if (qtyElement) {
        let qty = parseInt(qtyElement.textContent) || 1;
        qty = Math.max(qty + change, 1);
        qtyElement.textContent = qty;
      }
    }
  
    const scrollContainer = document.getElementById("thumbnailRow");
    const leftArrow = document.querySelector(".arrow.left");
    const rightArrow = document.querySelector(".arrow.right");
  
    function scrollThumbnails(direction) {
      const scrollAmount = 100;
      if (scrollContainer) {
        scrollContainer.scrollBy({
          left: direction * scrollAmount,
          behavior: "smooth"
        });
      }
    }
  
    if (leftArrow && rightArrow && scrollContainer) {
      leftArrow.addEventListener("click", () => scrollThumbnails(-1));
      rightArrow.addEventListener("click", () => scrollThumbnails(1));
    }
  
    const mainImage = document.getElementById("mainImage");
    const thumbnails = document.querySelectorAll(".thumbnail-row img");
  
    thumbnails.forEach(thumbnail => {
      thumbnail.addEventListener("mouseover", () => {
        // Set as main image
        if (mainImage) {
          mainImage.src = thumbnail.src;
        }
  
        thumbnails.forEach(img => img.classList.remove("selected"));
  
        // Add selected to hovered
        thumbnail.classList.add("selected");
      });
    });

    window.img = function (src) {
      const mainImage = document.getElementById("mainImage");
      if (mainImage) {
        mainImage.src = src;
      }
    };
  
    if (typeof ScrollReveal !== "undefined") {
      ScrollReveal().reveal(".page-body", {
        origin: "bottom",
        distance: "50px",
        duration: 1000,
        delay: 100,
        reset: true
      });
    } else {
      console.warn("ScrollReveal is not defined.");
    }
  
    window.changeImage = changeImage;
    window.changeQty = changeQty;
  });