Zdarzenie zmiany rozmiaru okna JavaScript

Odpowiedzi:

641

jQuery właśnie otacza standardowe resizezdarzenie DOM, np.

window.onresize = function(event) {
    ...
};

jQuery może trochę popracować, aby zapewnić, że zdarzenie zmiany rozmiaru będzie uruchamiane konsekwentnie we wszystkich przeglądarkach, ale nie jestem pewien, czy którakolwiek z przeglądarek się różni, ale zachęcam do przetestowania w Firefox, Safari i IE.

olliej
źródło
226
Jednym z potencjalnych problemów z tą metodą jest to, że zastępujesz zdarzenie, więc nie możesz dołączyć do niego wielu akcji. Kombinacja addEventListener / attachEvent to najlepszy sposób, aby uczynić javascript przyjaznym do gry z każdym innym skryptem, który może być wykonywany na stronie.
MyItchyChin
4
var onresize = window.onresize; window.onresize = function (event) {if (typeof onresize === 'function') onresize (); / ** ... * /}
SubOne
230
window.addEventListener('resize', function(){}, true);
WebWanderer
16
@SubOne jest doskonałym przykładem tego, jak nigdy nie należy tego robić.
Eugene Pankov,
28
ECMAScript 6 : window.onresize = () => { /* code */ };lub lepszy:window.addEventListener('resize', () => { /* code */ });
Derk Jan Speelman
559

Po pierwsze, wiem, że addEventListenermetoda została wspomniana w komentarzach powyżej, ale nie widziałem żadnego kodu. Ponieważ jest to preferowane podejście, oto:

window.addEventListener('resize', function(event){
  // do stuff here
});

Oto działająca próbka .

Jondlm
źródło
63
To najprostsza odpowiedź.
jacoviza,
7
Brzmi jak najlepsza odpowiedź, ponieważ nie zastępujesz zdarzenia window.onresize :-)
James Harrington
27
Wiele osób dodaje anonimowe obiekty nasłuchujące zdarzenia, tak jak Ty, w tym przykładzie, ale nie jest to naprawdę dobra praktyka, ponieważ takie podejście uniemożliwia późniejsze usunięcie tych obiektów nasłuchujących. Kiedyś nie stanowiło to problemu, ponieważ strony internetowe i tak były krótkotrwałe, ale w dzisiejszych czasach AJAX, RIA i SPA stają się jednym. Potrzebujemy sposobu zarządzania cyklem życia detektorów zdarzeń. Dlatego lepiej byłoby rozdzielić funkcję detektora na osobną funkcję, aby można ją było później usunąć za pomocą removeEventListener.
Stijn de Witt,
6
dlaczego nie każdy może odpowiedzieć w ten sposób bez całego puchu
cichy
2
Myślę, że tutaj zarówno prawda Stijn de Witt, jak i quemouna są prawdą. Czasami zależy Ci na usunięciu detektorów zdarzeń, ale w innym przypadku dodanie całego dodatkowego kodu, jak w powyższym przykładzie, jest niepotrzebne i może zarówno powodować wzdęcia, jak i mniejszą czytelność. Moim zdaniem, jeśli nie przewidujesz powodu do usunięcia detektora, takie podejście jest najlepszym rozwiązaniem. W razie potrzeby zawsze możesz go przepisać później. Z mojego doświadczenia wynika, że ​​te potrzeby pojawiają się tylko w określonych sytuacjach.
cazort
525

Nigdy nie zastępuj funkcji window.onresize.

Zamiast tego utwórz funkcję, aby dodać detektor zdarzeń do obiektu lub elementu. To sprawdza i powoduje, że detektory nie działają, a następnie zastępuje funkcję obiektu w ostateczności. Jest to preferowana metoda stosowana w bibliotekach takich jak jQuery.

object: element lub obiekt okna
type: zmiana rozmiaru, przewijanie (typ zdarzenia)
callback: odwołanie do funkcji

