Javascript WebdriverIO: Element did not become interactable

Im currently learning webdriver. I have a homework task to write a logout test for this site https://practicesoftwaretesting.com/auth/login in --headless mode for chrome, firefox and edge.

the steps are the following…

Scenario: User logs out successfully
    Given the user is on the login page
    And the user enters email "[email protected]" 
    And the user enters password "welcome01"
    And the user clicks the Login button
    When the user clicks the user menu
    And the user clicks the logout button
    Then the user redirected to the login page

here is my code in the spec file

import { expect, browser, $ } from '@wdio/globals'

describe('My Login application', () => {
    
    it('User logs in and logs out successfully', async () => {
        
        await browser.url('https://practicesoftwaretesting.com/auth/login')


        // LOGIN STEPS

        await $('[id="email"]').setValue('[email protected]');
        await $('[id="password"]').setValue('welcome01');
        await $('[value="Login"]').click();


        // LOGOUT STEPS

        await $('//*[@id="menu"]').click();

        await $('//*[@id="menu"]').waitForDisplayed();

        await $('/html/body/app-root/app-header/nav/div/div/ul/li[4]/ul/li[7]/a').click();

        await expect(browser).toHaveUrl('https://practicesoftwaretesting.com/auth/login');

        })

})

The login steps are working, but im stuck with the logout steps in chrome, because the logout button is in a dropdown menu. I get the following error message.

webdriverio(middleware): element did not become interactable in "My Login application.User logs in and logs out successfully"
webdriverio(middleware): element did not become interactable: Element <a _ngcontent-ng-c4175243378="" href="#" data-test="nav-menu" id="menu" role="button" data-bs-toggle="dropdown" aria-expanded="false" class="nav-link dropdown-toggle">Jack Howe</a> did not become interactable

Is there any solution for this?

PS: i had put it on github too, you can check it out there too

Chest animation stops working after adding item popup

I’m working on a 2D maze game in JavaScript with a chest-opening animation. The animation worked fine until I added a popup to show the item found.

Here’s the simplified logic:

  1. AnimationChest() updates the frame in requestAnimationFrame

  2. When the player opens a chest, openChest() plays audio, sets chestAnim.active = true, and calls addItemToInventory()

  3. addItemToInventory() triggers showChestPopup(), which displays a popup overlay

After adding the popup, the chest disappears and the animation doesn’t play.

Expected behavior:

Chest plays its opening animation before disappearing.

Actual behavior:

Chest disappears instantly. No animation plays.

Could the popup logic or overlay be interfering with the animation? Any help would be appreciated.

I expected the chest to animate like it usually did but after some changing in the code instead of animate the chest open it just disappeared. I tried to ask chatGPT, I tried to block test the entire code and I tried to rewrite the entire code but I weren’t able to find where is the problem

function openChest() {
  const pos = `${currentPlayer.x},${currentPlayer.y}`;
  if (maze[currentPlayer.y][currentPlayer.x] === 'C' && !openedChest.has(pos)) {
    openedChest.add(pos);
    chestAnim.active = true;
    document.getElementById("chestaudio").play();
    const item = allItems[Math.floor(Math.random() * allItems.length)];
    addItemToInventory(item); // shows popup
  }
}

function AnimationChest(delta) {
  chestAnim.totalTime += delta;
  if (chestAnim.totalTime >= chestAnim.frameSpeed) {
    chestAnim.totalTime = 0;
    chestAnim.currentFrame = chestAnim.active ?
      Math.min(chestAnim.currentFrame + 1, chestAnim.totalFrame - 1) :
      Math.max(chestAnim.currentFrame - 1, 0);
  }
}

function AnimationLoop(time) {
  const delta = time - lastTime;
  lastTime = time;
  AnimationChest(delta);
  drawMaze(); // draws chest with current frame
  requestAnimationFrame(AnimationLoop);
}

What is the tech stack, and how does JavaScript fit into it? [closed]

The specific technologies and tools used in the project or organization (tech stack)
How JavaScript is utilized within that tech stack (frontend, backend, or full-stack)The role JavaScript plays in the overall architecture and development of the project

In essence, the question aims to gain insight into the technical landscape and JavaScript’s position within it.

My while loop is not running smoothly, where did i go wrong?

//  NUMBER GUESSING GAME

// Html dom elements to work with

const rollBtn = document.getElementById("rollBtn");
let guess = document.getElementById("guess");
let display = document.getElementById("display");

// Other variables needed
let running = true;
let attempts = 0;
const minNum = 1;
const maxNum = 100;
const answer = Math.floor(Math.random() * (maxNum - minNum + 1)); 

rollBtn.onclick = function(){
    
    while(running){
        guess = Number(guess.value);
        if(isNaN(guess)){
            display.textContent = `Please, input a valid number between ${minNum} - ${maxNum}`;
        }
        else if(guess < minNum || guess > maxNum){
            display.textContent = `Please, input a number between ${minNum} - ${maxNum}`;
        }else{
            attempts++;

            if(guess < answer){
                display.textContent = "Too Low, Try again";
            }
            else if(guess > answer){
                display.textContent = "Too High, Try again";
            }
            else{
                display.textContent =`Correct! the answer is ${answer}, It took you ${attempts} attempt(s)`;

                running = false;
            }
            
        }
    }  
}
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Number Guessing Game</title>
    <link rel="stylesheet" href="style.css">
</head>
<body>
    <label for="guess">Enter a number:</label>
    <input type="text" id="guess">
    <button id="rollBtn">Roll</button>
    <h3 id="display"></h3>
    <script src="index.js"></script>
</body>
</html>

This is what the console shows me but can’t seem to resolve the issue:

