Fix Breadcrumbs in Vue js

I work on a project in vue js. I want to create a website with several pages (routers). Therefor I created a breadcrumb trail component to show the current pathway. I am new to vue js but I think I am on the right track.

  • But have some minor bugs which I cannot fix. I don’t want the “>” seperator between the crumbs to be underlined, when I hover over the latest breadcrumb element.
  • I also don’t want the cursor to change to a pointer when hovered over the “>” seperator.
  • And I cannot get rid of the “>” seperator when only the Homepage is shown. It look like this then: “Startseite >”.
  • Between the image and the “Startseite” string is an underline, when I hover over the string, which I want to get rid off.

This is my breadcrumb.vue component:

<template>
  <nav class="breadcrumb">
    <router-link v-for="(crumb, index) in breadcrumbs" :key="index" :to="crumb.path">
      <template v-if="crumb.name === 'Startseite'">
        <img :src="homeIcon" alt="Home" class="breadcrumb-icon" />
        {{ crumb.name }}
      </template>
      <template v-else>
        {{ crumb.name }}
      </template>
    </router-link>
  </nav>
</template>

<script>
import homeIcon from '@/assets/img/Home.svg'; // Pfad zur SVG-Datei

export default {
  data() {
    return {
      homeIcon,
    };
  },
  computed: {
    breadcrumbs() {
      let pathArray = this.$route.path.split("/");
      pathArray.shift(); // Remove the first empty element
      let breadcrumbArray = [{ name: "Startseite", path: "/" }];
      pathArray.forEach((path, index) => {
        breadcrumbArray.push({
          name: path.charAt(0).toUpperCase() + path.slice(1),
          path: "/" + pathArray.slice(0, index + 1).join("/"),
        });
      });
      return breadcrumbArray;
    },
  },
};
</script>

<style scoped>

.breadcrumb-icon {
  width: 1.4rem;
  height: 1.4rem;
  
}
nav a.router-link-exact-active {
  color: black;
}

</style>

this is the component css file:


@media (min-width: 1024px) {
  .Veranstaltungen {
    min-height: 100vh;
    display: flex;
    align-items: center;
  }
}


html, body {
    overflow: hidden; /* Hide scroll bars */
    margin: 0; /* Remove default margin */
    padding: 0; /* Remove default padding */
    width: 100vw; /* Ensure full viewport width */
    height: 100vh; /* Ensure full viewport height */
}

/* upper Rectangle */

.ver_background {
    position: absolute;
    width: 100vw; /* 100% of the viewport width */
    height: 100vh; /* 100% of the viewport height */
    left: 0;
    top: 80px;
    background: #E4E4E4; /* White background */
    border-radius: 20px 20px 0 0;
}


/* lower Rectangle Copy */

.ver_window {

position: absolute;
width: 100vw; /* 100% of the viewport width */
height: 100vh; /* 100% of the viewport height */
left: 0px;
top: 244px;
background: #F9FAFA;
border-radius: 20px 20px 0px 0px;
}

/* Neue Veranstaltung */

.headline {
position: absolute;
width: 333px;
height: 40px;
left: 40px;
top: 132px;

/* Headline_32 */

font-style: normal;
font-weight: 800;
font-size: 2rem;
line-height: 3.0rem;
/* identical to box height, or 125% */

color: #000000;

}

.breadcrumb {
  display: flex;
  list-style: none;
  padding: 0;
  margin: 0;
  font-size: 1rem;
  color: #000000;
  position: absolute;
  width: auto;
  height: auto;
  left: 2.5rem;
  top: 6rem;

}

  .breadcrumb router-link a {
    text-decoration: none;
    color: black; /* Set the text color to black */
    padding: 5px;
  }
  
  .breadcrumb router-link:hover,
  .breadcrumb router-link a:hover {
    text-decoration: underline;
    color: darkgoldenrod; /* Change color on hover */
  }
  
  .breadcrumb router-link::after,
  .breadcrumb router-link a::after {
    content: ' / '; /* Add separator */
    color: #999; /* Separator color */
  }
  
  .breadcrumb router-link:last-child::after,
  .breadcrumb router-link a:last-child::after {
    content: ''; /* Remove separator after the last breadcrumb */
  }
  
  .breadcrumb a {
    text-decoration: none;
    color: #000000;
    padding: 0.2rem;
  
  }
  
  .breadcrumb a:hover {
    text-decoration: underline;
    background-color: #ff000000;
  }
  
  .breadcrumb a + a::before {
    content: ">";
    padding: 0 0.5rem;
    color: #6c757d;
    text-decoration: none; /* Ensure the ">" does not get underlined */
   
  }
  
  .breadcrumb-icon {
    width: 1.4rem;
    height: 1.4rem;
    margin-right: 0.2rem;
    position: relative;
    top: 0.25rem; /* Adjust this value to move the image up or down */
  
  }
  
  

