JQuery: come chiamare l'evento RESIZE solo una volta che è stato FINITO il ridimensionamento?


109

Come posso chiamare una funzione una volta che le finestre del browser hanno FINITO il ridimensionamento?

Sto provando a farlo in questo modo, ma ho problemi. Sto usando la funzione evento JQuery Resize:

$(window).resize(function() {
  ... // how to call only once the browser has FINISHED resizing?
});

Tuttavia, questa funzione viene chiamata continuamente se l'utente ridimensiona manualmente la finestra del browser. Ciò significa che potrebbe chiamare questa funzione dozzine di volte in un breve intervallo di tempo.

Come posso chiamare la funzione di ridimensionamento solo una volta (una volta terminato il ridimensionamento della finestra del browser)?

AGGIORNARE

Anche senza dover utilizzare una variabile globale.


@BGerrissen, se puoi mostrarmi come fare jsfiddle.net/Zevan/c9UE5/1 senza una variabile globale, lo farò sicuramente :)
nickb

il metodo cleartimeout / settimeout sopra funziona meravigliosamente bene.
kris-o3

Risposte:


129

Ecco un esempio utilizzando le istruzioni di thejh

Puoi memorizzare un ID di riferimento in qualsiasi setInterval o setTimeout. Come questo:

var loop = setInterval(func, 30);

// some time later clear the interval
clearInterval(loop);

Mi piace che questo codice sia così semplice. Qualche modo per farlo funzionare senza dover utilizzare una variabile globale?
nickb

Se vedo come farlo senza la variabile globale, farò che la "risposta accettata"
nickb

nessun problema. Ci sono alcuni modi. Ho modificato la risposta per riflettere quella più semplice a cui potessi pensare.
Zevan

grazie per l'aggiornamento ma sembra che utilizzi ancora una variabile globale. Puoi aggiornarlo per non richiedere una variabile globale (var id). Grazie
nickb

4
Penso che ti sia perso il link alla fine del post ... dai un'occhiata: jsfiddle.net/Zevan/c9UE5/5
Zevan

85

Debounce .

function debouncer( func , timeout ) {
   var timeoutID , timeout = timeout || 200;
   return function () {
      var scope = this , args = arguments;
      clearTimeout( timeoutID );
      timeoutID = setTimeout( function () {
          func.apply( scope , Array.prototype.slice.call( args ) );
      } , timeout );
   }
}


$( window ).resize( debouncer( function ( e ) {
    // do stuff 
} ) );

Nota, puoi utilizzare questo metodo per tutto ciò che desideri rimuovere dal rimbalzo (eventi chiave, ecc.).

Modifica il parametro di timeout per ottenere l'effetto desiderato ottimale.


1
Ho appena provato a eseguirlo e sembra funzionare DUE VOLTE . Ho sostituito "// do stuff" con "$ (" body "). Append (" <br/> DONE! ");" e lo chiama DUE VOLTE su un ridimensionamento del browser
nickb

oops ... ho dimenticato una parte piuttosto importante (effettivamente impostando il timeoutID) ... Risolto ora, per favore riprova;)
BGerrissen

3
@ user43493: Chiama la cuntion func, con il thispuntatore interno che punta a scope, e con Array.prototype.slice.call(args)(che genera un array standard da args) come argomenti
Eric

4
Questa è un'astrazione riutilizzabile, quindi non è necessario codificare manualmente i timeout o tenere traccia dei timeout ID globali. Puoi usarlo per molto di più del ridimensionamento della finestra;) ad esempio per annullare il rimbalzo di un pulsante di invio passando un parametro di timeout più alto. Puoi optare per la soluzione con meno codice, ma ti consiglio di tenere questo frammento nel tuo kit, lo apprezzerai più tardi;)
BGerrissen

2
Underscore.js ha una bella implementazione di questo se stai già usando quella libreria. underscorejs.org/#debounce
twmulloy

22

Puoi usare setTimeout()e clearTimeout()in combinazione con jQuery.data:

$(window).resize(function() {
    clearTimeout($.data(this, 'resizeTimer'));
    $.data(this, 'resizeTimer', setTimeout(function() {
        //do something
        alert("Haven't resized in 200ms!");
    }, 200));
});

Aggiornare

Ho scritto un'estensione per migliorare il gestore di eventi predefinito on(& bind) di jQuery . Associa una funzione di gestore eventi per uno o più eventi agli elementi selezionati se l'evento non è stato attivato per un determinato intervallo. Ciò è utile se desideri attivare una richiamata solo dopo un ritardo, come l'evento di ridimensionamento o altro. https://github.com/yckart/jquery.unevent.js