Script terminated by timeout at:
rollBtn.onclick@http://127.0.0.1:5500/index.js:18:11
EventHandlerNonNull*@http://127.0.0.1:5500/index.js:16:1

How can I record a user’s screen on a mobile device in the browser?

I’m building a web application that needs to capture and save a user’s screen recording on their phone. On desktop browsers I can use:

if (navigator.mediaDevices && navigator.mediaDevices.getDisplayMedia) {
  const screenStream = await navigator.mediaDevices.getDisplayMedia({ video: true });
  const recorder = new MediaRecorder(screenStream);
  const chunks = [];

  recorder.ondataavailable = e => chunks.push(e.data);
  recorder.onstop = () => {
    const blob = new Blob(chunks, { type: 'video/webm' });
    // …upload or save “blob”…
  };

  recorder.start();
  // Stop after X seconds, or when user clicks “Stop”
}

But on mobile Safari (iOS 15+) and Chrome on Android, navigator.mediaDevices.getDisplayMedia is either undefined or prompts an error. I haven’t found any reliable documentation on capturing the entire screen (not just the camera) on mobile browsers.

•   **What APIs or polyfills** exist to enable screen capture on mobile browsers?
•   Are there any **workarounds** (e.g. WebRTC-based, Cordova/Capacitor plugins, user-driven screen-share intents) that allow me to record the screen without requiring a native app?
•   How do popular web apps (e.g. Loom, Zoom) achieve in-browser screen recording on smartphones?

Any guidance or examples would be greatly appreciated!

What I’ve tried:

1.  Using getDisplayMedia() on mobile — not supported.
2.  Fallback to getUserMedia({ video: { mediaSource: "screen" } }) — errors out.
3.  Investigated WebRTC screen-capture flags — no mobile support.

Counter not working when setInterval state in Reactjs

I am writing an automatic seconds counter and it should increment by 1 every second, but currently it is not working even though i have setInterval for it to repeat and setCount every 1000 ms (1 second). I just don’t got why it is.

My current code

import React, { useState, useEffect } from 'react';

function Counter() {
  const [count, setCount] = useState(0);

  useEffect(() => {
    const interval = setInterval(() => {
      setCount(count + 1);
    }, 1000);

    return () => clearInterval(interval);
  }, []);

  return <div>Count: {count}</div>;
}

I expect someone to help me find out why.

The expected result is that the count will increase by 1 every second.

Use data from mapping fetch to feed a component

I have a React component fetching an API and returning a list of users and some data for each in cards (user.map). I would like to make those cards clickable and for each one to return a individual card with more informations about those users. So I’m trying to pass the data for one specific user to another component (idividualCard). How can I do that?
Here is my code on React:

import {useState, useEffect } from "react";
import { Link } from "react-router-dom";
import { Card } from "react-bootstrap";
import { Rating } from "react-simple-star-rating";

export default function ArtisanAlimentation() {

  const [artisans, setArtisans] = useState([]);

  const getArtisan = async () => {
    const res = await fetch ("http://localhost:3000/artAlim");
    const json = await res.json();
    setArtisans (json);
  }

  
  useEffect (() => {
    getArtisan();
  },[])

return (

    <>
        <div className="mt-5 pt-5">
            <br></br>
            <h3 className="titreSections">Les artisans du secteur de l'alimentation</h3>
            <div className="container">
            {artisans.map(artisan => (
                <Link to='#'>
                <Card className="mb-3" style={{ width: '18rem' }}>
                <Card.Body>
                <p>Nom: {artisan.nom}</p>
                <p>Note: <Rating initialValue={artisan.note} allowFraction size={25}/></p>
                <p>Sapécialité: {artisan.specialite}</p>
                <p>Localisation: {artisan.ville}</p>
                </Card.Body>
                </Card>
                </Link>
            
            ))}
        </div>
        </div>
        

    </>

)
}

Chakra3 ui Select slot recipe – indicator color change on trigger state change (focused, hovered, expanded)

i am trying to compose my own chakra 3 ui slot recipe based on the reference slot recipe provided by chakra.

Currently i am trying to achieve the behavior whereby the indicator aka the down chervon icon color turns to “green” or any other color whenever the parent trigger is hovered across or expanded or focused ( i am using storybook to demo these states on render)

i am focusing on the primary variant, looks it the the indicator is not able to infer the parent trigger state. any idea about this? thank you.

