jQuery: Fire mousemove events less often

12,944

Solution 1

Your code is fine except that you should clear the timeout before setting it to null or it might leak:

window.clearTimeout(mousemove_timeout);
mousemove_timeout = null;

As an alternative you could use mousemove/mousestop in conjunction with window.setInterval

var timer = null;
var isIntervalSet = false;

$('body').mousemove(function() {
    if (isIntervalSet) {
        return;
    }
    timer = window.setInterval(function() {
        /*
        * Run my code...
        */    
    }, 250);
    isIntervalSet = true;
}).mousestop(function() {
    isIntervalSet = false;
    window.clearTimeout(timer);
    timer = null;
});

Solution 2

After I tried the solution in the accepted answer, I found out that if the mouse keep moving constantly, especially in circular motion, mousemove() event is fired continuously, but the mouse coordinates remain the same. So I came up with a simpler solution which eliminates mousestop() and setTimeout.

$("body").mousemove(function (e) {
        if (enableHandler) {
            handleMouseMove(e);
            enableHandler = false;
        }
});

timer = window.setInterval(function(){
    enableHandler = true;
}, 100);

This will correctly call handleMouseMove() approximately every 100ms. (Note that I said approximately because time delays and intervals in JavaScript is not real-time guaranteed)

Solution 3

A solution and a question^^

What about this approach without a global var. Is that a suitable solution?

$(function() {
    $("#foo").mousemove((function() {
        var timer = null;

        return function() {
            if (timer !== null) {
                window.clearTimeout(timer);
            }
            timer = window.setTimeout(foo, 250);
        };
    })());
});

function foo() {
    //...
}

Solution 4

A simple way of gettin the mouse position in a custom period of miliseconds

var timer;
var refresh_time = 50;
var x = 0;
jQuery('body').mousemove(function(evt) {
  if (timer)
    clearTimeout(timer);
    timer = setTimeout(function(){
      var mouse_x = evt.clientX;
      if(mouse_x != x){
        x = mouse_x;
        console.log('mouse is on a new x position' + x);    
      }
    }, refresh_time);        
})

Solution 5

You seek code Throttling / Debouncing.

http://benalman.com/projects/jquery-throttle-debounce-plugin/ http://drupalmotion.com/article/debounce-and-throttle-visual-explanation

Sample from underscore.jS

// Returns a function, that, as long as it continues to be invoked, will not
// be triggered. The function will be called after it stops being called for
// N milliseconds. If `immediate` is passed, trigger the function on the
// leading edge, instead of the trailing.
function debounce(func, wait, immediate) {
    var timeout;
    return function() {
        var context = this, args = arguments;
        var later = function() {
            timeout = null;
            if (!immediate) func.apply(context, args);
        };
        var callNow = immediate && !timeout;
        clearTimeout(timeout);
        timeout = setTimeout(later, wait);
        if (callNow) func.apply(context, args);
    };
};
Share:
12,944
Topher Fangio
Author by

Topher Fangio

Founder/CEO of Profoundry LLC in Abilene, Texas. Core team member of Angular Material. Building awesome things with Angular, Ruby on Rails, PostgreSQL, and vim (the best damn editor on the planet). Check out my mad skillz!

Updated on July 27, 2022

Comments

  • Topher Fangio
    Topher Fangio almost 2 years

    I'm trying to figure out a clean way to aggregate mousemove events so that I ensure my code gets called, but only once every 250-300 milliseconds.

    I've thought about using something like the following, but was wondering if there was a better pattern, or something jQuery provides that will do the same thing:

    var mousemove_timeout = null;
    
    $('body').mousemove(function() {
      if (mousemove_timeout == null) {
        mousemove_timeout = window.setTimeout(myFunction, 250);
      }
    });
    
    function myFunction() {
      /*
       * Run my code...
       */
    
      mousemove_timeout = null;
    }
    

    EDIT: The accepted answer below would work perfectly for this situation, however, I found that the mousestop() functionality provided in the answer actually eliminated my need for the aggregation, so if you're reading this question and looking for an answer, see if the mousestop plugin is what you really need!

  • Topher Fangio
    Topher Fangio over 13 years
    +1 - Thanks for the tip! Is this the best/cleanest way though? Seems like a bit of a hack...
  • Darin Dimitrov
    Darin Dimitrov over 13 years
    Yes indeed it is a bit of a hack, window.setInterval would be more adapted to this scenario.
  • Topher Fangio
    Topher Fangio over 13 years
    Accepted! - Turns out, the mousestop function is exactly what I need and will eliminate the timeout code entirely. Thanks so much!
  • Ethan
    Ethan over 12 years
    Something's not right with this approach: if the mouse keep moving constantly, mousemove event keep getting fired, but the mouse coordinates remain the same.
  • Topher Fangio
    Topher Fangio over 12 years
    +1 - Thanks! This is actually the type of solution that I was looking for originally. This is a much cleaner approach than the accepted answer, but, the mousestop plugin is still what I went with.
  • Topher Fangio
    Topher Fangio almost 11 years
    +1 - I definitely like not using a global variable. Although I haven't tried this code, it does look fairly clean and intuitive.
  • Mokhlesur Rahman
    Mokhlesur Rahman over 9 years
    Is there a way to stop the timer when mouse is not moving?