JavaScript / JQuery: $ (okno) .resize jak strzelać PO zakończeniu zmiany rozmiaru?

235

Używam JQuery jako takiego:

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

Wydaje się jednak, że jeśli dana osoba ręcznie zmieni rozmiar okien przeglądarki, przeciągając krawędź okna, aby ją powiększyć / zmniejszyć, .resizepowyższe zdarzenie zostanie uruchomione wielokrotnie.

Pytanie: Jak wywołać funkcję PO zakończeniu zmiany rozmiaru okna przeglądarki (aby zdarzenie było uruchamiane tylko raz)?

Sarah
źródło
Zobacz tę odpowiedź: stackoverflow.com/questions/667426/... Wymaga to użycia limitów czasu w celu opóźnienia wykonania funkcji.
Alastair Pitts
Nie wiem, czy to możliwe, możesz wypróbować tę wtyczkę, chociaż benalman.com/projects/jquery-resize-plugin
BrunoLM
Nawiasem mówiąc, zauważyłem, że jest to bardzo przydatne w przeglądarkach mobilnych, które mają tendencję do stopniowego ukrywania paska adresu, gdy użytkownik przewija w dół, a tym samym zmienia rozmiar ekranu. Spodziewałem się również, że zmiana rozmiaru ekranu nastąpi tylko na pulpicie ...
MakisH,

Odpowiedzi:

299

Oto modyfikacja rozwiązania CMS, którą można wywołać w wielu miejscach w kodzie:

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

Stosowanie:

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

Rozwiązanie CMS jest w porządku, jeśli wywołujesz go tylko raz, ale jeśli wywołujesz go wiele razy, np. Jeśli różne części kodu konfigurują osobne wywołania zwrotne do zmiany rozmiaru okna, to nie powiedzie się, ponieważ współużytkują timerzmienną.

Dzięki tej modyfikacji dostarczasz unikalny identyfikator dla każdego wywołania zwrotnego, a te unikalne identyfikatory służą do oddzielenia wszystkich zdarzeń przekroczenia limitu czasu.

Brahn
źródło
2
I <3 u, połączyłem to podczas zmiany rozmiaru pełnoekranowych (nie HTML5) wykresów Highcharts i działa świetnie.
Michael J. Calkins
Absolutnie niewiarygodne!
Starkers
2
Ponieważ tak wiele skorzystałem z twojej odpowiedzi, pomyślałem, że udostępniłem działający prototyp udostępnionego kodu dla reszty społeczności: jsfiddle.net/h6h2pzpu/3 . Ciesz się wszystkim!
Jonas Stawski
1
To wszystko jest idealne, ALE opóźnia wykonanie rzeczywistej funkcji o milisekundy (ms), które mogą być BARDZO NIEPRZECIWNE.
Tomas M
Czy to po prostu idealne, czy istnieje sposób na uchwycenie ostatniej zmiany i nie musisz polegać na tych milisekundach? W każdym razie właśnie to uratowało mi dzień:) Dzięki.
Leo
138

Wolę stworzyć wydarzenie:

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

Oto jak go utworzysz:

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

Możesz mieć to gdzieś w globalnym pliku javascript.

Carlos Martinez T.
źródło
Użyłem tego rozwiązania. Ale na dłuższą metę chciałbym wdrożyć to samo, co rozwiązanie Brahna.
Bharat Patil
pozwala to na powiązanie z tym wydarzeniem w dowolnym miejscu, a nie tylko w jednej funkcji, przy założeniu, że masz wiele stron / plików .js wymagających osobnych reakcji
hanzolo
Nie działało to dla mnie, ale odpowiedź
DusanV zadziałała
123

Używam następującej funkcji do opóźniania powtarzających się działań, zadziała w twoim przypadku:

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

Stosowanie:

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

Przekazana do niego funkcja oddzwaniania zostanie wykonana tylko wtedy, gdy ostatnie wezwanie do opóźnienia zostanie wykonane po upływie określonego czasu, w przeciwnym razie timer zostanie zresetowany, uważam to za przydatne do innych celów, takich jak wykrywanie, kiedy użytkownik przestał pisać itp. ..

CMS
źródło
5
Myślę, że to podejście może się nie powieść, jeśli zostanie użyte wiele razy (np. Jeśli różne części wywołań zwrotnych konfiguracji kodu $(window).resize), ponieważ wszystkie one współużytkują timerzmienną. Zobacz moją odpowiedź poniżej dla proponowanego rozwiązania.
brahn
Bardzo dobrze! Działa jak marzenie! Jak twoje odpowiedzi ... proste i eleganckie!
Pan Dynia
74

Jeśli masz zainstalowany plik Underscore.js, możesz:

$(window).resize(_.debounce(function(){
    alert("Resized");
},500));
JT.
źródło
1
Nawet jeśli nie chcesz uwzględniać podkreślenia, przynajmniej
pobierz
Debounce jest tutaj właściwą techniką, to tylko kwestia implementacji. Jest to doskonała implementacja, jeśli Underscore / Lodash jest już zależny od projektu.
Factor Mystic
Właśnie tego używam
Bharat Patil
20

Niektóre z wcześniej wymienionych rozwiązań nie działały dla mnie, mimo że mają bardziej ogólne zastosowanie. Alternatywnie znalazłem ten, który wykonał zadanie przy zmianie rozmiaru okna:

$(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
źródło
1
Tylko twój przykład działał dla mnie ... inni powyżej nie! Nie jestem pewien, dlaczego Twoja odpowiedź nie została wybrana.
Mumbo Jumbo,
Nie rozumiem, dlaczego łapać zdarzenie zmiany rozmiaru wewnątrz zdarzenia zmiany rozmiaru, ale to też działa dla mnie ...
lightbyte
Mumbo Jumbo, myślę, ponieważ inni koncentrują się na uogólnieniu, szukając rozwiązania każdego takiego problemu (wiele wywołań funkcji).
DusanV
lightbyte, to dlatego, że musisz zatrzymać poprzednie zdarzenia przy każdym wywołaniu funkcji.
DusanV,
13

Ogromne podziękowania dla Davida Walsha, oto waniliowa wersja podkreślenia podkreślenia.

Kod:

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

Proste użycie:

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

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

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

Irvin Dominin
źródło
6

W rzeczywistości, jak wiem, nie możesz wykonywać niektórych akcji dokładnie wtedy, gdy zmiana rozmiaru jest wyłączona, po prostu dlatego, że nie znasz działań przyszłego użytkownika. Możesz jednak założyć, że czas minął między dwoma zdarzeniami zmiany rozmiaru, więc jeśli poczekasz trochę dłużej niż ten czas i nie zostanie wykonana zmiana rozmiaru, możesz wywołać swoją funkcję.
Chodzi o to, że używamy setTimeouti jest to identyfikator, aby go zapisać lub usunąć. Na przykład wiemy, że czas między dwoma zdarzeniami zmiany rozmiaru wynosi 500 ms, dlatego będziemy czekać 750 ms.

var a;
$(window).resize(function(){
  clearTimeout(a);
  a = setTimeout(function(){
    // call your function
  },750);
});

Newint
źródło
3

Zadeklaruj globalnie opóźniony słuchacz:

var resize_timeout;

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

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

A poniżej używaj detektorów do resizedorganizowania wydarzeń, jak chcesz:

$(window).on('resized', function(){
    console.log('resized');
});
gzzz
źródło
Dzięki, to obecnie najlepsze rozwiązanie. Testowałem również, że 250 ms działa najlepiej przy zmianie rozmiaru okna.
AlexioVay,
2

Prosta wtyczka jQuery do opóźnionego zdarzenia zmiany rozmiaru okna.

SKŁADNIA:

Dodaj nową funkcję, aby zmienić rozmiar zdarzenia

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

Usuń funkcję (deklarując jej identyfikator) dodaną wcześniej

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

Usuń wszystkie funkcje

jQuery(window).resizeDelayed( false );

STOSOWANIE:

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

WYDAJNOŚĆ UŻYCIA:

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

PODŁĄCZ:

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

    }

})();
Déján Kőŕdić
źródło
1

Zakładając, że kursor myszy powróci do dokumentu po zmianie rozmiaru okna, możemy stworzyć zachowanie przypominające wywołanie zwrotne ze zdarzeniem onmouseover. Nie zapominaj, że to rozwiązanie może nie działać w przypadku ekranów dotykowych zgodnie z oczekiwaniami.

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
źródło
W porządku w przypadku witryn przeznaczonych tylko dla komputerów stacjonarnych, ale zdarzenie najechania kursorem myszy nigdy się nie zdarzy, jeśli na przykład użytkownik jest na telefonie komórkowym i zmienia widok z poziomego na pionowy lub zmienia rozmiar okna na pulpicie z ekranem dotykowym.
user56reinstatemonica8
Możesz zmienić rozmiar okna przeglądarki za pomocą klawiatury w najnowszych systemach Windows, takich jak Win-Arrow-Left Win-Arrow-Right itp.
Lu4
0

Oto co zaimplementowałem:

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

możemy wyczyścić setTimeout, jeśli spodziewamy się, że zmiana rozmiaru nastąpi mniej niż500ms

Powodzenia...

Akash
źródło