this is the base.css file:

/* color palette from <https://github.com/vuejs/theme> */
:root {
  --vt-c-white: #ffffff;
  --vt-c-white-soft: #f8f8f8;
  --vt-c-white-mute: #f2f2f2;

  --vt-c-black: #2d3a42;
  --vt-c-black-soft: #222222;
  --vt-c-black-mute: #282828;

  --vt-c-indigo: #2c3e50;

  --vt-c-divider-light-1: rgba(60, 60, 60, 0.29);
  --vt-c-divider-light-2: rgba(60, 60, 60, 0.12);
  --vt-c-divider-dark-1: rgba(84, 84, 84, 0.65);
  /* --vt-c-divider-dark-2: rgba(84, 84, 84, 0.48); */

  --vt-c-text-light-1: var(--vt-c-indigo);
  --vt-c-text-light-2: rgba(60, 60, 60, 0.66);
  --vt-c-text-dark-1: var(--vt-c-white);
  --vt-c-text-dark-2: rgba(235, 235, 235, 0.64); 
}

/* semantic color variables for this project */
:root {
  --color-background: var(--vt-c-white);
  --color-background-soft: var(--vt-c-white-soft);
  --color-background-mute: var(--vt-c-white-mute);

  --color-border: var(--vt-c-divider-light-2);
  --color-border-hover: var(--vt-c-divider-light-1);

  --color-heading: var(--vt-c-text-light-1);
  --color-text: var(--vt-c-text-light-1);

  --section-gap: 160px;
}

@media (prefers-color-scheme: dark) {
  :root {
    --color-background: var(--vt-c-black);
    --color-background-soft: var(--vt-c-black-soft);
    --color-background-mute: var(--vt-c-black-mute);

    --color-border: var(--vt-c-divider-dark-2);
    --color-border-hover: var(--vt-c-divider-dark-1);

    --color-heading: var(--vt-c-text-dark-1);
    --color-text: var(--vt-c-text-dark-2);
  }
}

*,
*::before,
*::after {
  box-sizing: border-box;
  margin: 0;
  font-weight: normal;
}

body {
  min-height: 100vh;
  /*color: var(--color-text);*/
  background: var(--color-background);
  transition:
    color 0.5s,
    background-color 0.5s;
  line-height: 1.6;
  font-family:
    Inter,
    -apple-system,
    BlinkMacSystemFont,
    'Segoe UI',
    Roboto,
    Oxygen,
    Ubuntu,
    Cantarell,
    'Fira Sans',
    'Droid Sans',
    'Helvetica Neue',
    sans-serif;
  font-size: 15px;
  text-rendering: optimizeLegibility;
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
}


Do you know how to fix these issues or have a hint for me?

I edited the code and added several css attributes for hover effects. But the “>” cannot be accessed by any css class as far as I see.
I created a new css class for the hover case with:

.breadcrumb a:hover + a::before {
  color: #007bff; /* Change this to your desired hover color */
}

But it did not work.

How do I keep the tab that I receive from backend in a string? [duplicate]

I’m having an issue with something I receive from my back-end. I get a string with line breaks and tabs. The line breaks are easy to handle, I just do a replaceAll('n', '<br>').

But I don’t know how to handle the tabs.

This is what I receive :

Back result

And this is how it’s displayed :

Display

My code is the following :

  displayConfirmDialog(data: string) {
    console.log(data)
    this.confirmationService.confirm({
      message: data !== "" ? data.replaceAll('n', "<br>").replaceAll("\t","t") : "Ce client n'est lié à aucun site, atelier ou équipement.", // This is where I handle the line breaks (and tried to handle the tabs)
      header: "Suppression",
      closable: true,
      closeOnEscape: true,
      icon: "pi pi-exclamation-triangle",
      accept: () => console.log("AAAAAAAAAAAH"),
      reject: () => this.displayMessage("Suppression de la résolution annulée.", "Annulation", "warn", 3000),
    })
  }

As you can see, I tried to replace the ‘t’ as well but honestly I don’t know what it does.

Sorry if it’s obvious.