heres the current implementation

  variants: {
    variant: {
      primary: {
        trigger: {
          _hover: {
            bg: '{colors.button.surface.secondary.hover}',
            borderColor: '{colors.button.border.secondary.hover}',
            color: '{colors.button.text.secondary.hover}',
      
          },
          _focus: {
            ring: '4px',
            ringColor: '{colors.gray.300}',
            bg: '{colors.button.surface.secondary.focused}',
            color: '{colors.button.text.secondary.focused}',
            borderColor: '{colors.button.border.secondary.focused}',
          },
          _disabled: {
            bg: '{colors.button.surface.secondary.disabled}',
            borderColor: '{colors.button.border.secondary.disabled}',
            color: '{colors.button.text.secondary.disabled}',
          },
          _expanded: {
            bg: '{colors.button.surface.secondary.hover}',
            color: '{colors.button.text.secondary.hover}',
            borderColor: '{colors.button.border.secondary.hover}',
          },
          '&:not([data-placeholder-shown])': {
            borderColor: '{colors.green.800}',
            color: '{colors.gray.800}',
            bg: '{colors.green.50}',
          },
          '&:is(:placeholder-shown, [data-placeholder-shown])': {
            color: '{colors.gray.600}',
          },
          bg: '{colors.button.surface.secondary.default}',
          borderWidth: '1px',
          borderWeight: '1',
          borderColor: '{colors.button.border.secondary.default}',
        },
        indicator: {
          color: '{colors.yellow.800}',
          '[data-hover] &': {
            color: '{colors.green.600}'
          },
        },
    },
      secondary: {
        trigger: {
          _hover: {
            bg: '{colors.button.surface.tertiary.hover}',
            borderColor: '{colors.button.border.tertiary.hover}',
            color: '{colors.button.text.tertiary.hover}',
          },
          _focus: {
            ring: '4px',
            ringColor: '{colors.gray.300}',
            dropShadow: '0 0 0 4px {colors.black}',
            bg: '{colors.button.surface.tertiary.focused}',
            borderColor: '{colors.button.border.tertiary.focused}',
            color: '{colors.button.text.tertiary.focused}',
          },
          _disabled: {
            bg: '{colors.button.surface.secondary.disabled}',
            borderColor: '{colors.button.border.tertiary.disabled}',
            color: '{colors.button.text.tertiary.disabled}',
          },
          _expanded: {
            bg: '{colors.button.surface.tertiary.hover}',
            borderColor: '{colors.button.border.tertiary.hover}',
            color: '{colors.button.text.tertiary.hover}',
          },
          '&:not([data-placeholder-shown])': {
            borderColor: '{colors.green.800}',
            color: '{colors.green.800}',
            bg: '{colors.green.50}',
          },
          '&:is(:placeholder-shown, [data-placeholder-shown])': {
            color: '{colors.gray.600}',
          },
          bg: '{colors.button.surface.tertiary.default}',
          borderWidth: '1px',
          borderWeight: '1',
          borderColor: '{colors.button.border.tertiary.default}',
        },
      },
    },
  },

I’m trying to make my first game in js but I have a problem with the animations that instead of starting the animation the chest just disappear [closed]

Labyrinth Game

*, *::before, *::after {

box-sizing: border-box;

}

/* GAME SCENES */

body {

background-color: #222;

display: flex;

justify-content: center;

align-items: center;

height: 100vh;

width: 100vw;

margin: 0;

font-family: sans-serif;

}

#HealthBar {

background-color: darkred;

height: 25px;

width: 150px;

font-size: 14px;

color: white;

position: absolute;

top: 20px;

left: 20px;

padding: 5px;

text-align: center;

border-radius: 5px;

}

canvas {

border: 40px solid;

border-image-source: url(“ASSETS/BorderWall.png”);

border-image-slice: 40;

border-image-repeat: repeat;

border-image-width: 40px;

background-color: #ddd;

}

/* GAME MENU */

#MenuButton {

position: absolute;

bottom: 50px;

left: 50px;

z-index: 10;

}

#PlayerMenu {

display: none;

grid-template-columns: 1fr 2fr;

grid-template-rows: 1fr 1fr;

position: absolute;

top: 50px;

right: 50px;

width: 300px;

background-color: #444;

color: white;

padding: 10px;

gap: 5px;

border: 2px solid white;

border-radius: 10px;

z-index: 10;

}

#PlayerDisplay {

grid-column: 1;

grid-row: 1;

background-color: #444;

padding: 10px;

border: 2px solid white;

border-radius: 10px;

}

#PlayerDisplay img {

width: 100%;

border: 2px solid;

border-radius: 10px;

}

#ArmourGrid {

grid-column: 1;

grid-row: 2;

display: grid;

grid-template-columns: repeat(2, 1fr);

padding: 5px;

gap: 5px;

border: 2px solid;

border-radius: 10px;

}

#ArmourGrid img {

width: 100%;

border: 2px solid white;

background-color: lightgrey;

border-radius: 10px;

}

#ItemGrid {

grid-column: 2;

grid-row: 1 / 3;

display: grid;

grid-template-columns: repeat(2, 1fr);

padding: 5px;

gap: 5px;

border: 2px solid;

border-radius: 10px;

}

#ItemGrid img {

width: 100%;

border: 2px solid white;

background-color: lightgrey;

border-radius: 10px;

}

/* GAME ELEMENTS */

/* CHEST POP UP */

#ChestOverlay {

position: fixed;

top: 0;

left: 0;

width: 100vw;

height: 100vh;

background: rgba(0, 0, 0, 0.8);

display: flex;

justify-content: center;

align-items: center;

z-index: 9999;

animation: fadeIn 0.5s ease-in-out;

}

#ChestContent {

width: 50vw;

height: 50vh;

background-color: #222;

color: white;

padding: 20px;

border: 4px solid gold;

border-radius: 15px;

text-align: center;

animation: popUp 0.4s ease-out;

}

#ChestContent img {

width: 100px;

margin-bottom: 10px;

}

@keyframes popUp {

0% {

transform: scale(0.2);

opacity: 0;

}

100% {

transform: scale(1);

opacity: 1;

}

}

@keyframes fadeIn {

from {opacity: 0;}

to {opacity: 1;}

}

/* start test movement from mobile */

.MobileButton {

display: grid;

grid-template-columns: repeat(3, 1fr);

height: 100px;

width: 100px;

position: absolute;

bottom: 50px;

right: 50px;

}

.MobileButton Button {

height: 100%;

width: 100%;

background-color: lightgrey;

}

.invisible {

visibility: hidden;

}

/* end test movement from mobile */

  <!-- PRELOAD SPRITES -->

  <!-- GAME SCENES -->

  <!-- INVENTORY MENU -->
<div id="PlayerDisplay"></div>

<div id="ArmourGrid"></div>

<div id="ItemGrid"></div>
  <!-- start test movement from mobile -->

  <!-- end test movement from mobile -->

  

// GET ELEMENTS IN VARIABLE

const canvas = document.getElementById(“game”);

const healthBar = document.getElementById(“HealthBar”);

const menuButton = document.getElementById(“MenuButton”)