var addEvent = function(object, type, callback) {
    if (object == null || typeof(object) == 'undefined') return;
    if (object.addEventListener) {
        object.addEventListener(type, callback, false);
    } else if (object.attachEvent) {
        object.attachEvent("on" + type, callback);
    } else {
        object["on"+type] = callback;
    }
};

Zatem użycie jest takie:

addEvent(window, "resize", function_reference);

lub z anonimową funkcją:

addEvent(window, "resize", function(event) {
  console.log('resized');
});
Alex V.
źródło
100
To powinna być zaakceptowana odpowiedź. Zastąpienie onresize to po prostu zła praktyka.
Tiberiu-Ionuț Stan
8
hah, o co chodzi z tymi wszystkimi dodatkowymi czekami zerowymi? próbujesz pracować w IE 4.5 czy coś?
FlavorScape,
1
Zastanawiasz się, jak wywołać tę funkcję, aby odbierać zdarzenia zmiany rozmiaru okna? Oto jak: addEvent (window, „resize”, function_reference); :)
oabarca
6
Pamiętaj, że od IE 11 attachEventnie jest już obsługiwany. Preferowanym sposobem na przyszłość jest addEventListener.
Łukasz
6
Ostrożnie z elem == undefined. Możliwe (choć mało prawdopodobne), że „niezdefiniowany” jest lokalnie zdefiniowany jako coś innego. stackoverflow.com/questions/8175673/…
Luke
32

Zdarzenie zmiany rozmiaru nigdy nie powinno być używane bezpośrednio, ponieważ jest uruchamiane w sposób ciągły podczas zmiany rozmiaru.

Użyj funkcji debounce, aby złagodzić nadmiar połączeń.

window.addEventListener('resize',debounce(handler, delay, immediate),false);

Oto powszechna debata unosząca się w sieci, ale szukaj bardziej zaawansowanych, jak featta w lodash.

const debounce = (func, wait, immediate) => {
    var timeout;
    return () => {
        const context = this, args = arguments;
        const later = function() {
            timeout = null;
            if (!immediate) func.apply(context, args);
        };
        const callNow = immediate && !timeout;
        clearTimeout(timeout);
        timeout = setTimeout(later, wait);
        if (callNow) func.apply(context, args);
    };
};

Można to wykorzystać tak ...

window.addEventListener('resize', debounce(() => console.log('hello'),
200, false), false);

Nigdy nie będzie strzelał więcej niż raz na 200 ms.

Do zmian orientacji mobilnej użyj:

window.addEventListener('orientationchange', () => console.log('hello'), false);

Oto mała biblioteka, którą stworzyłem, aby starannie się tym zająć.

przodem do góry
źródło
Doceniam informacje na temat: debounce (teraz wydaje się nieocenioną koncepcją / funkcją, mimo że wcześniej opierałem się stosowaniu limitów czasu w ten sposób), lodash (choć osobiście nie jestem przekonany) i rozwiązywanie niejednoznaczności co do tego, czy użyć addEvent jako rezerwowego addEventListener (tylko dlatego, że starsze odpowiedzi tak naprawdę nie odnoszą się wprost do komentarzy na ten temat)
wunth
5
Do Twojej wiadomości, ponieważ używasz notacji ES6 ze strzałkami, funkcja wewnętrzna musi mieć (...arguments) => {...}(lub ...argsdla mniejszego zamieszania), ponieważ argumentszmienna nie jest już dostępna.
coderatchet
Jestem w pełni świadomy, że ten fragment kodu nie jest moim kodem, jest tylko przykładem.
frontsideup
Świetna odpowiedź, należy rozwiązać problemy perf ze zmianą rozmiaru! Ogłaszanie jest opcją, inną jest proste dławienie (co może być dobrym rozwiązaniem, jeśli potrzebujesz interfejsu użytkownika, aby zmienić rozmiar w „krokach”). Zobacz bencentra.com/code/2015/02/27/optimizing-window-resize.html przykłady
Robin METRAL
24