select a value from dropdown whenever page loads using jquery or jscript

I am trying to set records per page value whenever my webpage loads. This value is a dropdown, but I want to set it to static value of 50.

enter image description here

I have written a java script to achieve this but i am getting how to set the value always to 50.
Below is my Jscript

$(document).ready(function() {
  if (window.location.href.indexOf("workitem/workItems.jsf") > -1) {
    console.log("I am on Workitems Page");
    var MutationObserver = window.MutationObserver || window.WebKitMutationObserver || window.MozMutationObserver;
    var observer = new MutationObserver(function(mutations) {
      mutations.forEach(function(mutation) {
        for (var i = 0; i < mutation.addedNodes.length; i++) {
          if ($("[id*='page-size-dropdown-toggle']").length) {
            console.log("detected pages button");
            //$("[id*='page-size-dropdown-toggle']").prop('disabled', true);
            //$("[id*='page-size-dropdown-toggle']").val("50").change();
            $("[id*='page-size-dropdown-toggle']").data("n    50 n    ");
          }
        }
      });
    });
    observer.observe(document, {
      childList: true,
      subtree: true,
      attributes: false
    });
  }
});

Below is the xhtml page in inspect mode.

enter image description here

Ensure UpdatePanel Content is Printed After __doPostBack Completes in ASP.NET WebForms

I am trying to print an ASP.NET UpdatePanel when the user presses Ctrl + P, but since __doPostBack triggers a postback that updates the panel, the print preview sometimes shows outdated content or a loading spinner instead of the updated content. I tried using Sys.WebForms.PageRequestManager.getInstance().add_endRequest() to detect when the update is complete and then calling window.print(), but it still sometimes prints before the page is fully updated. Adding setTimeout() and requestAnimationFrame() helped slightly, but the issue persists, and I need a reliable way to ensure that the print preview always shows the latest UpdatePanel content.

document.addEventListener("keydown", function (event) {
    if (event.ctrlKey && event.key === "p") {
        event.preventDefault();
        sessionStorage.setItem("printTriggered", "true");
        __doPostBack('<%= myUpdatePanel.ClientID %>', '');
    }
});

Sys.WebForms.PageRequestManager.getInstance().add_endRequest(function () {
    if (sessionStorage.getItem("printTriggered") === "true") {
        sessionStorage.removeItem("printTriggered");
        var loadingElement = document.getElementById("loadingComponent"); //optional
        if (loadingElement) {
            loadingElement.style.display = "none";
        }
        setTimeout(() => {
            requestAnimationFrame(() => {
                window.print();
            });
        }, 500);
    }
});

How can I ensure that window.print() is executed only after the UpdatePanel content is fully updated, including backend processing, so the print preview never shows outdated content?

not receiving cookies from backend [duplicate]

I’m working on a React frontend and an Express backend, and I’m trying to send a login request using axios. However, the requisite does not include any cookies, even though I’m using withCredentials: true and cors, and have token in a cookie, I got undefined

import axios from "axios";