const playerMenu = document.getElementById(“PlayerMenu”);

const playerDisplay = document.getElementById(“PlayerDisplay”);

const armourGrid = document.getElementById(“ArmourGrid”);

const itemGrid = document.getElementById(“ItemGrid”);

const wallImg = document.getElementById(“wall”);

const floorImg = document.getElementById(“floor”);

const exitImg = document.getElementById(“exit”);

const playerImg = document.getElementById(“player”);

const itemImg = document.getElementById(“item”);

const trapImg = document.getElementById(“trap”);

const chestImg = document.getElementById(“chest”);

const overlay = document.getElementById(“ChestOverlay”);

// CREATE THE MAZE

const ctx = canvas.getContext(“2d”);

const tileSize = 40;

const maze = [

[‘#’, ‘#’, ‘#’, ‘#’, ‘#’, ‘#’, ‘#’, ‘#’, ‘#’, ‘#’],

[‘#’, ‘P’, ‘ ‘, ‘T’, ‘#’, ‘ ‘, ‘ ‘, ‘ ‘, ‘ ‘, ‘#’],

[‘#’, ‘C’, ‘#’, ‘I’, ‘#’, ‘ ‘, ‘#’, ‘#’, ‘ ‘, ‘#’],

[‘#’, ‘ ‘, ‘#’, ‘ ‘, ‘ ‘, ‘ ‘, ‘#’, ‘ ‘, ‘ ‘, ‘#’],

[‘#’, ‘ ‘, ‘#’, ‘#’, ‘#’, ‘ ‘, ‘#’, ‘ ‘, ‘#’, ‘#’],

[‘#’, ‘ ‘, ‘C’, ‘ ‘, ‘#’, ‘ ‘, ‘ ‘, ‘ ‘, ‘ ‘, ‘#’],

[‘#’, ‘#’, ‘#’, ‘ ‘, ‘#’, ‘#’, ‘#’, ‘#’, ‘ ‘, ‘#’],

[‘#’, ‘ ‘, ‘#’, ‘ ‘, ‘S’, ‘ ‘, ‘ ‘, ‘#’, ‘ ‘, ‘#’],

[‘#’, ‘ ‘, ‘#’, ‘#’, ‘#’, ‘#’, ‘ ‘, ‘#’, ‘E’, ‘#’],

[‘#’, ‘#’, ‘#’, ‘#’, ‘#’, ‘#’, ‘#’, ‘#’, ‘#’, ‘#’],

];

function drawMaze() {

for (let y = 0; y {playerMenu.style.display =

(playerMenu.style.display === “none”) ?

“grid” : “none”;

});

function updatePlayerMenu() {

playerDisplay.innerHTML = ` `;

armourGrid.innerHTML = “”;

currentPlayer.armour?.forEach(armour => { const armourElement = document.createElement(“img”);

armourElement.src = armour.img;

armourElement.alt = “armour”;

armourElement.title = armour.name;

armourGrid.appendChild(armourElement);});

itemGrid.innerHTML = “”;

currentPlayer.item?.forEach((item, index) => {

const itemElement = document.createElement(“img”);

itemElement.src = item.img;

itemElement.alt = “item”;

itemElement.title = item.name;

itemElement.addEventListener(“click”, () => {

if (confirm(`Use or remove “${item.name}”?`)) {removeItemFromInventory(index);

}

});

itemGrid.appendChild(itemElement);

});

// make a bestiary for encountered enemies

}

function addItemToInventory(item) {

currentPlayer.item.push(item);

updatePlayerMenu();

showChestPopup(item);

}

function removeItemFromInventory(index) {

if (index >= 0 && index = trapAnim.frameSpeed) {

trapAnim.totalTime = 0;

trapAnim.currentFrame = trapAnim.active ?

Math.min(trapAnim.currentFrame + 1, trapAnim.totalFrame – 1) :

Math.max(trapAnim.currentFrame – 1, 0);

}

}

//CHEST

const chestAnim = {

totalFrames: 3,

currentFrame: 0,

totalTime: 0,

frameSpeed: 150,

active: false

};

const openedChest = new Set();

function openChest() {

// check what chests have been opened

const openedChestPosition = `${currentPlayer.x},${currentPlayer.y}`;

if (maze[currentPlayer.y][currentPlayer.x] === ‘C’ && !openedChest.has(openedChestPosition)) {

openedChest.add(openedChestPosition);

// play the chest opening audio and animatiom

document.getElementById(“chestaudio”).play();

chestAnim.active = true;

// Pick a random item

const randomItem = allItems[Math.floor(Math.random() * allItems.length)];

alert(“you found me”);

// call fucntion when chest is opened

health(30);

addItemToInventory(randomItem);

// give item in inventory near health bar(later on game menu like botw) and get rid of item posiotion x:3 y:3

}

// if maze player x === S

}

function showChestPopup(item) {

// Clear any previous content instead of removing the entire element

overlay.innerHTML = “”;

overlay.style.display = “flex”;

const content = document.createElement(“div”);

content.id = “ChestContent”;

content.innerHTML = `

${item.name}

${item.description}

(Click anywhere to close)

`;

overlay.appendChild(content);

// Click anywhere to close

overlay.onclick = () => {

overlay.style.display = “none”;

overlay.innerHTML = “”;

};

// Auto-remove after 5 seconds

setTimeout(() => {

overlay.style.display = “none”;

overlay.innerHTML = “”;

}, 5000);

}

function AnimationChest(lastTimeChest) {

chestAnim.totalTime += lastTimeChest;

if (chestAnim.totalTime >= chestAnim.frameSpeed) {

chestAnim.totalTime = 0;

chestAnim.currentFrame = chestAnim.active ?

Math.min(chestAnim.currentFrame + 1, chestAnim.totalFrame – 1) :

Math.max(chestAnim.currentFrame – 1, 0);

}

}

// PLAYER

const basePlayer = {

maxHealth: 100,

health: 100,

armour: [],

item: []

};

let currentPlayer = {

x: 1,

y: 1,

…structuredClone(basePlayer)

};

healthBar.textContent = “HEALTH: ” + currentPlayer.health;

function health(number) {

currentPlayer.health += number;

healthBar.textContent = “HEALTH: ” + currentPlayer.health;

if (currentPlayer.health > currentPlayer.maxHealth) {

currentPlayer.health = currentPlayer.maxHealth;

}

if (currentPlayer.health alert(“You win!”), 100);

}

if (maze[newY][newX] === ‘T’) {

trapAnim.active= true;

health(-20);

alert(“You got hurt!”);

}

}

}

function resetPlayer() {

currentPlayer = {

x: 1,

y: 1,

…structuredClone(basePlayer)

};

}

/*player death

const totalFrameDeath = 3;

let currentFrameDeath = 0;

let totalTimeDeath = 0;

const frameSpeedDeath = 150;

let activeDeath = false;

function Death() {

alert(“YOU DIED”);

resetPlayer();

activeDeath = true;

– death animation

– stop movement

– lose display

}

*/

// ITEMS

const allItems = [

{

name: “Golden Fleece”,

description: “Argonaut’s story”,

img: “ASSETS/SPRITES/GoldenFleece.png”

//give 1 extra life

//if health = 0 then health = maxHealth (only once)

},

{

name: “Potion of Healing”,

description: “Restores 50 health.”,

img: “ASSETS/SPRITES/HealingPotion.png”

}

]

/* {skull = gain 1 throwable skull from skeleton chest and throw it in an arch( use THE trick to make items or enemies jump in 2d games simulating the arch – youtube link with explanations : https://youtu.be/46TL8AEL4yY?si=yqMJ9-BDJeMvc-_G)

– randomize between skull, bones or rocks when looting a skeleton chest }, */

/* { door = {make a closed door and find keys in chest or when defeating enemies. if key then activeDoor = true

},*/

// KEY AND BUTTON

document.addEventListener(“keydown”, (e) => {

switch (e.key) {

case “ArrowUp”: move(0, -1); break;

case “ArrowDown”: move(0, 1); break;

case “ArrowLeft”: move(-1, 0); break;

case “ArrowRight”: move(1, 0); break;

case “e”: case “E”: openChest(); break}

});

/*start test (movement from mobile)*/

document.getElementById(“topcenter”).addEventListener(“click”, () => move(0, -1));

document.getElementById(“bottomcenter”).addEventListener(“click”, () => move(0, 1));

document.getElementById(“centerleft”).addEventListener(“click”, () => move(-1, 0));

document.getElementById(“centerright”).addEventListener(“click”, () => move(1, 0));

/*end test (movement from mobile)*/

document.getElementById(“centercenter”).addEventListener(“click”, () =>

openChest());

// ANIMATIONS LOOP

let startTimeChest= performance.now(); let startTimeTrap = performance.now();

function AnimationLoop(time) {

const lastTimeTrap = time – startTimeTrap; startTimeTrap = time;

AnimationTrap(lastTimeTrap);

const lastTimeChest = time – startTimeChest; startTimeChest = time;

AnimationChest(lastTimeChest);

updatePlayerMenu();

drawMaze();

requestAnimationFrame(AnimationLoop);}

requestAnimationFrame(AnimationLoop);

// TO DO LIST

// gain god’s favour through challenges and sacrifice (example : kill 1000 enemies and Ares will make you stronger, solve 1000 puzzles and will gain Athena’s favour, Hermes will make you faster, Apollo will make your bow stronger) (level up favours)

I was expecting the chest to animate with the frameshhet but it only disappear. I tried to block test but nothing. I’m posting the entire script because I don’t know if the problem is in the CSS, html or JavaScript.

I’m trying to make my first game but I have a problem with the animations that instead of starting the animation the chest just disappear

Labyrinth Game

*, *::before, *::after {

box-sizing: border-box;

}

/* GAME SCENES */

body {

background-color: #222;

display: flex;

justify-content: center;

align-items: center;

height: 100vh;

width: 100vw;

margin: 0;

font-family: sans-serif;

}

#HealthBar {

background-color: darkred;

height: 25px;

width: 150px;

font-size: 14px;

color: white;

position: absolute;

top: 20px;

left: 20px;

padding: 5px;

text-align: center;

border-radius: 5px;

}

canvas {

border: 40px solid;

border-image-source: url(“ASSETS/BorderWall.png”);

border-image-slice: 40;

border-image-repeat: repeat;

border-image-width: 40px;

background-color: #ddd;

}

/* GAME MENU */

#MenuButton {

position: absolute;

bottom: 50px;

left: 50px;

z-index: 10;

}

#PlayerMenu {

display: none;

grid-template-columns: 1fr 2fr;

grid-template-rows: 1fr 1fr;

position: absolute;

top: 50px;

right: 50px;

width: 300px;

background-color: #444;

color: white;

padding: 10px;

gap: 5px;

border: 2px solid white;

border-radius: 10px;

z-index: 10;

}

#PlayerDisplay {

grid-column: 1;

grid-row: 1;

background-color: #444;

padding: 10px;

border: 2px solid white;

border-radius: 10px;

}

#PlayerDisplay img {

width: 100%;

border: 2px solid;

border-radius: 10px;

}

#ArmourGrid {

grid-column: 1;

grid-row: 2;

display: grid;

grid-template-columns: repeat(2, 1fr);

padding: 5px;

gap: 5px;

border: 2px solid;

border-radius: 10px;

}

#ArmourGrid img {

width: 100%;

border: 2px solid white;

background-color: lightgrey;

border-radius: 10px;

}

#ItemGrid {

grid-column: 2;

grid-row: 1 / 3;

display: grid;

grid-template-columns: repeat(2, 1fr);

padding: 5px;

gap: 5px;

border: 2px solid;

border-radius: 10px;

}

#ItemGrid img {

width: 100%;

border: 2px solid white;

background-color: lightgrey;

border-radius: 10px;

}

