JQuery: Comment appeler l'événement RESIZE uniquement une fois le redimensionnement FINISHED?

109

Comment appeler une fonction une fois que la fenêtre du navigateur a terminé le redimensionnement?

J'essaye de le faire comme ça, mais j'ai des problèmes. J'utilise la fonction d'événement JQuery Resize:

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

Cependant, cette fonction est appelée en permanence si l'utilisateur redimensionne manuellement la fenêtre du navigateur. Ce qui signifie qu'il peut appeler cette fonction des dizaines de fois dans un court intervalle de temps.

Comment puis-je appeler la fonction de redimensionnement une seule fois (une fois que la fenêtre du navigateur a terminé le redimensionnement)?

METTRE À JOUR

Aussi sans avoir à utiliser une variable globale.

nickb
la source
@BGerrissen, si vous pouvez me montrer comment faire jsfiddle.net/Zevan/c9UE5/1 sans variable globale, je le ferai certainement :)
nickb
la méthode cleartimeout / settimeout ci-dessus fonctionne à merveille.
kris-o3
Copie
EvilDr

Réponses:

129

Voici un exemple utilisant les instructions de thejh

Vous pouvez stocker un identifiant de référence dans n'importe quel setInterval ou setTimeout. Comme ça:

var loop = setInterval(func, 30);

// some time later clear the interval
clearInterval(loop);
Zevan
la source
J'adore que ce code soit si simple. Un moyen de faire en sorte que cela fonctionne sans avoir à utiliser une variable globale?
nickb
Si je vois comment faire cela sans la variable globale, je ferai que la "réponse acceptée"
nickb
aucun problème. Il y a plusieurs façons. J'ai modifié la réponse pour refléter la réponse la plus simple à laquelle je puisse penser.
Zevan
merci pour la mise à jour mais il semble toujours utiliser une variable globale. Pouvez-vous le mettre à jour pour ne pas nécessiter de variable globale (var id). Merci
nickb
4
Je pense que vous avez manqué le lien à la fin de l'article ... vérifiez-le: jsfiddle.net/Zevan/c9UE5/5
Zevan
85

Rebondissez .

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

Notez que vous pouvez utiliser cette méthode pour tout ce que vous souhaitez supprimer (événements clés, etc.).

Ajustez le paramètre de délai pour un effet optimal souhaité.

BGerrissen
la source
1
J'ai juste essayé d'exécuter ceci et il semble fonctionner DEUX FOIS . J'ai remplacé "// faire des trucs" par "$ (" body "). Append (" <br/> DONE! ");" et il l'appelle DEUX FOIS sur un redimensionnement du navigateur
nickb
oups ... J'ai oublié une partie assez importante (en fait la définition du timeoutID) ... Corrigé maintenant, veuillez réessayer;)
BGerrissen
3
@ user43493: Il appelle le cuntion func, avec le thispointeur interne pointant vers scope, et avec Array.prototype.slice.call(args)(qui génère un tableau standard à partir de args) comme arguments
Eric
4
Il s'agit d'une abstraction réutilisable, vous n'avez donc pas à coder manuellement les délais d'expiration ou à suivre vous-même les ID de délai d'expiration globaux. Vous pouvez l'utiliser pour beaucoup plus que simplement le redimensionnement de la fenêtre;) par exemple pour supprimer un bouton d'envoi en passant un paramètre de délai d'attente plus élevé. Vous pouvez opter pour la solution avec moins de code, mais je vous conseille de conserver cet extrait dans votre kit, vous l'apprécierez plus tard;)
BGerrissen
2
Underscore.js a une belle implémentation de ceci si vous utilisez déjà cette lib. underscorejs.org/#debounce
twmulloy
22

Vous pouvez utiliser setTimeout()et clearTimeout()en conjonction avec jQuery.data:

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

Mettre à jour

J'ai écrit une extension pour améliorer le gestionnaire par défaut on(& bind) -event-handler de jQuery. Il attache une fonction de gestionnaire d'événements pour un ou plusieurs événements aux éléments sélectionnés si l'événement n'a pas été déclenché pendant un intervalle donné. Ceci est utile si vous souhaitez déclencher un rappel uniquement après un délai, comme l'événement de redimensionnement, ou autre. 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));

Utilisez-le comme n'importe quel autre gestionnaire onou bind-event, sauf que vous pouvez passer un paramètre supplémentaire en dernier:

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

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

yckart
la source
1
Faites littéralement la même recherche Google tous les jours. et revenez à la même page pour ce morceau de code. J'aimerais pouvoir m'en souvenir haha.
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);
});
Jlis
la source
C'est ce que j'aime le plus, mais pourquoi ne pas utiliser des accolades sur vos instructions conditionnelles? Je sais que cela fonctionne sans eux, mais c'est une douleur pour les autres développeurs à regarder;)
teewuane
7

Juste pour ajouter à ce qui précède, il est courant d'obtenir des événements de redimensionnement indésirables en raison des barres de défilement qui apparaissent et sortent, voici un code pour éviter cela:

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

voir jsfiddle

kofifus
la source
5

Underscore.js a quelques bonnes méthodes pour cette tâche: throttleet debounce. Même si vous n'utilisez pas Underscore, jetez un œil à la source de ces fonctions. Voici un exemple:

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

Voici ma démarche:

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

Le resize-event-listener attrape tous les appels de redimensionnement entrants, crée une fonction de temporisation pour chacun et enregistre l'identificateur de délai avec un numéro itératif précédé de 'id-'(pour être utilisable comme clé de tableau) dans letos -array.

à chaque fois, le timeout se déclenche, il appelle la fonction fn-fonction, qui vérifie si c'était le dernier timeout du tostableau (la fonction fn supprime chaque timeout exécuté). si vrai (= if(len-1 == 0)), le redimensionnement est terminé.

lsblsb
la source
Ce serait bien si vous aviez des commentaires ou une explication sur ce que vous faites ici
Brett Gregson
1

jQuery fournit une offméthode pour supprimer le gestionnaire d'événements

$(window).resize(function(){
    if(magic == true) {
        $(window).off('resize', arguments.callee);
    }
});
Qiang
la source