Rozwiązanie dla 2018+:

Powinieneś użyć ResizeObserver . Jest to rozwiązanie natywne dla przeglądarki, które ma znacznie lepszą wydajność niż korzystanie ze resizezdarzenia. Ponadto obsługuje nie tylko zdarzenie, documentale także arbitralne elements.

var ro = new ResizeObserver( entries => {
  for (let entry of entries) {
    const cr = entry.contentRect;
    console.log('Element:', entry.target);
    console.log(`Element size: ${cr.width}px x ${cr.height}px`);
    console.log(`Element padding: ${cr.top}px ; ${cr.left}px`);
  }
});

// Observe one or multiple elements
ro.observe(someElement);

Obecnie obsługują go Firefox, Chrome i Safari . W przypadku innych (i starszych) przeglądarek musisz użyć wypełnienia wypełniającego .

str
źródło
Nadal najlepsza odpowiedź w 2020 roku imo
Jesse Reza Khorasanee
16

Uważam, że @Alex V udzielił prawidłowej odpowiedzi, ale odpowiedź wymaga pewnej modernizacji, ponieważ ma już ponad pięć lat.

Istnieją dwa główne problemy:

  1. Nigdy nie używaj objectjako nazwy parametru. To słowo zastrzeżone. Biorąc to pod uwagę, zapewniona funkcja @Alex V nie będzie działać strict mode.

  2. addEventFunkcję dostarczaną przez @Alex V nie zwracają event objectjeśli addEventListenerstosowana jest metoda. Aby addEventto umożliwić, należy dodać kolejny parametr do funkcji.

UWAGA: Nowy parametr addEventzostał opcjonalny, aby migracja do tej nowej wersji funkcji nie przerywała żadnych wcześniejszych wywołań tej funkcji. Wszystkie starsze zastosowania będą obsługiwane.

Oto zaktualizowana addEventfunkcja z następującymi zmianami:

/*
    function: addEvent

    @param: obj         (Object)(Required)

        -   The object which you wish
            to attach your event to.

    @param: type        (String)(Required)

        -   The type of event you
            wish to establish.

    @param: callback    (Function)(Required)

        -   The method you wish
            to be called by your
            event listener.

    @param: eventReturn (Boolean)(Optional)

        -   Whether you want the
            event object returned
            to your callback method.
*/
var addEvent = function(obj, type, callback, eventReturn)
{
    if(obj == null || typeof obj === 'undefined')
        return;

    if(obj.addEventListener)
        obj.addEventListener(type, callback, eventReturn ? true : false);
    else if(obj.attachEvent)
        obj.attachEvent("on" + type, callback);
    else
        obj["on" + type] = callback;
};

Przykładowe wywołanie nowej addEventfunkcji:

var watch = function(evt)
{
    /*
        Older browser versions may return evt.srcElement
        Newer browser versions should return evt.currentTarget
    */
    var dimensions = {
        height: (evt.srcElement || evt.currentTarget).innerHeight,
        width: (evt.srcElement || evt.currentTarget).innerWidth
    };
};

addEvent(window, 'resize', watch, true);
WebWanderer
źródło
Twierdziłbym, że attachEvent nie ma już znaczenia w 2017 r.
Vlad Nicula,
@VladNicula Zgadzam się, ale ta odpowiedź ma dwa lata.
WebWanderer
12

Dzięki za odwołanie się do mojego postu na blogu pod adresem http://mbccs.blogspot.com/2007/11/fixing-window-resize-event-in-ie.html .

Chociaż możesz po prostu podłączyć się do standardowego zdarzenia zmiany rozmiaru okna, przekonasz się, że w IE zdarzenie jest uruchamiane raz na każdy ruch X i raz na każdy ruch w osi Y, w wyniku czego wywoływana jest tona zdarzeń, które mogą mieć wydajność wpływ na witrynę, jeśli renderowanie jest intensywnym zadaniem.

