GSAP scrollTrigger snaps to unexpected snap point

I must be getting GSAP’s scrollTrigger behaviour wrong, but I can’t figure out what I am missing since I believe to stick closely to the documentation.

The content of my webpage features four sections tagged by the class .sectionContainer, each with a height equal to exactly 1 viewport. Once the user finishes scrolling, I want the page to snap to the closest position that shows one full .sectionContainer. In other words, I don’t want the page to rest in between two sections.

However, when I scroll through the example below, GSAP often snaps to positions that lie in between two sections, even though I believe my snap points are calculated correctly.

F.e. totalHeight of 3578px, sectionHeight of 832px and the 50px padding at the top of .contentContainer imply a first snap point of 0.0139742873113471 by the mapping function sections.map((_, i) => offSet * (i + 1) + i * sectionCoverage).
Similarly, for the #secondSectionContainer, the snap point is 0.26048071548351, which seems conceptually correct to me, but the code’s behaviour disagrees and snaps to a point within #firstSectionContainer.

Can someone please point me to what I am missing?

gsap.registerPlugin(ScrollTrigger);

const totalHeight = document.querySelector('.contentContainer').scrollHeight;
const sectionHeight = document.querySelector('.sectionContainer').offsetHeight
const sectionCoverage = sectionHeight/totalHeight;
const contentContainerGap = 50;
const offSet = contentContainerGap/totalHeight;


let sections = gsap.utils.toArray('.sectionContainer');
let snapPoints = sections.map((_, i) => offSet * (i + 1) + i * sectionCoverage);
console.log('snapPoints: ', snapPoints);

ScrollTrigger.create({
    trigger: ".contentContainer",
    start: "top top",
    end: "bottom bottom",
    snap: {
        snapTo: snapPoints,
        duration: 0.5,
        ease: "power1.inOut"
    },
    markers: true
});
.contentContainer {
    flex-grow: 1;
    display: flex;
    flex-direction: column;
    gap: 50px;
    padding: 50px 0 50px 0;
    align-items: center;
}

.sectionContainer {
    border: 2px solid blue;
    width: 90%;
    height: 100vh;
    padding: 20px;
}

#firstSectionContainer {
  background-color: red;
}

#secondSectionContainer {
  background-color: green;
}

#thirdSectionContainer {
  background-color: yellow;
}

#fourthSectionContainer {
  background-color: purple;
}
<div id="contentContainer" class="contentContainer">
  <div id="firstSectionContainer" class="sectionContainer">
  </div>
  <div id="secondSectionContainer" class="sectionContainer">
  </div>
  <div id="thirdSectionContainer" class="sectionContainer">
  </div>
  <div id="fourthSectionContainer" class="sectionContainer">
  </div>
</div>

<script src="https://cdn.jsdelivr.net/npm/[email protected]/dist/gsap.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/[email protected]/dist/ScrollTrigger.min.js"></script>

How to Access Google Drive Files Programmatically Using drive.file Scope and Picker setFileIds()?

I am developing an application that processes resumes submitted via Google Forms, storing them on Google Drive. Our app uses ATS scoring and filtering algorithms to evaluate these resumes. I want to allow recruiters to select the relevant resume files using the Google Picker API, then have the app automatically access the file content for processing.

I am using the drive.file scope to minimize access, as it is designed to allow access only to files explicitly “opened” by the application. However, the challenge is that manually “opening” each file is impractical when processing many resumes.

I came across the new Picker API method setFileIds(fileIds), which allows pre-navigating to specific file IDs. My question is:

Is there a way to use the drive.file scope and setFileIds() to programmatically treat the selected files as “opened” so that the app can access their content without requiring the user to manually open each file?

In other words, once the file IDs are obtained using the Picker, can the app automatically access the content of those files for processing?

Any guidance or best practices on how to achieve this functionality using drive.file and the Picker API would be greatly appreciated.

Thanks!