const login = async () => {
  try {
    const getData = async () => {
    try {

      const {data} = await axios.get("http://127.0.0.1:5000/api/auth/data",{
        withCredentials: true,
        headers: {
          "Content-Type": "application/json",
        },
      });
      console.log(data);
    } catch (error) {
      console.log(error);
    }
  };

import "dotenv/config";
import express from "express";
import cors from "cors";
import cookieParser from "cookie-parser";
import connectDb from "./config/connectDb.js";
import authRoute from "./routes/authRoute.js";

const app = express();
const port = process.env.PORT || 4000;
connectDb();


app.use(express.json());
app.use(cookieParser());
app.use(
  cors({
    origin: "http://localhost:5173",
    credentials: true
  })
);


app.use("/api/auth", authRoute);

app.listen(port, () => console.log(`Server is running on port ${port}`));

import jwt from "jsonwebtoken";

export const isAuth = async (req, res, next) => {
  const  token  = req.cookies.token;
  if (!token) {
    return res.json({
      success: false,
      message: "You are not authorized",
    });
  }

  try {
    const decoded = jwt.verify(token, process.env.JWT_SECRET);
    if (decoded.id) {
      req.body.userId = decoded.id;
    }else{
        return res.status(400).json({
            success: false,
            message: "You are not authrized, login again"
        })
    }
    next();
  } catch (error) {
    return res.status(400).json({
      success: false,
      message: error.message,
    });
  }
};

What I’ve tried:

  • Used withCredentials: true in axios request.
  • Enabled credentials: true in cors middleware.
  • Used cookieParser() in Express.
  • Verified that Set-Cookie is included in the response headers (checked via Postman & Network tab in Chrome DevTools).
  • Checked browser settings to ensure third-party cookies are not blocked.

Best way to find if string matches in Array Object Javascript [closed]

I have following array object list

errordata = [{"errvar":"user.name","message":"missing username"}, 
             {"errvar":"user.age","message":"missing age"}, 
             {"errvar":"user.gender","message":"missing gender"}, 
             {"errvar":"user.city","message":"missing city"}
            ]

I have couple of strings “city”,”age” .I need to find if any of these 2 strings(city,age) are present/matching in the any of errvar of errordata list object.

If even one of string is matching then I need to add some condition logic. How can we search that in best possible way.

Any help would be appreciated

Error message: Property ‘onCommentCreate’ does not exist on type ‘IFrame’.ts(2339)

I’m trying to create a trigger when a new comment is posted in Wix Comments widget.
This is the code in the wix comments widget page (Frontend):

$w("#wixComments1").onCommentCreate((widgetComment) => {
    // Log the newly created comment to the console
    console.log("New comment created:", widgetComment);
    // You can add additional logic here to process the comment
});

The Wix IDE throws this error: “Property ‘onCommentCreate’ does not exist on type ‘IFrame’.ts(2339)”

Can anyone help me on this, i’m new to Velo…

unexpected end of archive error when trying to open a Zip file downloaded via javascript

I’m trying to download a ZIP file from a backend API that returns the ZIP binary as a string.

If I do this:
curl --location 'https://backend-endpoint.com/get-file' >> test.zip it creates a ZIP file containing a couple of files that I can extract and open correctly.

On the frontend side I have a button that, if clicked, calls the following code that should download the ZIP file after the backend API call:

const convertBinaryToBlob = (binary: string, contentType: string): Blob => {
    // Convert the binary string to a byte array
    const binaryLen = binary.length;
    const bytes = new Uint8Array(binaryLen);
    for (let i = 0; i < binaryLen; i++) {
      bytes[i] = binary.charCodeAt(i);
    }

    const blob = new Blob([bytes], { type: contentType });
    return blob;
};

clientAPI.getFile().then((resp) => {
    if (resp.status === 200) {
        let blobContent = convertBinaryToBlob(resp.data, 'application/zip');
        const href = URL.createObjectURL(blobContent);
        const link = document.createElement('a');
        link.href = href;
        link.setAttribute('download', 'test.zip');
        document.body.appendChild(link);
        link.click();
        document.body.removeChild(link);
        URL.revokeObjectURL(href);
    }
});

When I click the button a ZIP file is downloaded but it seems corrupt because If I try opening it I get the following error: unexpected end of archive

What am i doing wrong?

How to make any native know to use document.currentScript.src or import.meta.url

I have no control how a 3rd party loads my scipt.

Can be <script type="module" src="myscript.js"> or <script src="myscript.js">

When not loaded as “module” the code inside my script always errors with:

Uncaught SyntaxError: Cannot use ‘import.meta’ outside a module

This SO answer “Uncaught SyntaxError: Cannot use import statement outside a module” when importing ECMAScript 6
is not an answer for native/vanilla JavaScript without bundling.

Is it possible at all to test for “module” inside a non-module script?

A simple if does not work:

    let myURL = "";
    if (document.currentScript) {
        myURL = document.currentScript.src;
    } else {
        myURL = import.meta.url;
    }

Try-Catch does not work:

    let myURL = "";
    try {
        myURL = import.meta.url;
    } catch (e) {
        if (document.currentScript) myURL = document.currentScript.src;
    }

You-know-who and You-know-Elon-who come up with variations of above code that do not work, causing the error

webpack-cli TypeError: Cannot read properties of undefined (reading ‘getArguments’)

I am trying to deploy my application and getting this error. Locally it is working perfectly fine here is the complete stack trace:

    2025-02-20T09:20:27.0519522Z > [email protected] build
2025-02-20T09:20:27.0520079Z > npm run migrate && webpack --config webpack.prod.js
2025-02-20T09:20:27.0521155Z 
2025-02-20T09:20:29.0825668Z 
2025-02-20T09:20:29.0838195Z > [email protected] migrate
2025-02-20T09:20:29.0838877Z > npx sequelize-cli db:migrate
2025-02-20T09:20:29.0839675Z 
2025-02-20T09:20:36.2886604Z 
2025-02-20T09:20:36.2887992Z [4mSequelize CLI [Node: 16.18.1, CLI: 6.6.2, ORM: 6.37.5][24m
2025-02-20T09:20:36.2888730Z 
2025-02-20T09:20:36.3267065Z Loaded configuration file "server/db/config.json".
2025-02-20T09:20:36.3267758Z Using environment "production".
2025-02-20T09:20:40.1777294Z No migrations were executed, database schema was already up to date.
2025-02-20T09:20:42.4559654Z [webpack-cli] TypeError: Cannot read properties of undefined (reading 'getArguments')
2025-02-20T09:20:42.4560664Z     at WebpackCLI.getBuiltInOptions (/var/www/html/litpath/litpath-dev/code/node_modules/webpack-cli/lib/webpack-cli.js:831:77)
2025-02-20T09:20:42.4562402Z     at makeCommand.options.entry (/var/www/html/litpath/litpath-dev/code/node_modules/webpack-cli/lib/webpack-cli.js:939:33)
2025-02-20T09:20:42.4563728Z     at async WebpackCLI.makeCommand (/var/www/html/litpath/litpath-dev/code/node_modules/webpack-cli/lib/webpack-cli.js:443:31)
2025-02-20T09:20:42.4566602Z     at async loadCommandByName (/var/www/html/litpath/litpath-dev/code/node_modules/webpack-cli/lib/webpack-cli.js:937:17)
2025-02-20T09:20:42.4571521Z     at async Command.<anonymous> (/var/www/html/litpath/litpath-dev/code/node_modules/webpack-cli/lib/webpack-cli.js:1336:17)
2025-02-20T09:20:42.4572305Z     at async Command.parseAsync (/var/www/html/litpath/litpath-dev/code/node_modules/webpack-cli/node_modules/commander/lib/command.js:935:5)
2025-02-20T09:20:42.4573019Z     at async WebpackCLI.run (/var/www/html/litpath/litpath-dev/code/node_modules/webpack-cli/lib/webpack-cli.js:1360:9)
2025-02-20T09:20:42.4574822Z     at async runCLI (/var/www/html/litpath/litpath-dev/code/node_modules/webpack-cli/lib/bootstrap.js:9:9)

My dependencies: (I am using node 16.x on my deployment server and locally it’s 20.x)

"webpack": "^5.92.1",
  "webpack-cli": "^5.1.4",

with this configuration my build is successfull but when running application on startup it failed.

My webpack.prod.js:

const merge = require('webpack-merge')
const webpack = require('webpack')
const TerserPlugin = require('terser-webpack-plugin')
const BrotliPlugin = require('brotli-webpack-plugin')
const NodePolyfillPlugin = require('node-polyfill-webpack-plugin')
const common = require('./webpack.common')

module.exports = () => {
  return merge(common, {
    optimization: {
      splitChunks: {
        chunks: 'all',
      },
      minimize: true,
      minimizer: [new TerserPlugin()],
    },
    devtool: 'source-map',
    mode: 'production',
    // https://webpack.js.org/plugins/define-plugin/
    // babel-minify-webpack look
    plugins: [
      new webpack.DefinePlugin({
        REACT_APP_SERVER_URL: JSON.stringify(process.env.BASE_URL),
      }),
      new BrotliPlugin({
        asset: '[path].br[query]',
        test: /.(js|css|html|svg)$/,
        threshold: 10240,
        minRatio: 0.8,
      }),
      new NodePolyfillPlugin(),
    ],
  })
}

my webpack.dev.js:

const path = require('path')
const merge = require('webpack-merge')
const webpack = require('webpack')
const NodePolyfillPlugin = require('node-polyfill-webpack-plugin')
const common = require('./webpack.common')

module.exports = () => {
  // location and env are `development` by default
  // https://webpack.js.org/plugins/define-plugin/
  return merge(common, {
    mode: 'development',
    devtool: 'inline-source-map',
    devServer: {
      static: path.join(__dirname, 'dist'),
      compress: true,
      host: 'localhost',
      port: 3000,
      historyApiFallback: true,
      // https://webpack.js.org/configuration/dev-server/#devserverproxy
      proxy: [
        {
          context: ['/auth', '/api'],
          target: 'http://localhost:3001',
          secure: false,
        },
      ],
    },
    plugins: [
      new webpack.DefinePlugin({
        REACT_APP_SERVER_URL: JSON.stringify('http://localhost:3001'),
      }),
      new NodePolyfillPlugin(),
    ],
  })
}

how to know when websocket upgrade fails in socket.io

i am using socket.io in a simple javascript app. I want to alert the user when upgrade to websocket fails due to any reason and HTTP long-polling is used as a fallback, so user knows that connection is not optimal.

I have read the socket.io documentation, websocket upgrade troubleshoot page and faq page. I know when upgrade to websocket is successful (e.g. socket.io.engine.on("upgrade", () => {});, but I don’t find the opposite there.

Apply correct CSS Animation to HTML ID element

I am trying to add an animation in ID “Rangasala” of the following HTML Code:

  <section id="showcase">
        <div class="container">
            <div class="bg">
                <h1 id="Rangasala"></h1>
                <p>Welcome to the Website of the Itahari Rangasala Karate Dojo</p>
            </div>
        </div>
    </section>

My CSS for fading animation:

@keyframes fadeIn {
    from {
        opacity: 0;
        transform: translateY(-20px);
    }
    to {
        opacity: 1;
        transform: translateY(0);
    }
}

.fadeIn {
    animation: fadeIn 2s ease-in-out forwards;
}

My JS Code:

document.addEventListener("DOMContentLoaded", () => {
    // Background image slider
    const showcase = document.getElementById("showcase");
    const images = [
        "./img/Dojo2.jpg",
        "./img/Dojo1.jpg",
        "./img/Dojo3.jpg"
    ];
    let index = 0;

    function changeBackground() {
        showcase.style.backgroundImage = `url('${images[index]}')`;

        // Apply clip-path and center only for Dojo3.jpg
        if (images[index].includes("Dojo3.jpg")) {
            showcase.style.clipPath = "inset(10% 0 0 0)";
            showcase.style.backgroundPosition = "center";
        } else {
            showcase.style.clipPath = "none";
            showcase.style.backgroundPosition = "center";
        }

        index = (index + 1) % images.length; // Loop through images
    }

    // Set initial background and start changing every 3 seconds
    changeBackground();
    setInterval(changeBackground, 3000);

    // Typed.js initialization
    var typed = new Typed('#Rangasala', {
        strings: ['Itahari Rangasala Karate Dojo'],
        typeSpeed: 50,
        backSpeed: 25,
        loop: true,
        startDelay: 1000,
        backDelay: 1500,
        onComplete: () => {
            // Add animation class after Typed.js completes
            const rangasalaElement = document.getElementById("Rangasala");
            rangasalaElement.classList.add("fadeIn");
        }
    });

    // Blur effect
    const bg = document.querySelector('.bg');
    let load = 0;
    let int = setInterval(blurring, 30);

    function blurring() {
        load++;
        if (load > 99) {
            clearInterval(int);
        }
        bg.style.filter = `blur(${scale(load, 0, 100, 30, 0)}px)`;
    }

    // Utility function for scaling
    function scale(num, in_min, in_max, out_min, out_max) {
        return ((num - in_min) * (out_max - out_min)) / (in_max - in_min) + out_min;
    }
});

I want the “Rangasala” ID to show “Itahari Rangasala Karate Dojo” on fading appear animation but it is working out.
Here is the Screenshot of my FrontEnd running:
Front End

How to make this image hover effect in website?

image warp effect

How can I create the effect shown in this image, where the part of the image under the mouse pointer appears to bend or warp as the cursor moves over it? I am new to web development and don’t have any experience with implementing such interactive effects. Could you please explain if this effect is possible to achieve using HTML, CSS, and JavaScript, or if it requires additional libraries like Three.js or GSAP? Also, if possible, I would appreciate a step-by-step guide or example code to help me understand how to implement it.

How does React’s useCallback read the variable in closures

In the following example, when the button is clicked, cb2 uses the memoized function. However, why doesn’t cb2 use the first render closures (countVal: 0)?

function TestHook() {

  const [count, setCount] = useState(0)
  let countVal = 0

  const cb = useCallback(() => {
    console.log('cb dep [count]', count, countVal)
  }, [count])

  const cb2 = useCallback(() => {
    console.log('cb2 dep []', count, countVal)
  }, [])

  useEffect(() => {
    cb() // first time: 0, 0 second time: 1, 0 
    cb2() // first time: 0, 0 second time: 0, 1
  })

  const add = () => {
    console.log('add', count)
    countVal ++
    setCount(count + 1)
  }

  return (
    <>
      <button onClick={add}>add</button>
    </>
  );
}

Question:
Can anyone explain the result of cb2() after a re-render?