Moja metoda polega na krótkim przekroczeniu limitu czasu, który jest anulowany przy kolejnych zdarzeniach, dzięki czemu zdarzenie nie jest propagowane do twojego kodu, dopóki użytkownik nie zmieni rozmiaru okna.

Steven
źródło
7
window.onresize = function() {
    // your code
};
saravanakumar
źródło
22
Jak mówi wiele powyższych komentarzy, najlepiej nie zastępować funkcji onresize; raczej dodaj nowe wydarzenie. Zobacz odpowiedź Jondlma.
Łukasz
6

Przydatny może być następujący post na blogu: Naprawianie zdarzenia zmiany rozmiaru okna w IE

Zapewnia ten kod:

Sys.Application.add_load(function(sender, args) {
    $addHandler(window, 'resize', window_resize);
});

var resizeTimeoutId;

function window_resize(e) {
     window.clearTimeout(resizeTimeoutId);
     resizeTimeoutId = window.setTimeout('doResizeCode();', 10);
}
lakshmanaraj
źródło
6
Ten dotyczy tylko aplikacji ASP.NET
Evgeny Gorb
2

Wymienione powyżej rozwiązania będą działać, jeśli wszystko, co chcesz zrobić, to zmienić rozmiar okna i tylko okna. Jeśli jednak chcesz, aby zmiana rozmiaru była propagowana do elementów potomnych, musisz sam propagować zdarzenie. Oto przykładowy kod, aby to zrobić:

window.addEventListener("resize", function () {
  var recResizeElement = function (root) {
    Array.prototype.forEach.call(root.childNodes, function (el) {

      var resizeEvent = document.createEvent("HTMLEvents");
      resizeEvent.initEvent("resize", false, true);
      var propagate = el.dispatchEvent(resizeEvent);

      if (propagate)
        recResizeElement(el);
    });
  };
  recResizeElement(document.body);
});

Zauważ, że element potomny może wywoływać

 event.preventDefault();

na obiekcie zdarzenia, który jest przekazywany jako pierwszy argument zdarzenia zmiany rozmiaru. Na przykład:

var child1 = document.getElementById("child1");
child1.addEventListener("resize", function (event) {
  ...
  event.preventDefault();
});
rysama
źródło
1
<script language="javascript">
    window.onresize = function() {
    document.getElementById('ctl00_ContentPlaceHolder1_Accordion1').style.height = '100%';
} 

</script>
Rob Herring
źródło
1
var EM = new events_managment();

EM.addEvent(window, 'resize', function(win,doc, event_){
    console.log('resized');
    //EM.removeEvent(win,doc, event_);
});

function events_managment(){
    this.events = {};
    this.addEvent = function(node, event_, func){
        if(node.addEventListener){
            if(event_ in this.events){
                node.addEventListener(event_, function(){
                    func(node, event_);
                    this.events[event_](win_doc, event_);
                }, true);
            }else{
                node.addEventListener(event_, function(){
                    func(node, event_);
                }, true);
            }
            this.events[event_] = func;
        }else if(node.attachEvent){

            var ie_event = 'on' + event_;
            if(ie_event in this.events){
                node.attachEvent(ie_event, function(){
                    func(node, ie_event);
                    this.events[ie_event]();
                });
            }else{
                node.attachEvent(ie_event, function(){
                    func(node, ie_event);
                });
            }
            this.events[ie_event] = func;
        }
    }
    this.removeEvent = function(node, event_){
        if(node.removeEventListener){
            node.removeEventListener(event_, this.events[event_], true);
            this.events[event_] = null;
            delete this.events[event_];
        }else if(node.detachEvent){
            node.detachEvent(event_, this.events[event_]);
            this.events[event_] = null;
            delete this.events[event_];
        }
    }
}
ZACHWYCAJĄCE
źródło