JQuery: Jak wywołać wydarzenie RESIZE dopiero po zakończeniu zmiany rozmiaru?

109

Jak wywołać funkcję po ZAKOŃCZENIU zmiany rozmiaru okien przeglądarki?

Próbuję to zrobić w ten sposób, ale mam problemy. Używam funkcji zdarzenia JQuery Resize:

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

Jednak ta funkcja jest wywoływana w sposób ciągły, jeśli użytkownik ręcznie zmienia rozmiar okna przeglądarki. Co oznacza, że ​​może wywołać tę funkcję dziesiątki razy w krótkim odstępie czasu.

Jak mogę wywołać funkcję zmiany rozmiaru tylko raz (po zakończeniu zmiany rozmiaru okna przeglądarki)?

AKTUALIZACJA

Również bez konieczności używania zmiennej globalnej.

nickb
źródło
@BGerrissen, jeśli możesz mi pokazać, jak zrobić jsfiddle.net/Zevan/c9UE5/1 bez zmiennej globalnej, na pewno to zrobię :)
nickb
powyższa metoda cleartimeout / settimeout działa doskonale.
kris-o3

Odpowiedzi:

129

Oto przykład wykorzystujący instrukcje jh

Możesz przechowywać identyfikator odwołania do dowolnego setInterval lub setTimeout. Lubię to:

var loop = setInterval(func, 30);

// some time later clear the interval
clearInterval(loop);
Zevan
źródło
Podoba mi się, że ten kod jest tak prosty. Jakikolwiek sposób, aby to działało bez konieczności używania zmiennej globalnej?
nickb
Jeśli zobaczę, jak to zrobić bez zmiennej globalnej, sprawię, że „zaakceptowana odpowiedź”
nickb
nie ma problemu. Sposobów jest kilka. Zredagowałem odpowiedź, aby odzwierciedlić najprostszą, jaką przyszło mi do głowy.
Zevan
dzięki za aktualizację, ale wydaje się, że nadal używa zmiennej globalnej. Czy możesz go zaktualizować, aby nie wymagał zmiennej globalnej (identyfikator zmiennej). Dzięki
nickb
4
Myślę, że przegapiłeś link na końcu postu ... sprawdź to: jsfiddle.net/Zevan/c9UE5/5
Zevan
85

Odbicie .

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

Uwaga, możesz użyć tej metody do wszystkiego, co chcesz usunąć (kluczowe zdarzenia itp.).

Dostosuj parametr limitu czasu, aby uzyskać optymalny pożądany efekt.

BGerrissen
źródło
1
Właśnie próbowałem to uruchomić i wygląda na to, że działa DWA RAZY . Wymieniłem "// robić rzeczy" z "$ (" body ") (<br/> zrobić!") Append. ";" i nazywa to DWUKROTNIE przy
zmianie
ups ... zapomniałem raczej ważnej części (właściwie ustawiam timeoutID) ... Naprawiono teraz, proszę spróbuj ponownie;)
BGerrissen
3
@ user43493: Wywołuje cuntion func, z wewnętrznym thiswskaźnikiem wskazującym na scopeiz Array.prototype.slice.call(args)(co generuje standardową tablicę z args) jako argumenty
Eric
4
Jest to abstrakcja wielokrotnego użytku, więc nie musisz samodzielnie kodować limitów czasu ani śledzić globalnych identyfikatorów timeoutID. Możesz go użyć do czegoś więcej niż tylko zmiany rozmiaru okna;) na przykład do usunięcia przycisku wysyłania przez przekazanie wyższego parametru limitu czasu. Możesz zdecydować się na mniej kodu, ale radzę zachować ten fragment kodu w swoim zestawie, docenisz go później;)
BGerrissen
2
Underscore.js ma ładną implementację tego, jeśli już używasz tej biblioteki. underscorejs.org/#debounce
twmulloy
22

Możesz używać setTimeout()iw clearTimeout()połączeniu z jQuery.data:

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

Aktualizacja

Napisałem rozszerzenie, aby ulepszyć domyślny on(& bind) -event-handler jQuery . Dołącza funkcję obsługi zdarzenia dla co najmniej jednego zdarzenia do wybranych elementów, jeśli zdarzenie nie zostało wyzwolone dla danego interwału. Jest to przydatne, jeśli chcesz uruchomić wywołanie zwrotne dopiero po opóźnieniu, takim jak zdarzenie zmiany rozmiaru, lub w innym przypadku. 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));

Użyj go jak każdego innego programu obsługi onlub bind-event, z wyjątkiem tego, że możesz przekazać dodatkowy parametr jako ostatni:

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

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

yckart
źródło
1
Dosłownie codziennie wykonuj to samo wyszukiwanie w Google. i przejdź do tej samej strony z tym fragmentem kodu. Chciałbym móc to po prostu zapamiętać 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
źródło
Podoba mi się to najbardziej, ale dlaczego nie użyć nawiasów klamrowych w instrukcjach warunkowych? Wiem, że działa bez nich, ale jest to uciążliwe dla innych deweloperów patrzeć;)
teewuane
7

Aby dodać do powyższego, często występują niepożądane zdarzenia zmiany rozmiaru z powodu pojawiających się i znikających pasków przewijania, oto kod, aby tego uniknąć:

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

zobacz jsfiddle

kofifus
źródło
5

Underscore.js ma kilka świetnych metod do tego zadania: throttlei debounce. Nawet jeśli nie używasz podkreślenia, spójrz na źródło tych funkcji. Oto przykład:

var redraw = function() {'redraw logic here'};
var debouncedRedraw = _.debounce(redraw, 750);
$(window).on('resize', debouncedRedraw);
tybro0103
źródło
2

To jest moje podejście:

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

Odbiornik zdarzeń zmiany rozmiaru przechwytuje wszystkie przychodzące wywołania zmiany rozmiaru, tworzy funkcję limitu czasu dla każdego z nich i zapisuje identyfikator limitu czasu wraz z liczbą iteracyjną poprzedzoną 'id-'(aby można było użyć jako klucz tablicy) w tos-array.

za każdym razem, gdy wyzwalany jest limit czasu, wywołuje funkcję -funkcję fn, która sprawdza, czy był to ostatni limit czasu w tostablicy (funkcja fn usuwa każdy wykonany limit czasu). jeśli prawda (= if(len-1 == 0)), zmiana rozmiaru jest zakończona.

lsblsb
źródło
Byłoby miło, gdybyś miał jakieś komentarze lub wyjaśnienie, co tu robisz
Brett Gregson
1

jQuery udostępnia offmetodę usuwania programu obsługi zdarzeń

$(window).resize(function(){
    if(magic == true) {
        $(window).off('resize', arguments.callee);
    }
});
Qiang
źródło