;(function ($) {
    var methods = { on: $.fn.on, bind: $.fn.bind };
    $.each(methods, function(k){
        $.fn[k] = function () {
            var args = [].slice.call(arguments),
                delay = args.pop(),
                fn = args.pop(),
                timer;

            args.push(function () {
                var self = this,
                    arg = arguments;
                clearTimeout(timer);
                timer = setTimeout(function(){
                    fn.apply(self, [].slice.call(arg));
                }, delay);
            });

            return methods[k].apply(this, isNaN(delay) ? arguments : args);
        };
    });
}(jQuery));

Usalo come qualsiasi altro ono bind-Event gestore, salvo che è possibile passare un parametro aggiuntivo come ultimo:

$(window).on('resize', function(e) {
    console.log(e.type + '-event was 200ms not triggered');
}, 200);

http://jsfiddle.net/ARTsinn/EqqHx/


1
Fai letteralmente la stessa ricerca su Google ogni giorno. e vieni alla stessa pagina per questo pezzo di codice. Vorrei poterlo ricordare ahah.
Dustin Silk

7
var lightbox_resize = false;
$(window).resize(function() {
    console.log(true);
    if (lightbox_resize)
        clearTimeout(lightbox_resize);
    lightbox_resize = setTimeout(function() {
        console.log('resize');
    }, 500);
});

Questo mi piace di più, ma perché non usare le parentesi graffe nelle tue dichiarazioni condizionali? So che funziona senza di loro, ma è una seccatura da guardare per gli altri sviluppatori;)
teewuane

7

Solo per aggiungere a quanto sopra, è comune ottenere eventi di ridimensionamento indesiderati a causa delle barre di scorrimento che spuntano dentro e fuori, ecco un po 'di codice per evitarlo:

function registerResize(f) {
    $(window).resize(function() {
        clearTimeout(this.resizeTimeout);
        this.resizeTimeout = setTimeout(function() {
            var oldOverflow = document.body.style.overflow;
            document.body.style.overflow = "hidden";
            var currHeight = $(window).height(),
                currWidth = $(window).width();
            document.body.style.overflow = oldOverflow;

            var prevUndefined = (typeof this.prevHeight === 'undefined' || typeof this.prevWidth === 'undefined');
            if (prevUndefined || this.prevHeight !== currHeight || this.prevWidth !== currWidth) {
                //console.log('Window size ' + (prevUndefined ? '' : this.prevHeight + "," + this.prevWidth) + " -> " + currHeight + "," + currWidth);
                this.prevHeight = currHeight;
                this.prevWidth = currWidth;

                f(currHeight, currWidth);
            }
        }, 200);
    });
    $(window).resize(); // initialize
}

registerResize(function(height, width) {
    // this will be called only once per resize regardless of scrollbars changes
});

vedi jsfiddle


5

Underscore.js ha un paio di ottimi metodi per questa operazione: throttlee debounce. Anche se non stai usando il carattere di sottolineatura, dai un'occhiata alla fonte di queste funzioni. Ecco un esempio:

var redraw = function() {'redraw logic here'};
var debouncedRedraw = _.debounce(redraw, 750);
$(window).on('resize', debouncedRedraw);

2

Questo è il mio approccio:

document.addEventListener('DOMContentLoaded', function(){
    var tos = {};
    var idi = 0;
    var fn  = function(id)
    {
        var len = Object.keys(tos).length;

        if(len == 0)
            return;

        to = tos[id];
        delete tos[id];

        if(len-1 == 0)
            console.log('Resize finished trigger');
    };

    window.addEventListener('resize', function(){
        idi++;
        var id = 'id-'+idi;
        tos[id] = window.setTimeout(function(){fn(id)}, 500);
    });
});

Il resize-event-listener cattura tutte le chiamate di ridimensionamento in arrivo, crea una funzione di timeout per ciascuna e salva l'identificatore di timeout insieme a un numero iterativo anteposto da 'id-'(per essere utilizzabile come chiave di matrice) tosnell'array.

ogni volta, il timeout si innesca, chiama la fn-funzione, che controlla se quello era l'ultimo timeout tosnell'array (la funzione fn cancella ogni timeout eseguito). se vero (= if(len-1 == 0)), il ridimensionamento è terminato.


Sarebbe bello se avessi qualche commento o una spiegazione su quello che stai facendo qui
Brett Gregson,

1

jQuery fornisce un offmetodo per rimuovere il gestore di eventi

$(window).resize(function(){
    if(magic == true) {
        $(window).off('resize', arguments.callee);
    }
});
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.