8

How do you lower the frequency of Javascript event polling? The events I'm concerned about are onResize and onScroll. These events may be triggered dozens of times per second when someone resizes their browser or scrolls down, respectively. I'd like these events to happen only once every 500 ms so I don't have to spend hours optimizing my event handlers and making sure they don't leak memory.

Brian Tompsett - 汤莱恩
  • 5,753
  • 72
  • 57
  • 129
JoJo
  • 19,587
  • 34
  • 106
  • 162

6 Answers6

11
var resizeTimeout;

window.onresize = function() {
    if (resizeTimeout) {
        clearTimeout(resizeTimeout);
    }
    resizeTimeout = setTimeout(function() {
        // Do it!
    }, 500);

});

This will trigger the setTimeout() function ~500ms after the person has finished resizing.

The onscroll version is very similar :)

alex
  • 479,566
  • 201
  • 878
  • 984
  • 3
    this one will not fire every 500 ms, it will only fire 500 ms after you stop/pause resizing – Andrey Dec 07 '10 at 02:09
  • +1 beat me to it. Remember to include a check for `resizeTimeout` since it'll be `undefined` the first time round. – casablanca Dec 07 '10 at 02:10
  • @Andrey Yeah, I know, I mentioned that. It may still be suitable for the OP. – alex Dec 07 '10 at 02:15
  • @alex, calling a handler after the resize pauses/stops is different, though, than calling a handler "live" but at a less zealous pace than the browser tends to fire the event. – eyelidlessness Dec 07 '10 at 03:37
5

You can't really control how frequently the event fires, you can do something like remember the time of first event firing, then on each consequent one you check if it's more than 500 ms from first one - if yes, you proceed with the event handler, otherwise you just exit the event hanlder

Andrey
  • 20,487
  • 26
  • 108
  • 176
  • +1 for this answer then, this is probably what the OP wants. However, you need some additional logic to handle the last resize event. – casablanca Dec 07 '10 at 02:13
  • 1
    I think the timer solution will take care of the last resize event, so the real solution would be some combination of the two – Andrey Dec 07 '10 at 02:14
1

check the underscore debounce function

Creates and returns a new debounced version of the passed function that will postpone its execution until after wait milliseconds have elapsed since the last time it was invoked. Useful for implementing behavior that should only happen after the input has stopped arriving. For example: rendering a preview of a Markdown comment, recalculating a layout after the window has stopped being resized, and so on.

Example:

window.onscroll = _.debounce(
  function() {
      // do something
  }, 500, false
);
Ping Yin
  • 31
  • 2
1

At the beginning of your handler, check to see if 500ms have passed since the last one, and just return if not.

Lou Franco
  • 87,846
  • 14
  • 132
  • 192
1

You can't prevent these events from firing. They always do. What you want to do is stop listening immediately, then handle the event to avoid repetition. Then the entire handler is set up again after setTimeout. No more recursion happens unless somebody resizes the window. I use 5000ms here as it's easier to see it working in the console. You shouldn't see more than one spam in the FF console every 5 seconds even if you resize like a spaz.

(function staggerListen(){
  window.onresize = function(){
    window.onresize = false;
    console.log('spam');
    setTimeout(staggerListen,5000);
  };
})()

Using logic to decide whether to do anything every time the handler fires is still technically firing a handler and an if statement + lookup. That can get heavy.

Erik Reppen
  • 4,605
  • 1
  • 22
  • 26
  • Wow. I'm not sure what the overhead is for dynamically adding/removing the listener callback, but +1 for being tricky, anyhow. :) – Phrogz Dec 07 '10 at 04:09
0

I used to make it like on the accepted answer but the problem is, it only triggers after the timeout specified. I wanted a solution that handles the resize right away, the first time. Here is what I ended up doing.

var _resize_is_busy = false;
var _resize_scheduled = false;
var _resize_precision = 100;

// This register for window resize events. No need to change anything.
$(window).resize(function () {

    if (!_resize_is_busy) {

        // call the scheduler who will do the work and set a timer to
        // check of other resizes occured within a certain period of time

        _resize_scheduler();
    }
    else {

        // the resizer is busy, i.e. a resize have been handled a little
        // time ago and then the scheduler is waiting some time before 
        // handling any other resize. This flag tells the scheduler that
        // a resize event have been receive while he was sleeping.

        _resize_scheduled = true;
    }
});

// This is the scheduler. No need to change anything.
var _resize_scheduler = function () {

    _resize_is_busy = true;
    _resize_scheduled = false;

    setTimeout(function () {

        _resize_is_busy = false;

        if (_resize_scheduled) 
            _handle_resize();

    }, _resize_precision);

    _handle_resize();
}

var _handle_resize = function () {

    console.log('DOING ACTUAL RESIZE');

    // do the work here
    //...
}

I hope this will help.

Djibril NDIAYE
  • 216
  • 2
  • 9