简体   繁体   中英

endless while loop after mousemove

I am going crazy here. I want to show an element on mouse move, and hide it 10 sec after last move of the mouse.

I wrote this:

document.addEventListener("DOMContentLoaded", function(event) {
  var time = 0;
  document.addEventListener("mousemove", function(event) {
    console.log('$');
    document.getElementsByClassName("mybar")[0].style.visibility = 'visible';
    time = 0;
    while (time < 11) {
      setTimeout(function() {
        time++
      }, 1000);
      console.log(time, time == 10);
      if (time == 10) {
        document.getElementsByClassName("mybar")[0].style.visibility = 'hidden';
      }
    }
  });
});
<div class='mybar'>
  <h1> TESTING </h1>
</div>

Why does it end up in an endless loop? Why doesn't it exit on condition? why does the if never gets the 'true' parameter? Notice : don't run it this way... it will kill your tab.

First, you don't need to wait for DOMContentLoaded to add an event listener to document , since if you did, you couldn't add DOMContentLoaded in the first place.

The infinite loop is because setTimeout doesn't pause the script. It schedules its callback for the time you provide, and irrespective of that time, the callbacks will not run until the current running code in the thread completes, which never happens because you don't increment the time variable.

So the loop never ends, and so the thread is never made available, so your callbacks never can run, so time can never be incremented.

Lastly, starting a setTimeout inside an event handler that shares a local variable and executes very rapidly on an event like mousemove is prone to give unexpected results. For example, in your code, every time the handler runs, it'll reset time to 0 , which doesn't seem to be what you'd want.


A solution would be to ditch the loop, schedule the visibility for 10 seconds, and prevent the main part of the code in the handler from running in the meantime by using a boolean variable.

var timer = null;
document.addEventListener("mousemove", function(event) {
    var myBar = document.querySelector(".mybar");
    if (!myBar) {
      return; // there's no mybar element
    }


    if (timer == null) {
      myBar.style.visibility = 'visible';
    } else {
      clearTimeout(timer); // clear the currently running timer
    }

    // set to hidden in 10 seconds
    timer = setTimeout(function() {
      myBar.style.visibility = 'hidden';
      timer = null; // clear the timer
    }, 10000);
});

I also switched to querySelector instead of getElementsByClassName because it's shorter and cleaner. And I used a variable to make sure the element is found before setting the style.

You need a flag out of the mousemove scope that tells your listener that you've already ran.

if(running) return; 
running = true;

In context:

document.addEventListener("DOMContentLoaded", function(event) {
  var time = 0;
  var running = false;
  document.addEventListener("mousemove", function(event) {

    console.log('$');

    if(running) return;
    running = true;

    document.getElementsByClassName("mybar")[0].style.visibility = 'visible';
    time = 0;
    while (time < 11) {
      setTimeout(function() {
        time++
      }, 1000);
      console.log(time, time == 10);
      if (time == 10) {
        document.getElementsByClassName("mybar")[0].style.visibility = 'hidden';
      }
    }
  });
});

Here's a way to do it with regular JavaScript. If your browser isnt ES6 compliant, you can replace the arrow functions with regular function expressions. The example hides the text after 2 seconds instead of 10, just so you can see it work without having to waste 8 extra seconds.

 //hide by default document.getElementById('myBar').style.display = 'none'; var timer = null; var hideDivTimer = () => { timer = setTimeout(() => { document.getElementById('myBar').style.display = 'none'; }, 2000); }; document.addEventListener('mousemove', () => { clearTimeout(timer); document.getElementById('myBar').style.display = 'inline'; hideDivTimer(); }); 
 <body> <div id='myBar'> <h1> TESTING </h1> </div> </body> 

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM