250

I'm using JQuery as such:

$(window).resize(function() { ... });

However, it appears that if the person manually resizes their browser windows by dragging the window edge to make it larger/smaller, the .resize event above fires multiple times.

Question: How to I call a function AFTER the browser window resize completed (so that the event only fires once)?

armstrhb
  • 4,054
  • 3
  • 20
  • 28
sarah
  • 2,501
  • 3
  • 15
  • 3
  • I don't know if that's possible, you could try this plugin though http://benalman.com/projects/jquery-resize-plugin/ – BrunoLM May 18 '10 at 03:47
  • By the way, I noticed this is very useful in mobile browsers that tend to gradually hide the address bar when the user scrolls down, therefore resizing the screen. I also expected screen resize to happen only on the desktop... – MakisH Sep 21 '16 at 22:23
  • See this answer: https://stackoverflow.com/questions/667426/javascript-resize-event-firing-multiple-times-while-dragging-the-resize-handle/668185#668185 It involves the use of timeouts to delay the execution of your function. – Alastair Pitts May 18 '10 at 03:42

13 Answers13

313

Here's a modification of CMS's solution that can be called in multiple places in your code:

var waitForFinalEvent = (function () {
  var timers = {};
  return function (callback, ms, uniqueId) {
    if (!uniqueId) {
      uniqueId = "Don't call this twice without a uniqueId";
    }
    if (timers[uniqueId]) {
      clearTimeout (timers[uniqueId]);
    }
    timers[uniqueId] = setTimeout(callback, ms);
  };
})();

Usage:

$(window).resize(function () {
    waitForFinalEvent(function(){
      alert('Resize...');
      //...
    }, 500, "some unique string");
});

CMS's solution is fine if you only call it once, but if you call it multiple times, e.g. if different parts of your code set up separate callbacks to window resizing, then it will fail b/c they share the timer variable.

With this modification, you supply a unique id for each callback, and those unique IDs are used to keep all the timeout events separate.

brahn
  • 12,096
  • 11
  • 39
  • 49
  • 2
    I <3 u, I combined this while resizing fullscreen (non html5) Highcharts graphs and works great. – Michael J. Calkins May 06 '13 at 06:51
  • Absolutely incredible! – Starkers Mar 10 '14 at 10:26
  • 2
    Since I've benefited so much from your answer I thought I shared a working prototype of your provided code for the rest of the community: https://jsfiddle.net/h6h2pzpu/3/. Enjoy everyone! – Jonas Stawski Aug 27 '15 at 19:55
  • 1
    This is all perfect, BUT it delays the execution of the actual function by the amount of miliseconds (ms), which may be VERY UNDESIRABLE. – Tomas M Sep 02 '15 at 14:33
  • This just does it perfectl is there a way of grabbing the last resize and not having to relay on those milliseconds? Anyways this just saved my day :') Thanks. – Leo Aug 05 '16 at 04:02
  • Here is a way simpler solutions: http://stackoverflow.com/questions/4298612/jquery-how-to-call-resize-event-only-once-its-finished-resizing – AlbertSamuel Mar 23 '17 at 02:59
  • This is the same as this one - https://codepen.io/galingong/pen/qpzrbv - except `timers` is not global right? – galingong Jan 25 '18 at 10:02
  • I was having trouble trying to create responsive bounds for a web game but was having random sizing issues. This solved it. I think the code was reading bounds on the first of several calls, and those dimensions didn't match those of the last call. In short, THANKS! – Chris Sprague May 13 '18 at 15:36
141

I prefer to create an event:

$(window).bind('resizeEnd', function() {
    //do something, window hasn't changed size in 500ms
});

Here is how you create it:

 $(window).resize(function() {
        if(this.resizeTO) clearTimeout(this.resizeTO);
        this.resizeTO = setTimeout(function() {
            $(this).trigger('resizeEnd');
        }, 500);
    });

You could have this in a global javascript file somewhere.

