JQuery: Wie rufe ich das RESIZE-Ereignis erst auf, wenn die Größenänderung abgeschlossen ist?

109

Wie rufe ich eine Funktion auf, wenn die Größenänderung des Browserfensters abgeschlossen ist?

Ich versuche es so zu machen, habe aber Probleme. Ich verwende die JQuery Resize-Ereignisfunktion:

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

Diese Funktion wird jedoch kontinuierlich aufgerufen, wenn der Benutzer die Größe des Browserfensters manuell ändert. Dies bedeutet, dass diese Funktion in kurzen Zeitabständen dutzende Male aufgerufen werden kann.

Wie kann ich die Formatfunktion nur rufen ein einziges Mal (einmal das Browser - Fenster fertig Redimensionierung hat)?

AKTUALISIEREN

Auch ohne eine globale Variable verwenden zu müssen.

Nickb
quelle
@BGerrissen, wenn Sie mir zeigen können, wie man jsfiddle.net/Zevan/c9UE5/1 ohne eine globale Variable macht, werde ich definitiv :)
Nickb
Die obige Cleartimeout / Settimeout-Methode funktioniert wunderbar.
kris-o3

Antworten:

129

Hier ist ein Beispiel mit den Anweisungen von thejh

Sie können eine Referenz-ID für jedes setInterval oder setTimeout speichern. So was:

var loop = setInterval(func, 30);

// some time later clear the interval
clearInterval(loop);
Zevan
quelle
Ich liebe es, dass dieser Code so einfach ist. Wie kann dies funktionieren, ohne dass eine globale Variable verwendet werden muss?
Nickb
Wenn ich sehe, wie das ohne die globale Variable geht, mache ich das zur "akzeptierten Antwort"
Nickb
kein Problem. Es gibt einige Möglichkeiten. Ich habe die Antwort so bearbeitet, dass sie die einfachste widerspiegelt, die mir einfällt.
Zevan
Vielen Dank für das Update, aber es scheint immer noch eine globale Variable zu verwenden. Können Sie es so aktualisieren, dass keine globale Variable (var id) erforderlich ist? Danke
Nickb
4
Ich denke, Sie haben den Link am Ende des Beitrags verpasst ... check it out: jsfiddle.net/Zevan/c9UE5/5
Zevan
85

Entprellen .

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

Beachten Sie, dass Sie diese Methode für alles verwenden können, was Sie entprellen möchten (Schlüsselereignisse usw.).

Passen Sie den Timeout-Parameter an, um den gewünschten Effekt zu erzielen.

BGerrissen
quelle
1
Ich habe gerade versucht, dies auszuführen, und es scheint ZWEIMAL ausgeführt zu werden . Ich habe "// do stuff" durch "$ (" body ") ersetzt. Append (" <br/> DONE! ");" und es nennt es ZWEIMAL in einem Browser Größe ändern
Nickb
oops ... Ich habe einen ziemlich wichtigen Teil vergessen (tatsächlich die timeoutID einstellen) ... Jetzt behoben, bitte versuchen Sie es erneut;)
BGerrissen
3
@ user43493: Es ruft die Fotze auf func, wobei der interne thisZeiger auf scopeund mit Array.prototype.slice.call(args)(die ein Standardarray daraus generieren args) als Argumente zeigt
Eric
4
Dies ist eine wiederverwendbare Abstraktion, sodass Sie keine Zeitüberschreitungen von Hand codieren oder die globalen Zeitüberschreitungs-IDs selbst verfolgen müssen. Sie können es weit mehr als nur zur Größenänderung von Fenstern verwenden;), um beispielsweise eine Senden-Schaltfläche durch Übergeben eines höheren Timeout-Parameters zu entprellen. Sie können sich für die Lösung mit weniger Code entscheiden, aber ich
rate
2
Underscore.js hat eine nette Implementierung davon, wenn Sie diese Bibliothek bereits verwenden. underscorejs.org/#debounce
twmulloy
22

Sie können verwenden setTimeout()und clearTimeout()in Verbindung mit jQuery.data:

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

Aktualisieren

Ich habe eine Erweiterung geschrieben , um den Standard on(& bind) -Event-Handler von jQuery zu verbessern . Es fügt den ausgewählten Elementen eine Ereignishandlerfunktion für ein oder mehrere Ereignisse hinzu, wenn das Ereignis für ein bestimmtes Intervall nicht ausgelöst wurde. Dies ist nützlich, wenn Sie einen Rückruf erst nach einer Verzögerung wie dem Größenänderungsereignis oder einem anderen auslösen möchten. 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));

Verwenden Sie es wie jeden anderen onoder bind-event-Handler, außer dass Sie als letzten einen zusätzlichen Parameter übergeben können:

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

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

yckart
quelle
1
Führen Sie buchstäblich jeden Tag die gleiche Google-Suche durch. und kommen Sie auf die gleiche Seite für diesen Code. Ich wünschte, ich könnte mich nur daran erinnern, 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
quelle
Ich mag das am meisten, aber warum nicht geschweifte Klammern für Ihre bedingten Aussagen verwenden? Ich weiß, dass es ohne sie funktioniert, aber es ist ein Schmerz für andere Entwickler, sich das anzuschauen;)
Teewuane
7

Nur um das oben Gesagte zu ergänzen, ist es üblich, dass unerwünschte Größenänderungsereignisse auftreten, weil Bildlaufleisten ein- und ausgehen. Hier ist ein Code, um dies zu vermeiden:

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

siehe jsfiddle

Kofifus
quelle
5

Underscore.js bietet einige großartige Methoden für diese Aufgabe: throttleund debounce. Auch wenn Sie keinen Unterstrich verwenden, werfen Sie einen Blick auf die Quelle dieser Funktionen. Hier ist ein Beispiel:

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

Das ist mein Ansatz:

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

Der Resize-Event-Listener fängt alle eingehenden Resize-Aufrufe ab, erstellt für jeden eine Timeout-Funktion und speichert die Timeout-ID zusammen mit einer iterierenden Nummer 'id-', die im Array vorangestellt ist (um als Array-Schlüssel verwendet werden zu können) tos.

Jedes Mal, wenn das Timout ausgelöst wird, ruft es die fn-function auf, die prüft, ob dies das letzte Timeout im tosArray war (die fn-Funktion löscht jedes ausgeführte Timout). Wenn true (= if(len-1 == 0)), ist die Größenänderung abgeschlossen.

lsblsb
quelle
Es wäre schön, wenn Sie einige Kommentare oder eine Erklärung hätten, was Sie hier tun
Brett Gregson
1

jQuery bietet eine offMethode zum Entfernen des Ereignishandlers

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