/* GAME ELEMENTS */

/* CHEST POP UP */

#ChestOverlay {

position: fixed;

top: 0;

left: 0;

width: 100vw;

height: 100vh;

background: rgba(0, 0, 0, 0.8);

display: flex;

justify-content: center;

align-items: center;

z-index: 9999;

animation: fadeIn 0.5s ease-in-out;

}

#ChestContent {

width: 50vw;

height: 50vh;

background-color: #222;

color: white;

padding: 20px;

border: 4px solid gold;

border-radius: 15px;

text-align: center;

animation: popUp 0.4s ease-out;

}

#ChestContent img {

width: 100px;

margin-bottom: 10px;

}

@keyframes popUp {

0% {

transform: scale(0.2);

opacity: 0;

}

100% {

transform: scale(1);

opacity: 1;

}

}

@keyframes fadeIn {

from {opacity: 0;}

to {opacity: 1;}

}

/* start test movement from mobile */

.MobileButton {

display: grid;

grid-template-columns: repeat(3, 1fr);

height: 100px;

width: 100px;

position: absolute;

bottom: 50px;

right: 50px;

}

.MobileButton Button {

height: 100%;

width: 100%;

background-color: lightgrey;

}

.invisible {

visibility: hidden;

}

/* end test movement from mobile */

  <!-- PRELOAD SPRITES -->

  <!-- GAME SCENES -->

  <!-- INVENTORY MENU -->