Carlos Martinez T
  • 6,458
  • 1
  • 34
  • 40
  • I used this solution. But in long run I would like to implement same as brahn's solution. – Bharat Patil Feb 11 '14 at 06:51
  • this allows you to bind to that event all over the place, not just in one function, assuming you have multiple pages / .js files requiring separate reactions to it of course – hanzolo Apr 08 '14 at 00:21
  • This didn't work for me but DusanV answer did the job – lightbyte May 19 '17 at 09:39
123

I use the following function for delaying repeated actions, it will work for your case:

var delay = (function(){
  var timer = 0;
  return function(callback, ms){
    clearTimeout (timer);
    timer = setTimeout(callback, ms);
  };
})();

Usage:

$(window).resize(function() {
    delay(function(){
      alert('Resize...');
      //...
    }, 500);
});

The callback function passed to it, will execute only when the last call to delay has been made after the specified amount of time, otherwise a timer will be reset, I find this useful for other purposes like detecting when the user stopped typing, etc...

Christian C. Salvadó
  • 807,428
  • 183
  • 922
  • 838
  • 6
    I think this approach may fail if used multiple times (e.g. if different parts of the code setup callbacks to `$(window).resize`) because they will all share the `timer` variable. See my answer below for proposed solution. – brahn Dec 27 '10 at 21:21
  • Very nice! Works like a charm! Like your answers... simple and elegant! – Mr. Pumpkin Aug 13 '14 at 20:51
74

If you have Underscore.js installed, you could:

$(window).resize(_.debounce(function(){
    alert("Resized");
},500));
JT.
  • 1,062
  • 8
  • 6
24

Some of the previously mentioned solutions did not work for me, even though they are of more general usage. Alternatively I've found this one that did the job on window resize:

$(window).bind('resize', function(e){
    window.resizeEvt;
    $(window).resize(function(){
        clearTimeout(window.resizeEvt);
        window.resizeEvt = setTimeout(function(){
        //code to do after window is resized
        }, 250);
    });
});
DusanV
  • 471
  • 4
  • 9
  • 1
    Only your example worked for me...the others above did not! I am not sure why your answer wasn't selected. – Mumbo Jumbo Jun 09 '15 at 21:03
  • I don't understand why to catch the resize event inside the resize event, but it works for me too... – lightbyte May 19 '17 at 09:37
  • Mumbo Jumbo, I think because others focus on generalization, looking for solution to any such issue (multiple calls of a function). – DusanV May 20 '17 at 10:05
  • lightbyte, it's because you have to stop previous events each time the function is called. – DusanV May 20 '17 at 10:06
13

Many thanks to David Walsh, here is a vanilla version of underscore debounce.

Code:

// 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);
    };
};

Simple usage:

var myEfficientFn = debounce(function() {
    // All the taxing stuff you do
}, 250);

$(window).on('resize', myEfficientFn);

Ref: http://davidwalsh.name/javascript-debounce-function

Irvin Dominin
  • 30,819
  • 9
  • 77
  • 111
6

Actually, as I know, you can't do some actions exactly when resize is off, simply because you don't know future user's actions. But you can assume the time passed between two resize events, so if you wait a little more than this time and no resize is made, you can call your function.
Idea is that we use setTimeout and it's id in order to save or delete it. For example we know that time between two resize events is 500ms, therefore we will wait 750ms.

var a;
$(window).resize(function(){
  clearTimeout(a);
  a = setTimeout(function(){
    // call your function
  },750);
});
Volker E.
  • 5,911
  • 11
  • 47
  • 64
newint
  • 61
  • 1
  • 1
6

It works for me. See this solution - https://alvarotrigo.com/blog/firing-resize-event-only-once-when-resizing-is-finished/

var resizeId;
$(window).resize(function() {
    clearTimeout(resizeId);
    resizeId = setTimeout(doneResizing, 500);
});
function doneResizing(){
    //whatever we want to do 
}
fremail
  • 330
  • 4
  • 8
vishwajit76
  • 2,300
  • 20
  • 16