<!DOCTYPE html>
<html>
<head>
  <title>Google Drive Picker & Open File Example</title>
  <style>
    body { font-family: Arial, sans-serif; max-width: 800px; margin: 20px auto; padding: 20px; }
    button { 
      padding: 10px 20px; 
      background: #4285f4; 
      color: white; 
      border: none; 
      border-radius: 4px; 
      cursor: pointer; 
      margin-right: 10px;
    }
    button:disabled {
      background: #ccc;
      cursor: not-allowed;
    }
    #result { 
      margin-top: 20px; 
      padding: 15px; 
      border: 1px solid #ddd; 
      white-space: pre-wrap; 
    }
    .error { color: #dc3545; }
    .file-info {
      margin: 15px 0;
      padding: 10px;
      background: #f8f9fa;
      border-radius: 4px;
    }
    .content-preview {
      margin-top: 15px;
      padding: 15px;
      background: #f8f9fa;
      border-radius: 4px;
      overflow-x: auto;
    }
    .loading {
      display: inline-block;
      padding: 10px;
      color: #666;
    }
  </style>
</head>
<body>
  <button onclick="handleFileAccess()" id="pickButton" disabled>Select File from Google Drive</button>
  <!-- These buttons will appear after file selection -->
  <button onclick="openInDrive()" id="openInDriveBtn" style="display: none;">Open in Drive</button>
  <button onclick="fetchContent()" id="fetchContentBtn" style="display: none;">Fetch Content</button>
  <div id="result"></div>

  <script>
    
    const CLIENT_ID = '';
    const API_KEY = '';
    const SCOPE = 'https://www.googleapis.com/auth/drive.file';

    let pickerInitialized = false;
    let tokenClient;
    let accessToken = null;
    let selectedFile = null;

 
    function initializeGapiClient() {
      return new Promise((resolve, reject) => {
        gapi.load('client', async () => {
          try {
            await gapi.client.init({
              apiKey: API_KEY,
              discoveryDocs: ['https://www.googleapis.com/discovery/v1/apis/drive/v3/rest'],
            });
            resolve();
          } catch (error) {
            reject(error);
          }
        });
      });
    }

   
    function initializePicker() {
      return new Promise((resolve, reject) => {
        gapi.load('picker', {
          callback: () => {
            pickerInitialized = true;
            console.log('Picker API loaded');
            resolve();
          },
          onerror: reject
        });
      });
    }

    function initializeGIS() {
      if (!google || !google.accounts) {
        showError('Google Identity Services not loaded yet');
        return;
      }
      tokenClient = google.accounts.oauth2.initTokenClient({
        client_id: CLIENT_ID,
        scope: SCOPE,
        callback: (response) => {
          if (response.error) {
            showError('Authorization failed: ' + response.error);
            return;
          }
          accessToken = response.access_token;
          showPicker();
        },
      });
    }

  
    async function handleFileAccess() {
      if (!pickerInitialized) {
        showError('Picker not initialized yet. Please try again.');
        return;
      }
      if (!accessToken) {
        tokenClient.requestAccessToken({ prompt: 'consent' });
      } else {
        try {
          await validateAccessToken();
          showPicker();
        } catch (error) {
          showError('Session expired. Please re-authenticate.');
          accessToken = null;
          tokenClient.requestAccessToken({ prompt: 'consent' });
        }
      }
    }

    function showPicker() {
      const view = new google.picker.DocsView()
          .setIncludeFolders(true)
          .setSelectFolderEnabled(false);

      const picker = new google.picker.PickerBuilder()
          .addView(view)
          .setOAuthToken(accessToken)
          .setDeveloperKey(API_KEY)
          .setCallback(pickerCallback)
          .build();
      picker.setVisible(true);
    }

   
    function pickerCallback(data) {
      const resultDiv = document.getElementById('result');
      resultDiv.innerHTML = '';
      document.getElementById('openInDriveBtn').style.display = 'none';
      document.getElementById('fetchContentBtn').style.display = 'none';

      if (data.action === google.picker.Action.PICKED && data.docs) {
        selectedFile = data.docs[0];
        resultDiv.innerHTML = `
          <div class="file-info">
              <strong>Selected File:</strong><br>
              Name: ${selectedFile.name}<br>
              Type: ${selectedFile.mimeType}<br>
              Size: ${selectedFile.sizeBytes ? `${(selectedFile.sizeBytes / 1024).toFixed(2)} KB` : 'Unknown'}<br>
          </div>
          <div>Select an action below to open the file and/or fetch its content.</div>
        `;
        // Show both buttons: one to open the file and one to fetch content
        document.getElementById('openInDriveBtn').style.display = 'inline-block';
        document.getElementById('fetchContentBtn').style.display = 'inline-block';
      } else if (data.action === google.picker.Action.CANCEL) {
        resultDiv.textContent = 'User canceled selection';
        selectedFile = null;
      }
    }


    function openInDrive() {
      if (!selectedFile) {
        showError('No file selected');
        return;
      }
      const fileUrl = `https://drive.google.com/file/d/${selectedFile.id}/view`;
      window.open(fileUrl, '_blank');
    }

    async function fetchContent() {
      if (!selectedFile) {
        showError('No file selected');
        return;
      }
      const resultDiv = document.getElementById('result');
      resultDiv.innerHTML += '<div class="loading">Fetching file content...</div>';
      try {
        const content = await fetchFileContent(selectedFile.id);
        // Display the fetched content preview (first 1000 characters)
        resultDiv.innerHTML += `
          <div class="content-preview">
            <strong>Content Preview:</strong><br>
            <pre>${escapeHtml(content.substring(0, 1000))}
            ${content.length > 1000 ? 'n... (truncated)' : ''}</pre>
          </div>
        `;
      } catch (error) {
        showError(`Failed to fetch file content: ${error.message}`);
      }
    }

  
    async function fetchFileContent(fileId) {
      try {
        const response = await fetch(`https://www.googleapis.com/drive/v3/files/${fileId}?alt=media&supportsAllDrives=true`, {
          headers: { 'Authorization': `Bearer ${accessToken}` }
        });
        if (!response.ok) {
          const error = await response.json();
          throw new Error(error.error?.message || 'Failed to fetch file');
        }
        return await response.text();
      } catch (error) {
        console.error('Error fetching file:', error);
        throw error;
      }
    }

   
    async function validateAccessToken() {
      try {
        const response = await fetch(`https://www.googleapis.com/oauth2/v3/tokeninfo?access_token=${accessToken}`);
        if (!response.ok) {
          throw new Error('Invalid access token');
        }
        const data = await response.json();
        console.log('Token info:', data);
      } catch (error) {
        console.error('Token validation error:', error);
        throw error;
      }
    }

    function escapeHtml(unsafe) {
      return unsafe
        .replace(/&/g, "&amp;")
        .replace(/</g, "&lt;")
        .replace(/>/g, "&gt;")
        .replace(/"/g, "&quot;")
        .replace(/'/g, "&#039;");
    }

   
    function showError(message) {
      const resultDiv = document.getElementById('result');
      resultDiv.innerHTML = `<div class="error">Error: ${message}</div>`;
      console.error(message);
    }

    function loadGoogleAPIs() {
      return new Promise((resolve, reject) => {
        const gsiScript = document.createElement('script');
        gsiScript.src = 'https://accounts.google.com/gsi/client';
        gsiScript.onload = () => {
          const gapiScript = document.createElement('script');
          gapiScript.src = 'https://apis.google.com/js/api.js';
          gapiScript.onload = async () => {
            try {
              await initializeGapiClient();
              await initializePicker();
              initializeGIS();
              document.getElementById('pickButton').disabled = false;
              resolve();
            } catch (error) {
              reject(error);
            }
          };
          gapiScript.onerror = reject;
          document.head.appendChild(gapiScript);
        };
        gsiScript.onerror = reject;
        document.head.appendChild(gsiScript);
      });
    }

    window.onload = function() {
      loadGoogleAPIs().catch(error => {
        showError(`Failed to initialize Google APIs: ${error.message}`);
      });
    };
  </script>
</body>
</html>

Prevent page reloading when zooming the leaflet map

Currently I have in my leaflet map loaded images in different coordinates of the map, but something unexpected happens to me, when I zoom the map, it reloads the whole page and I don’t have any event in the zoom to do it.

How do I prevent it from reloading the page when zooming?

Am I missing some option that I don’t know? I have checked the leaflet documentation but I don’t see anything for it. Maybe some of you have had this happen to you.

How to create a 3D-like spinning diamond animation using HTML5 Canvas and JavaScript?

I’m trying to create a 3D-like spinning diamond animation similar to this reference using HTML5 Canvas and JavaScript. The diamond should rotate smoothly, giving the illusion of depth and perspective.

Here’s what I’ve tried so far:

const canvas = document.getElementById('myCanvas');
const ctx = canvas.getContext('2d');

const centerX = canvas.width / 2;
const centerY = canvas.height / 2;
const size = 50; // Size of the diamond

function drawDiamond(angle) {
  ctx.clearRect(0, 0, canvas.width, canvas.height); // Clear the canvas
  ctx.save();
  ctx.translate(centerX, centerY);
  ctx.rotate((angle * Math.PI) / 180); // Convert degrees to radians

  // Draw the front face of the diamond
  ctx.beginPath();
  ctx.moveTo(0, -size);
  ctx.lineTo(size, 0);
  ctx.lineTo(0, size);
  ctx.lineTo(-size, 0);
  ctx.closePath();
  ctx.strokeStyle = '#61dafb';
  ctx.lineWidth = 2;
  ctx.stroke();

  // Draw the back face of the diamond (optional for 3D effect)
  ctx.beginPath();
  ctx.moveTo(0, -size * 0.8);
  ctx.lineTo(size * 0.8, 0);
  ctx.lineTo(0, size * 0.8);
  ctx.lineTo(-size * 0.8, 0);
  ctx.closePath();
  ctx.strokeStyle = '#61dafb';
  ctx.lineWidth = 2;
  ctx.stroke();

   ctx.restore();
}

let angle = 0;
function animate() {
  angle += 2; // Increment the angle for rotation
  drawDiamond(angle);
  requestAnimationFrame(animate);
}

animate();

The animation I’m trying to achieve is similar to this Pinterest https://ar.pinterest.com/pin/595952963194601283/ reference, where the diamond rotates smoothly with a 3D-like effect.

selection synchronization not work on large dataset [closed]

I tryed simple example with 6 graphs that i need in sinc.
All graphs had relatively large dataset (around 100000 values each).
Zoom is in sync but selection (value prewiewv by mouse) is not showing across graphs.

large dataset:
data around 100000 samples

small dataset:
data around 50 samples

see the dots in middle of graph where cursor is located. in small dataset the dots are visible in all graphs. in large dataset only in main graph is the dots visible.

i try set graphs to 100 values each and then selec line apear.

Node.js vs React.js – Which One Should I Choose for My Web Project? [closed]

I am currently planning a web project and trying to decide between Node.js and React.js. I understand that they serve different purposes—Node.js is a backend runtime, while React.js is a frontend library. However, I’m still confused about when to choose one over the other.

Here are a few key points I’d like to clarify:

Use Cases – In what scenarios would Node.js be a better choice than React.js, and vice versa?
Performance & Scalability – How do they compare in terms of handling large-scale applications?
SEO & Rendering – How does React.js handle SEO challenges, and can Node.js improve this?
Combining Both – When does it make sense to use Node.js with React.js together?
I want to make an informed decision based on my project’s requirements. Could someone with experience explain the best use cases for each? Any insights, benchmarks, or examples would be greatly appreciated!

SyntaxError when using @nuxtjs/mdc in Nuxt.js – How to fix?

@nuxtjs/mdc SyntaxError

Okay, so I visited the official documentation at this link.

https://nuxt.com/modules/mdc

and wanted to add the MDC renderer to try using it for Markdown. I installed it using the following command:

npx nuxi@latest module add mdc

and added it into the modules:

export default defineNuxtConfig({
  modules: ['@nuxtjs/mdc']
})

my final code looks like this

<template>
  <div>
    <h1>text</h1>
    <MDC :value="md" tag="article" />
  </div>
</template>

<script setup lang="ts">
const md = `
  Hello MDC
`
</script>

<style>

</style>

but when I try to load the page, I get the following error in the console:

@sentry_vue.js?v=ffe9eaf3:816 [nuxt] error caught during app initialization H3Error: The requested module '/_nuxt/node_modules/debug/src/browser.js?v=ffe9eaf3' does not provide an export named 'default'
Caused by: SyntaxError: The requested module '/_nuxt/node_modules/debug/src/browser.js?v=ffe9eaf3' does not provide an export named 'default' (at create-tokenizer.js?v=ffe9eaf3:40:8)

and on screen:

500
The requested module '/_nuxt/node_modules/debug/src/browser.js?v=ffe9eaf3' does not provide an export named 'default'

If I comment out the mdc I dont get the error and the page works.

<template>
  <div>
    <h1>text</h1>
    <!-- <MDC :value="md" tag="article" /> -->
  </div>
</template>

<script setup lang="ts">
const md = `
  Hello MDC
`
</script>

<style>

</style>

What did I do wrong, why is it not working ?

Not working:
Not working

Working it shows the text:
Working

Versions:

"@nuxtjs/mdc": "^0.14.0",
"@nuxt/content": "^3.1.1",
"nuxt": "^3.15.4",

Textbox highlighted issue in create.js

When I click the audio button, the first textbox of the word should be
highlighted. However, when I click the audio button again, the first
textbox of that word is not highlighted, instead, the highlight
appears somewhere else. Sometimes, the highlighter works correctly,
but other times, it does not highlight the respective textbox properly

var yr2_chaptertextarray = ["väg", "vägg", "glas", "glass"];

var yr2_soundsArr = [
    bloburl + "https://develop-lasresan-ak2.majema.se/Lasresan/AK2/YR2-StudentsWeb/Tincan/stava/audio/chapter4/audio1.mp3",
    bloburl + "https://develop-lasresan-ak2.majema.se/Lasresan/AK2/YR2-StudentsWeb/Tincan/stava/audio/chapter4/audio2.mp3",
    bloburl + "https://develop-lasresan-ak2.majema.se/Lasresan/AK2/YR2-StudentsWeb/Tincan/stava/audio/chapter4/audio3.mp3",
    bloburl + "https://develop-lasresan-ak2.majema.se/Lasresan/AK2/YR2-StudentsWeb/Tincan/stava/audio/chapter4/audio4.mp3",
];

shuffle(yr2_chaptertextarray, yr2_soundsArr);


var count = 0;
for (var i = 0; i < 10; i++) {
    for (var j = 0; j < 1; j++) {
        var posX = i > 4 ? 900 : 400;
        var posY = i > 4 ? -102 : 298;

        var yr2_audio_container1 = new createjs.Container();
        let audiobutton1 = new createjs.Bitmap(audiobutton_1);
        audiobutton1.setTransform(posX, posY + (i * 80));
        audiobutton1.cursor = "pointer";

        yr2_audio_container1.addChild(audiobutton1);
        yr2_audio_container1.id = count;
        yr2_audio_container1.click = 0;
        yr2_audio_container1.name = "removeimage";
        yr2_audio_container1.on("click", checkaudio);

        stage.addChild(yr2_audio_container1);
        count++;
    }
}

var textBoxes = [];
var answercorrect = 0;
var wordPositions = [];

// Combine words and store their positions
let combineword = '';
for (let j = 0; j < yr2_chaptertextarray.length; j++) {
    let word = yr2_chaptertextarray[j];
    let position = combineword.length;
    combineword += word;
    wordPositions.push({ word, position });
}

// Function to check audio on click
function checkaudio(event) {
    event.currentTarget.click++;

    if (event.currentTarget.click > 1) {
        sounds[event.currentTarget.id].load();
        sounds[event.currentTarget.id].play();
        resethighlighter();
        startingpos(event);
    } else {
        resethighlighter();
        sounds[event.currentTarget.id].load();
        sounds[event.currentTarget.id].play();

        let QstnTxtArr;
        let Qstn = createQstnArr(yr2_chaptertextarray[event.currentTarget.id], 872, 690, 90, 0);
        QstnTxtArr = Qstn[event.currentTarget.id];

        let Xpos = event.currentTarget.id > 4 ? 1008 : 510;
        let yaxis_box = event.currentTarget.id > 4 ? -100 : 300;

        for (let col = 0; col < yr2_chaptertextarray[event.currentTarget.id].length; col++) {
            let xaxis_box = Xpos + (col * 50);

            let textBoxtemp = new createjs.Container();
            let textBox_rectangle = new createjs.Shape();
            textBox_rectangle.graphics.setStrokeStyle(2).f('#FFFFFF').beginStroke('#F08300').drawRoundRect(0, 0, 50, 50, 0);
            textBox_rectangle.setTransform(xaxis_box, yaxis_box + (event.currentTarget.id * 80));

            let textBox_highlighter = new createjs.Shape();
            textBox_highlighter.graphics.setStrokeStyle(2).f("#FDF5CE").beginStroke('#F08300').drawRoundRect(0, 0, 50, 50, 0);
            textBox_highlighter.setTransform(xaxis_box, yaxis_box + (event.currentTarget.id * 80));
            textBox_highlighter.visible = (col === 0);

            var textBoxText = new createjs.Text("", "63px MajemaSkol_Exercise_bold", "#808080");
            textBoxText.textAlign = 'center';
            textBoxText.x = xaxis_box + 25;
            textBoxText.y = yaxis_box + 35 + (event.currentTarget.id * 80);
            textBoxText.textBaseline = "alphabetic";

            textBoxtemp.addChild(textBox_rectangle, textBox_highlighter, textBoxText);
            textBoxtemp.name = "removeimage";
            textBoxtemp.textBoxText = textBoxText;

            textBoxtemp.on('click', function (evt) {
                document.getElementById("temp_textbox").style.display = "block";
                $("#temp_textbox").val("").focus();

                if (answercorrect === 0) {
                    for (var i = 0; i < textBoxes.length; i++) {
                        textBoxes[i].children[1].visible = false;
                        textBoxes[i].isClicked = false;
                    }
                    evt.currentTarget.children[1].visible = true;
                    evt.currentTarget.isClicked = true;
                }
            });

            textBoxes.push(textBoxtemp);
            if (col === 0) textBoxtemp.isClicked = true;

            stage.addChild(textBoxtemp);
        }
    }
}

// Function to reset highlighters
function resethighlighter() {
    for (var j = 0; j < textBoxes.length; j++) {
        textBoxes[j].isClicked = false;
        textBoxes[j].children[1].visible = false;
    }
}

// Function to start highlighting correct position
function startingpos(event) {
    console.log(wordPositions);
    if (wordPositions[event.currentTarget.id].word === yr2_chaptertextarray[event.currentTarget.id]) {
        textBoxes[wordPositions[event.currentTarget.id].position].isClicked = true;
        textBoxes[wordPositions[event.currentTarget.id].position].children[1].visible = true;
    }
}

Missing module (rc-util/es/Dom/canUseDom) and possible conflict between CommonJS and ES modules causing build failure

The issue occurs during the frontend Next.js build process (npm run build) due to a missing module error for /node-modules/rc-util/es/Dom/canUseDom, which is required by node_modules/rc-util/es/Dom/dynamicCSS.js.

There are also experimental warnings related to ES module usage in @ant-design/icons-svg, which could be a conflict between CommonJS and ES module imports.

Is there something I am missing? Are these 2 issues related?

I have manually checked and both files are present.
Also, I have tried removing package-lock.json and node-modules, then npm install before building again, but does not work.
Cleaning cache does not work either. Could be some dependency issues between ANTD and rc-util, but both haven been updated to the latest versions ([email protected] [email protected])

I have reinstalled the dependencies, but it is not working either.

Custom scrollbar div blocks wheel scrolling on hover

I am trying to create a custom scrollbar using a div element. The scrollbar itself works well, but I noticed an issue:

When I place my mouse cursor over the scrollbar track or the thumb (as shown in the image below, the green and gray areas), scrolling the mouse wheel does not properly scroll the content.

Scrollbar track and thumb (wheel scroll issue)

Here is my code:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Custom Floating Scrollbar</title>
    <style>
        * {
            padding: 0;
            margin: 0;
            box-sizing: border-box;
        }

        main {
            width: 100vw;
            height: 100vh;
            overflow: hidden;
            position: relative;
        }

        .scroll-content {
            width: 100%;
            height: 100%;
            overflow-y: scroll;
            scrollbar-width: none; /* Firefox */
        }

        .scroll-content::-webkit-scrollbar {
            display: none; /* Chrome */
        }

        .custom-scrollbar {
            position: absolute;
            top: 0;
            right: 30px;
            width: 50px;
            height: 100%;
            background-color: #3CB521;
        }

        .scroll-thumb {
            width: 100%;
            height: 50px;
            background: gray;
            position: absolute;
            top: 0;
            transition: background 0.2s;
            padding-right: 30px;
        }

        .scroll-thumb::after {
            content: "";
            position: absolute;
            top: 0;
            right: -30px;
            width: 30px;
            height: 100%;
            background: transparent;
        }

        .scroll-thumb:hover, .scroll-thumb:active {
            background: black;
        }

        section {
            width: 100%;
            height: 100vh;
            display: grid;
            place-items: center;
            font-size: 8em;
        }

        section:nth-child(1) {
            background: #fff2cc;
        }

        section:nth-child(2) {
            background: #e2f0d9;
        }

        section:nth-child(3) {
            background: #deebf7;
        }

        section:nth-child(4) {
            background: #fbe5d6;
        }

    </style>
</head>
<body>
<main>
    <div class="scroll-content">
        <section>1</section>
        <section>2</section>
        <section>3</section>
        <section>4</section>
    </div>
    <div class="custom-scrollbar">
        <div class="scroll-thumb"></div>
    </div>
</main>
<script>
    const content = document.querySelector('.scroll-content');
    const scrollbar = document.querySelector('.custom-scrollbar');
    const thumb = document.querySelector('.scroll-thumb');

    function updateThumbHeight() {
        let contentHeight = content.scrollHeight;
        let visibleHeight = content.clientHeight;
        let thumbHeight = Math.max((visibleHeight / contentHeight) * visibleHeight, 10);
        thumb.style.height = `${thumbHeight}px`;
    }

    function syncThumbPosition() {
        let scrollRatio = content.scrollTop / (content.scrollHeight - content.clientHeight);
        let maxThumbTop = scrollbar.clientHeight - thumb.clientHeight;
        thumb.style.top = `${scrollRatio * maxThumbTop}px`;
    }

    content.addEventListener('scroll', () => {
        requestAnimationFrame(syncThumbPosition);
        sessionStorage.setItem("scrollPosition", content.scrollTop);
    }, {passive: true});

    window.addEventListener('load', () => {
        updateThumbHeight();
        let savedScrollPosition = sessionStorage.getItem("scrollPosition");
        if (savedScrollPosition !== null) {
            content.scrollTop = parseInt(savedScrollPosition, 10);
            syncThumbPosition();
        }
    });

    let isDragging = false, startY, startScrollTop;

    thumb.addEventListener('mousedown', (e) => {
        isDragging = true;
        startY = e.clientY;
        startScrollTop = content.scrollTop;
        document.body.style.userSelect = 'none';
    });

    document.addEventListener('mouseup', () => {
        isDragging = false;
        document.body.style.userSelect = '';
    });

    document.addEventListener('mousemove', (e) => {
        if (!isDragging) return;
        let deltaY = e.clientY - startY;
        let scrollRatio = (content.scrollHeight - content.clientHeight) / (scrollbar.clientHeight - thumb.clientHeight);
        content.scrollTop = startScrollTop + deltaY * scrollRatio;
    });

    window.addEventListener('resize', () => {
        updateThumbHeight();
        syncThumbPosition();
    });

    if ('scrollRestoration' in history) {
        history.scrollRestoration = "manual";
    }
</script>
</body>
</html>

Issue:

  • When I hover over the custom scrollbar track (green area) or the scrollbar thumb (gray area), scrolling with the mouse wheel does not work.

  • Scrolling works fine when the mouse is outside the scrollbar area.

Expected Behavior:

  • The content should scroll when the mouse wheel is used, even when the cursor is over the custom scrollbar.

Why am I getting error: is not a function? [duplicate]

I have:

/services/db.js

import mysql from "../node_modules/mysql2/promise.js";
import config from "./config.js";

async function query(sql, params) {
    const connection = await mysql.createConnection(config.db);
    const [results] = await connection.execute(sql, params);

    return results;     
}

export default {
    query
};

/routes/testRoutes.js

const express = require('express');
const router = express.Router();

const db = require('../services/db.js');
const { emptyOrRows } = require('../services/helper.js');

// Test the functionality
router.get('/', async (req, res) => {
  try {
    const rows = await db.query('SELECT * FROM customers');
    const data = emptyOrRows(rows);
    console.log(data);
    res.json(data);
  }  catch (error) {
    res.status(500).json({ message: 'Error retrieving test', error: error.message });
  }
});

module.exports = router;

I get error: db.query is not a function

I also tried:

const query = require('../services/db.js');

. . . 

const rows = await query(`SELECT * FROM customers`);
const { query } = require('../services/db.js');

. . . 

const rows = await query(`SELECT * FROM customers`);

What am I doing wrong?

How to replace findDOMNode to get element’s right position in React class component?

I have a class component where I used findDOMNode to get the real DOM node and calculate its right property for positioning logic. Now I need to replace findDOMNode, but I can’t find an alternative to get the right value. What can I do?

Here’s my code:

class MyComponent extends React.Component {
  componentDidMount() {
    if (this.settingBtnRef) {
      const triggerDom = findDOMNode(this.settingBtnRef);
      const { right } = triggerDom.getBoundingClientRect();
      if (document.body.clientWidth - right < 240) {
        this.setState({ settingPopPlacement: 'bottomRight' });
      }
    }
  }

  render() {
    return (
      <Button
        onClick={() => this.showList()}
        type="primary"
        ref={(e) => { this.settingBtnRef = e }}
      >
        Settings
      </Button>
    );
  }
}

What I’ve Tried:

The ref callback gives me the component instance of Button, not the actual DOM node.
I know findDOMNode is deprecated, but I need a way to access the DOM’s right property for layout calculations.
What’s the Best Alternative to findDOMNode Here?

Next.js dynamic import with unknown variable

I’m trying to dynamically load a component inside a folder (or subfolder) using the apps router. But i’m getting some weird behaviour from next.

import dynamic from "next/dynamic";
// import test from "@system/plugins/elems/home/Banner1"; //yeah, im sure it exists

export async function importElement(
  elem: HomeElement_t, // {type: 'Banner1', plugin?: 'banner', params: {...}}
  key = 0
): Promise<React.ReactNode> {
  // console.log('test',test);
  let url = '';
  if (elem.plugin) {
    url = `plugins/${elem.plugin}`;
  }

  try {
    const Elem = dynamic(() => import(
      /* webpackInclude: /@system/(elems|plugins/elems)/.+/ */
      '@system/' + url + '/elems/' + elem.type
    ).then((mod) => mod.default));

    return <Elem {...elem.params} key={key}/>;
  } catch (e) {
    console.log('e', e);
    return ' ';
  }
}

The current code catches the following:

Error at runtime

I’m really losing my mind on this one. It’s been 2 months since this bug appeared :(.

I tried several things and tests:

  • replace the url inside the import to plugins/banner: Works, but I lose the other path i was supposed to use.
  • Use webpackIgnore: throws a weird error (TypeError: undefined is not an object (evaluating 'error.stack'))
  • Creating a second import through a if-else statement: Only reads the last import (ignoring the if-else), causing the error to be against @system/plugins.
  • Adding @system (the project root) alias to both tsconfig.json and the webpack config’s config.resolve.alias. No difference.

Unable to update popover content in show.bs.popover event

I can’t understand why the following code won’t work.

$('img.example-conversion').on('show.bs.popover', function () {
    var popover = bootstrap.Popover.getInstance(this);
    popover._config.content = 'Custom Text!!!';
    popover.setContent();
    popover.update();
});

No content is set at all.

If I save the popover returned from the initialization and use that, it works! My problem is that I have more than one popover on the page, so I need to know which one is being displayed.

Some documentation shows bootstrap.Popover.getInstance() takes a selector. But the Bootstrap documentation gives the following example.

var exampleTriggerEl = document.getElementById('example')
var popover = bootstrap.Popover.getInstance(exampleTriggerEl)

I’ve verified this is the element the popover is attached to, so why won’t this work? I even checked the value returned by bootstrp.Popover.getInstance(this), and it appears to be a valid popover object. But I cannot find any way to modify the text using this object.

jsxgraph only positive axis including zero

As the title says, how do I define the default axes (both x and y) that they start with 0, only go in positive direction, and have a tick and a label at zero.

I figured out, that straightFirst:false forces a non-negative axis and drawZero should add a label/major tick at 0. However, they do not seem to work together. Any ideas?
Here is the relevant part:

axis:true,
    defaultAxes: {
      x: {
        straightFirst:false,
        strokeWidth: 1,
        strokeColor: "#000000",
        ticks: {
          strokeColor: "#000000",
          drawZero: true,
          insertTicks: false,
          ticksDistance: 1,
          strokeOpacity: 1,
          strokeWidth: 1,
          majorHeight: 5,
          minorTicks:false,
          majorTickEndings: [0,1]} },
      y: {
        straightFirst:false,
        strokeWidth: 1,
        strokeColor: "#000000",
        ticks: {
          drawZero: true,
          strokeColor: "#000000",
          insertTicks: false,
          ticksDistance: 1,
          strokeOpacity: 1,
          strokeWidth: 1,
          majorHeight: 5,
          minorTicks:false,
          majorTickEndings: [1,0]} }
    }