<div id="PlayerDisplay"></div>

<div id="ArmourGrid"></div>

<div id="ItemGrid"></div>
  <!-- start test movement from mobile -->

  <!-- end test movement from mobile -->

  

// GET ELEMENTS IN VARIABLE

const canvas = document.getElementById(“game”);

const healthBar = document.getElementById(“HealthBar”);

const menuButton = document.getElementById(“MenuButton”)

const playerMenu = document.getElementById(“PlayerMenu”);

const playerDisplay = document.getElementById(“PlayerDisplay”);

const armourGrid = document.getElementById(“ArmourGrid”);

const itemGrid = document.getElementById(“ItemGrid”);

const wallImg = document.getElementById(“wall”);

const floorImg = document.getElementById(“floor”);

const exitImg = document.getElementById(“exit”);

const playerImg = document.getElementById(“player”);

const itemImg = document.getElementById(“item”);

const trapImg = document.getElementById(“trap”);

const chestImg = document.getElementById(“chest”);

const overlay = document.getElementById(“ChestOverlay”);

// CREATE THE MAZE

const ctx = canvas.getContext(“2d”);

const tileSize = 40;

const maze = [

[‘#’, ‘#’, ‘#’, ‘#’, ‘#’, ‘#’, ‘#’, ‘#’, ‘#’, ‘#’],

[‘#’, ‘P’, ‘ ‘, ‘T’, ‘#’, ‘ ‘, ‘ ‘, ‘ ‘, ‘ ‘, ‘#’],

[‘#’, ‘C’, ‘#’, ‘I’, ‘#’, ‘ ‘, ‘#’, ‘#’, ‘ ‘, ‘#’],

[‘#’, ‘ ‘, ‘#’, ‘ ‘, ‘ ‘, ‘ ‘, ‘#’, ‘ ‘, ‘ ‘, ‘#’],

[‘#’, ‘ ‘, ‘#’, ‘#’, ‘#’, ‘ ‘, ‘#’, ‘ ‘, ‘#’, ‘#’],

[‘#’, ‘ ‘, ‘C’, ‘ ‘, ‘#’, ‘ ‘, ‘ ‘, ‘ ‘, ‘ ‘, ‘#’],

[‘#’, ‘#’, ‘#’, ‘ ‘, ‘#’, ‘#’, ‘#’, ‘#’, ‘ ‘, ‘#’],

[‘#’, ‘ ‘, ‘#’, ‘ ‘, ‘S’, ‘ ‘, ‘ ‘, ‘#’, ‘ ‘, ‘#’],

[‘#’, ‘ ‘, ‘#’, ‘#’, ‘#’, ‘#’, ‘ ‘, ‘#’, ‘E’, ‘#’],

[‘#’, ‘#’, ‘#’, ‘#’, ‘#’, ‘#’, ‘#’, ‘#’, ‘#’, ‘#’],

];

function drawMaze() {

for (let y = 0; y {playerMenu.style.display =

(playerMenu.style.display === “none”) ?

“grid” : “none”;

});

function updatePlayerMenu() {

playerDisplay.innerHTML = ` `;

armourGrid.innerHTML = “”;

currentPlayer.armour?.forEach(armour => { const armourElement = document.createElement(“img”);

armourElement.src = armour.img;

armourElement.alt = “armour”;

armourElement.title = armour.name;

armourGrid.appendChild(armourElement);});

itemGrid.innerHTML = “”;

currentPlayer.item?.forEach((item, index) => {

const itemElement = document.createElement(“img”);

itemElement.src = item.img;

itemElement.alt = “item”;

itemElement.title = item.name;

itemElement.addEventListener(“click”, () => {

if (confirm(`Use or remove “${item.name}”?`)) {removeItemFromInventory(index);

}

});

itemGrid.appendChild(itemElement);

});

// make a bestiary for encountered enemies

}

function addItemToInventory(item) {

currentPlayer.item.push(item);

updatePlayerMenu();

showChestPopup(item);

}

function removeItemFromInventory(index) {

if (index >= 0 && index = trapAnim.frameSpeed) {

trapAnim.totalTime = 0;

trapAnim.currentFrame = trapAnim.active ?

Math.min(trapAnim.currentFrame + 1, trapAnim.totalFrame – 1) :

Math.max(trapAnim.currentFrame – 1, 0);

}

}

//CHEST

const chestAnim = {

totalFrames: 3,

currentFrame: 0,

totalTime: 0,

frameSpeed: 150,

active: false

};

const openedChest = new Set();

function openChest() {

// check what chests have been opened

const openedChestPosition = `${currentPlayer.x},${currentPlayer.y}`;

if (maze[currentPlayer.y][currentPlayer.x] === ‘C’ && !openedChest.has(openedChestPosition)) {

openedChest.add(openedChestPosition);

// play the chest opening audio and animatiom

document.getElementById(“chestaudio”).play();

chestAnim.active = true;

// Pick a random item

const randomItem = allItems[Math.floor(Math.random() * allItems.length)];

alert(“you found me”);

// call fucntion when chest is opened

health(30);

addItemToInventory(randomItem);

// give item in inventory near health bar(later on game menu like botw) and get rid of item posiotion x:3 y:3

}

// if maze player x === S

}

function showChestPopup(item) {

// Clear any previous content instead of removing the entire element

overlay.innerHTML = “”;

overlay.style.display = “flex”;

const content = document.createElement(“div”);

content.id = “ChestContent”;

content.innerHTML = `

${item.name}

${item.description}

(Click anywhere to close)

`;

overlay.appendChild(content);

// Click anywhere to close

overlay.onclick = () => {

overlay.style.display = “none”;

overlay.innerHTML = “”;

};

// Auto-remove after 5 seconds

setTimeout(() => {

overlay.style.display = “none”;

overlay.innerHTML = “”;

}, 5000);

}

function AnimationChest(lastTimeChest) {

chestAnim.totalTime += lastTimeChest;

if (chestAnim.totalTime >= chestAnim.frameSpeed) {

chestAnim.totalTime = 0;

chestAnim.currentFrame = chestAnim.active ?

Math.min(chestAnim.currentFrame + 1, chestAnim.totalFrame – 1) :

Math.max(chestAnim.currentFrame – 1, 0);

}

}

// PLAYER

const basePlayer = {

maxHealth: 100,

health: 100,

armour: [],

item: []

};

let currentPlayer = {

x: 1,

y: 1,

…structuredClone(basePlayer)

};

healthBar.textContent = “HEALTH: ” + currentPlayer.health;

function health(number) {

currentPlayer.health += number;

healthBar.textContent = “HEALTH: ” + currentPlayer.health;

if (currentPlayer.health > currentPlayer.maxHealth) {

currentPlayer.health = currentPlayer.maxHealth;

}

if (currentPlayer.health alert(“You win!”), 100);

}

if (maze[newY][newX] === ‘T’) {

trapAnim.active= true;

health(-20);

alert(“You got hurt!”);

}

}

}

function resetPlayer() {

currentPlayer = {

x: 1,

y: 1,

…structuredClone(basePlayer)

};

}

/*player death

const totalFrameDeath = 3;

let currentFrameDeath = 0;

let totalTimeDeath = 0;

const frameSpeedDeath = 150;

let activeDeath = false;

function Death() {

alert(“YOU DIED”);

resetPlayer();

activeDeath = true;

– death animation

– stop movement

– lose display

}

*/

// ITEMS

const allItems = [

{

name: “Golden Fleece”,

description: “Argonaut’s story”,

img: “ASSETS/SPRITES/GoldenFleece.png”

//give 1 extra life

//if health = 0 then health = maxHealth (only once)

},

{

name: “Potion of Healing”,

description: “Restores 50 health.”,

img: “ASSETS/SPRITES/HealingPotion.png”

}

]

/* {skull = gain 1 throwable skull from skeleton chest and throw it in an arch( use THE trick to make items or enemies jump in 2d games simulating the arch – youtube link with explanations : https://youtu.be/46TL8AEL4yY?si=yqMJ9-BDJeMvc-_G)

– randomize between skull, bones or rocks when looting a skeleton chest }, */

/* { door = {make a closed door and find keys in chest or when defeating enemies. if key then activeDoor = true

},*/

// KEY AND BUTTON

document.addEventListener(“keydown”, (e) => {

switch (e.key) {

case “ArrowUp”: move(0, -1); break;

case “ArrowDown”: move(0, 1); break;

case “ArrowLeft”: move(-1, 0); break;

case “ArrowRight”: move(1, 0); break;

case “e”: case “E”: openChest(); break}

});

/*start test (movement from mobile)*/

document.getElementById(“topcenter”).addEventListener(“click”, () => move(0, -1));

document.getElementById(“bottomcenter”).addEventListener(“click”, () => move(0, 1));

document.getElementById(“centerleft”).addEventListener(“click”, () => move(-1, 0));

document.getElementById(“centerright”).addEventListener(“click”, () => move(1, 0));

/*end test (movement from mobile)*/

document.getElementById(“centercenter”).addEventListener(“click”, () =>

openChest());

// ANIMATIONS LOOP

let startTimeChest= performance.now(); let startTimeTrap = performance.now();

function AnimationLoop(time) {

const lastTimeTrap = time – startTimeTrap; startTimeTrap = time;

AnimationTrap(lastTimeTrap);

const lastTimeChest = time – startTimeChest; startTimeChest = time;

AnimationChest(lastTimeChest);

updatePlayerMenu();

drawMaze();

requestAnimationFrame(AnimationLoop);}

requestAnimationFrame(AnimationLoop);

// TO DO LIST

// gain god’s favour through challenges and sacrifice (example : kill 1000 enemies and Ares will make you stronger, solve 1000 puzzles and will gain Athena’s favour, Hermes will make you faster, Apollo will make your bow stronger) (level up favours)

I was expecting the chest to animate with the frameshhet but it only disappear.

How to perform TTS operations with JavaScript?

I was following this demo: https://codepen.io/matt-west/pen/DpmMgE

And it worked with a button to select a voice, but I need the specific voice to be set all the time.


var firstText = "Starch";

var defaultVoiceName = "Microsoft Hazel - English (United Kingdom)";

if (speechSynthesis) {
} else {
    alert("Bad news! Your browser doesn't have the Speech Synthesis API this project requires. Try opening this webpage using the newest version of Google Chrome.");
}

// Create a new instance of SpeechSynthesisUtterance.
var msg = new SpeechSynthesisUtterance();

// Set voice. I'm trying to set needed voice ("Microsoft Hazel - English (United Kingdom)") // here, but it doesn't work. This voice is installed on my machine.
msg.voice = speechSynthesis.getVoices().filter(function(voice) { return voice.name == defaultVoiceName })[0];

// Create a new utterance for the specified text and add it to
// the queue.
function speak(text) {
    // Stop the previous tts
    window.speechSynthesis.cancel();

    // Set the text.
    msg.text = text;

    // Queue this utterance.
    window.speechSynthesis.speak(msg);
}

speak(firstText);

What am I doing wrong here? Maybe there is a better way to tts something? I tried the “ResponsiveVoice” library, but it works with a delay.

About calling multiple APIs while catching user input events in React

I am developing a React application as a beginner, when the user types in the search box, I need to send a request (API call) to get suggested results. However, if I call the API immediately every time the user types each character, it will be resource-consuming and have a bad experience and have to load many times.

I tried delaying it by 500ms, but the result still calls many APIs

function bindInputWithDelay(selector, callback) {
  const inputEl = document.querySelector(selector);
  if (!inputEl) return;

  inputEl.addEventListener('input', (event) => {
    const value = event.target.value;
    setTimeout(() => {
      callback(value);
    }, 500);
  });
}

Please help me write a function that will wait for the user to finish typing and then send the API only once, the waiting time can be 500ms.

If it’s a custom React Hook, that’s even better.

Thanks.

About calling multiple APIs while catching user input events in React (beginner)

I am developing a React application as a beginner, when the user types in the search box, I need to send a request (API call) to get suggested results. However, if I call the API immediately every time the user types each character, it will be resource-consuming and have a bad experience and have to load many times.

I tried delaying it by 500ms, but the result still calls many APIs

function bindInputWithDelay(selector, callback) {
  const inputEl = document.querySelector(selector);
  if (!inputEl) return;

  inputEl.addEventListener('input', (event) => {
    const value = event.target.value;
    setTimeout(() => {
      callback(value);
    }, 500);
  });
}

Please help me write a function that will wait for the user to finish typing and then send the API only once, the waiting time can be 500ms.

If it’s a custom React Hook, that’s even better.

Thanks.

issues on mobile with overflow:hidden and splide slider

I’m using splide on my webflow site to create a services slider. on desktop it works great and in the mobile preview on desktop, it also works. the problem starts when i try and view it on my phone, the content on each slide glitches in and out during each transition. I had the whole section that holds the splide slider set to overflow:hidden so it doesnt get the horizontal scroll bar, when i turned that off, it was working normally on mobile again.

this is my webflow link: https://platinum-medical.webflow.io

I tried making the whole body overflow:hidden but that still broke it, i also tried setting the splide__track to a specific width but i still get the horizontal scroll issue.

here is my code (CSS & JS):

<link rel="stylesheet" type="text/css" href="https://cdn.jsdelivr.net/npm/@splidejs/[email protected]/dist/css/splide-core.min.css">
<style>
.splide__track {
  overflow: visible;
}
</style>

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

function slider1() {

let splides = $('.slider_model');
for ( let i = 0, splideLength = splides.length; i < splideLength; i++ ) {
    new Splide( splides[ i ], {
  // Desktop on down
    autoplay: true,
  interval: 3000,
  perPage: 3,
    perMove: 1,
  focus: 'center', // 0 = left and 'center' = center
  type: 'loop', // 'loop' or 'slide'
  gap: '.5em', // space between slides
  arrows: 'false', // 'slider' or false
  pagination: 'false', // 'slider' or false
  speed : 1000, // transition speed in miliseconds
  dragAngleThreshold: 30, // default is 30
  autoWidth: false, // for cards with differing widths
  rewind : false, // go back to beginning when reach end
  rewindSpeed : 400,
  waitForTransition : true,
  updateOnMove : true,
  trimSpace: false, // true removes empty space from end of list
  breakpoints: {
        991: {
    perPage: 2,
        // Tablet
        },
    767: {
        // Mobile Landscape
      perPage: 2,
      
        },
    479: {
        // Mobile Portrait
      perPage: 1,
        }
    }
} ).mount();
}

}
slider1();

</script>

This is the animation css for the active slide

<style>

.splide__slide.is-active .service_slider-content{
opacity: 100%;
transform: translate(0%);
}

.splide__slide.is-active .slider_mood{
transform:scale(1);
}

</style>

What I would like my site to look like, without the horizontal scrolling

screen recording of the glitch on my phone -> https://drive.google.com/file/d/1Gr0oznD2Q6qs3X1cblGvKSVnPAuVPKkK/view?usp=sharing

I swear i googled and searched the splide docs before making this post, please, if the answer was obvious i would love to be humbled :’)