6

Declare globally delayed listener:

var resize_timeout;

$(window).on('resize orientationchange', function(){
    clearTimeout(resize_timeout);

    resize_timeout = setTimeout(function(){
        $(window).trigger('resized');
    }, 250);
});

And below use listeners to resized event as you want:

$(window).on('resized', function(){
    console.log('resized');
});
gzzz
  • 349
  • 3
  • 7
  • Thanks, that is the best solution currently. Also I have tested that 250ms is working the best way when resizing the window. – AlexioVay Mar 06 '19 at 08:17
2

Simple jQuery plugin for delayed window resize event.

SYNTAX:

Add new function to resize event

jQuery(window).resizeDelayed( func, delay, id ); // delay and id are optional

Remove the function(by declaring its ID) added earlier

jQuery(window).resizeDelayed( false, id );

Remove all functions

jQuery(window).resizeDelayed( false );

USAGE:

// ADD SOME FUNCTIONS TO RESIZE EVENT
jQuery(window).resizeDelayed( function(){ console.log( 'first event - should run after 0.4 seconds'); }, 400,  'id-first-event' );
jQuery(window).resizeDelayed( function(){ console.log('second event - should run after 1.5 seconds'); }, 1500, 'id-second-event' );
jQuery(window).resizeDelayed( function(){ console.log( 'third event - should run after 3.0 seconds'); }, 3000, 'id-third-event' );

// LETS DELETE THE SECOND ONE
jQuery(window).resizeDelayed( false, 'id-second-event' );

// LETS ADD ONE WITH AUTOGENERATED ID(THIS COULDNT BE DELETED LATER) AND DEFAULT TIMEOUT (500ms)
jQuery(window).resizeDelayed( function(){ console.log('newest event - should run after 0.5 second'); } );

// LETS CALL RESIZE EVENT MANUALLY MULTIPLE TIMES (OR YOU CAN RESIZE YOUR BROWSER WINDOW) TO SEE WHAT WILL HAPPEN
jQuery(window).resize().resize().resize().resize().resize().resize().resize();

USAGE OUTPUT:

first event - should run after 0.4 seconds
newest event - should run after 0.5 second
third event - should run after 3.0 seconds

PLUGIN:

jQuery.fn.resizeDelayed = (function(){

    // >>> THIS PART RUNS ONLY ONCE - RIGHT NOW

    var rd_funcs = [], rd_counter = 1, foreachResizeFunction = function( func ){ for( var index in rd_funcs ) { func(index); } };

    // REGISTER JQUERY RESIZE EVENT HANDLER
    jQuery(window).resize(function() {

        // SET/RESET TIMEOUT ON EACH REGISTERED FUNCTION
        foreachResizeFunction(function(index){

            // IF THIS FUNCTION IS MANUALLY DISABLED ( by calling jQuery(window).resizeDelayed(false, 'id') ),
            // THEN JUST CONTINUE TO NEXT ONE
            if( rd_funcs[index] === false )
                return; // CONTINUE;

            // IF setTimeout IS ALREADY SET, THAT MEANS THAT WE SHOULD RESET IT BECAUSE ITS CALLED BEFORE DURATION TIME EXPIRES
            if( rd_funcs[index].timeout !== false )
                clearTimeout( rd_funcs[index].timeout );

            // SET NEW TIMEOUT BY RESPECTING DURATION TIME
            rd_funcs[index].timeout = setTimeout( rd_funcs[index].func, rd_funcs[index].delay );

        });

    });

    // <<< THIS PART RUNS ONLY ONCE - RIGHT NOW

    // RETURN THE FUNCTION WHICH JQUERY SHOULD USE WHEN jQuery(window).resizeDelayed(...) IS CALLED
    return function( func_or_false, delay_or_id, id ){

        // FIRST PARAM SHOULD BE SET!
        if( typeof func_or_false == "undefined" ){

            console.log( 'jQuery(window).resizeDelayed(...) REQUIRES AT LEAST 1 PARAMETER!' );
            return this; // RETURN JQUERY OBJECT

        }

        // SHOULD WE DELETE THE EXISTING FUNCTION(S) INSTEAD OF CREATING A NEW ONE?
        if( func_or_false == false ){

            // DELETE ALL REGISTERED FUNCTIONS?
            if( typeof delay_or_id == "undefined" ){

                // CLEAR ALL setTimeout's FIRST
                foreachResizeFunction(function(index){

                    if( typeof rd_funcs[index] != "undefined" && rd_funcs[index].timeout !== false )
                        clearTimeout( rd_funcs[index].timeout );

                });

                rd_funcs = [];

                return this; // RETURN JQUERY OBJECT

            }
            // DELETE ONLY THE FUNCTION WITH SPECIFIC ID?
            else if( typeof rd_funcs[delay_or_id] != "undefined" ){

                // CLEAR setTimeout FIRST
                if( rd_funcs[delay_or_id].timeout !== false )
                    clearTimeout( rd_funcs[delay_or_id].timeout );

                rd_funcs[delay_or_id] = false;

                return this; // RETURN JQUERY OBJECT

            }

        }

        // NOW, FIRST PARAM MUST BE THE FUNCTION
        if( typeof func_or_false != "function" )
            return this; // RETURN JQUERY OBJECT

        // SET THE DEFAULT DELAY TIME IF ITS NOT ALREADY SET
        if( typeof delay_or_id == "undefined" || isNaN(delay_or_id) )
            delay_or_id = 500;

        // SET THE DEFAULT ID IF ITS NOT ALREADY SET
        if( typeof id == "undefined" )
            id = rd_counter;

        // ADD NEW FUNCTION TO RESIZE EVENT
        rd_funcs[id] = {
            func : func_or_false,
            delay: delay_or_id,
            timeout : false
        };

        rd_counter++;

        return this; // RETURN JQUERY OBJECT

    }

})();
Dejan
  • 794
  • 9
  • 9
1

Assuming that the mouse cursor should return to the document after window resize, we can create a callback-like behavior with onmouseover event. Don't forget that this solution may not work for touch-enabled screens as expected.

var resizeTimer;
var resized = false;
$(window).resize(function() {
   clearTimeout(resizeTimer);
   resizeTimer = setTimeout(function() {
       if(!resized) {
           resized = true;
           $(document).mouseover(function() {
               resized = false;
               // do something here
               $(this).unbind("mouseover");
           })
       }
    }, 500);
});
onur
  • 395
  • 1
  • 3
  • 8
  • Fine for mouse-based desktop-only sites but the mouseover event will never happen if, for example, the user is on a mobile and changes from landscape to portrait, or if they resized the window on a touchscreen desktop. – user56reinstatemonica8 Mar 04 '13 at 13:04
  • You can resize browser window with the help of keyboard on latest windows oses like Win-Arrow-Left Win-Arrow-Right, etc... – Lu4 Oct 21 '15 at 21:14
0

This is what i've implemented:

$(window).resize(function(){ setTimeout(someFunction, 500); });

we can clear the setTimeout if we expect resize to happen less than 500ms

Good Luck...

user697473
  • 2,165
  • 1
  • 20
  • 47
Aakash
  • 21,375
  • 7
  • 100
  • 81
0

Many Solutions. I tried to do the event after a mouuseevent so I addded the reload just after ouse enters the window:

jQuery(window).resize(function() {
        // this. is window
        if(  this.resizeTO) {
            clearTimeout(this.resizeTO) 
        };
    
         this.resizeTO = setTimeout(function() {
            
                jQuery(window).mouseenter(function() {
                    if( jQuery(window).width() < 700 &&   jQuery(window).width() > 400 ){
                        console.log("mouseenter  reloads elements"); 
                        // is loading the page  
                        location.reload();
                        //
                     }; // just mobile
                }); // mouse  fired    
        }, 400);  // set Time Ouuut
});