Jak wykryć zmieniony wymiar DIV?

351

Mam następujący przykładowy html, jest DIV, który ma 100% szerokości. Zawiera niektóre elementy. Podczas zmiany rozmiaru okien elementy wewnętrzne mogą zostać ponownie ustawione, a wymiar div może się zmienić. Pytam, czy można podpiąć zdarzenie zmiany wymiaru div? i jak to zrobić? Obecnie wiążę funkcję zwrotną ze zdarzeniem zmiany rozmiaru jQuery na docelowym DIV, jednak nie jest generowany dziennik konsoli, patrz poniżej:

Przed zmianą rozmiaru wprowadź opis zdjęcia tutaj

<html>
<head>
    <script type="text/javascript" language="javascript" src="http://code.jquery.com/jquery-1.6.1.min.js"></script>
    <script type="text/javascript" language="javascript">
            $('#test_div').bind('resize', function(){
                console.log('resized');
            });
    </script>
</head>
<body>
    <div id="test_div" style="width: 100%; min-height: 30px; border: 1px dashed pink;">
        <input type="button" value="button 1" />
        <input type="button" value="button 2" />
        <input type="button" value="button 3" />
    </div>
</body>
</html>
William Choi
źródło
7
To nie zadziała, ponieważ wiążesz zdarzenie zmiany rozmiaru z określonym elementem div. Ale zdarzenie zmiany rozmiaru wyzwoli okno, a nie element.
Fatih Acet
Możesz użyć setIntervaltutaj jako możliwego rozwiązania. Zawsze możesz powiązać clearIntervalkliknięcie przycisku, aby zatrzymać pętlę po zakończeniu pracy.
Peter Darmis
1
Od 2020 roku ResizeObserver działa we wszystkich głównych przeglądarkach (Chrome, Safari, Firefox, Edge) oprócz IE. caniuse.com/#feat=resizeobserver
Dan

Odpowiedzi:

263

Istnieje bardzo skuteczna metoda określania, czy rozmiar elementu został zmieniony.

http://marcj.github.io/css-element-queries/

Ta biblioteka ma klasę, ResizeSensorktórej można użyć do wykrywania zmiany rozmiaru.
Wykorzystuje podejście oparte na zdarzeniach, więc jest cholernie szybkie i nie marnuje czasu procesora.

Przykład:

new ResizeSensor(jQuery('#divId'), function(){ 
    console.log('content dimension changed');
});

Nie używaj wtyczki jQuery onresize, ponieważ używa ona setTimeout()w połączeniu z odczytem DOM clientHeight/ clientWidthwłaściwości w pętli w celu sprawdzenia zmian.
Jest to niesamowicie wolne i niedokładne, ponieważ powoduje przeładowanie układu .

Ujawnienie: Jestem bezpośrednio powiązany z tą biblioteką.

Marc J. Schmidt
źródło
8
@jake, obsługa IE11 jest teraz zaimplementowana.
Marc J. Schmidt
15
@Aletheios powinieneś przyjrzeć się bliżej. requestAnimationFrame nie służy do powolnego wykrywania wymiarów, ale wręcz przeciwnie: wygładza wszystkie te ekstremalne liczby zdarzeń, które są wywoływane przez czujnik zmiany rozmiaru oparty na rzeczywistych zdarzeniach, aby zaoszczędzić czas procesora. Zobacz zaktualizowane dokumenty na github.com/marcj/css-element-queries/blob/master/src/…
Marc J. Schmidt
6
@Aletheios Chyba brakuje Ci punktu: setTimeout jest nie tylko niedokładny, ale powolny jak diabli. RequestAnimationFrame, która sprawdza prostą wartość logiczną, jest o rząd wielkości szybsza niż setTimeout cały czas sprawdzający właściwości DOM. Poza tym setTimeout jest również nazywany co kilka milisekund.
Marc J. Schmidt,
9
@Orwellophile, oczywiście nic nie zrozumiałeś. Przestań zaniżać odpowiedzi tam, gdzie nie masz pojęcia, jak to działa wewnętrznie. Ponownie: css-element-query: używa rzeczywistego zdarzenia zmiany rozmiaru, które jest wywoływane za każdym razem, gdy zmienia się wymiar. Ponieważ otrzymujesz zbyt wiele zdarzeń (na przykład przeciągnij i upuść), dodano funkcję requestAnimationFrame sprawdzającą prostą zmienną logiczną, aby wygładzić te wyzwalane zdarzenia do 1 / klatki. wtyczka jquery: Wykorzystuje setTimeout do sprawdzania przez cały czas rekwizytów układu DOM (clientHeight itp.), gdzie każde sprawdzanie jest bardzo wolne, ale musi być często sprawdzane, aby mieć tę samą funkcjonalność.
Marc J. Schmidt,
9
@Aletheios, @Orwellophile i wszyscy, którzy zostali przez nich wprowadzeni w błąd: requestAnimationFramevs nie setTimeoutma znaczenia, ta biblioteka nie zapętla się . requestAnimationFramesłuży tylko do zmniejszania częstotliwości wywołań wywołań zwrotnych, nie odpytuje . MutationObserver można teoretycznie wykorzystać do podobnego efektu, ale nie w starszych przeglądarkach, w przeciwieństwie do tego. Jest to wyjątkowo sprytne.
Han Seoul-Oh
250

Nowy standard to Resi Observer api, dostępny w Chrome 64.

function outputsize() {
 width.value = textbox.offsetWidth
 height.value = textbox.offsetHeight
}
outputsize()

new ResizeObserver(outputsize).observe(textbox)
Width: <output id="width">0</output><br>
Height: <output id="height">0</output><br>
<textarea id="textbox">Resize me</textarea><br>

Zmień rozmiar obserwatora

Spec: https://wicg.github.io/ResizeObserver

Polyfills: https://github.com/WICG/ResizeObserver/issues/3

Problem z Firefoksem: https://bugzil.la/1272409

Wydanie Safari: http://wkb.ug/157743

Bieżące wsparcie: http://caniuse.com/#feat=resizeobserver

Daniel Herr
źródło
3
nie jest jeszcze przydatny w witrynach produkcyjnych, ponieważ obecnie obsługuje go tylko Chrome. Ale zdecydowanie najlepsze rozwiązanie tutaj! btw, tutaj jest szybki przegląd statusu wdrożenia: chromestatus.com/feature/5705346022637568
Philipp
8
@Philipp Istnieje polifill.
Daniel Herr
2
Wygląda na to, że wciąż jest funkcją eksperymentalną we wszystkich przeglądarkach caniuse.com/#feat=resizeobserver
Francesc Rosas
5
Nawet 2 lata po tej odpowiedzi obsługa przeglądarki jest ponura: caniuse.com/#search=resizeobserver
Scrimothy
2
Działa jak urok w najnowszych Chrome i FF.
Klemen Tušar
32

Musisz powiązać resizezdarzenie z windowobiektem, a nie z ogólnym elementem HTML.

Możesz wtedy użyć tego:

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

a w ramach funkcji oddzwaniania możesz sprawdzić nową szerokość swojego divpołączenia

$('.a-selector').width();

Tak więc odpowiedź na twoje pytanie brzmi: nie , nie możesz resizeprzypisać wydarzenia do div.

Alessandro Vendruscolo
źródło
123
ta odpowiedź nie jest wystarczająco dobra. zmiany rozmiaru mogą wystąpić bez ŻADNEJ zmiany rozmiaru okna. w ogóle.
vsync
9
na przykład, gdy masz element rozdzielający lub po prostu dołączasz jakiś tekst lub inną treść do elementu.
Günter Zöchbauer
@anu nieprawda, możesz mieć np. modyfikację DOM za pomocą javascript, co spowoduje zastosowanie CSS do nowej struktury, co spowoduje inny układ - wielkość każdego kontenera.
Antoniossss,
29

W dłuższej perspektywie będziesz mógł korzystać z ResizeObserver .

new ResizeObserver(callback).observe(element);

Niestety nie jest obecnie domyślnie obsługiwany w wielu przeglądarkach.

W międzyczasie możesz użyć funkcji takiej jak poniżej. Ponieważ większość zmian rozmiaru elementu będzie pochodzić ze zmiany rozmiaru okna lub zmiany czegoś w DOM. Możesz słuchać zmiany rozmiaru okna za pomocą zdarzenia zmiany rozmiaru okna i możesz słuchać zmian DOM za pomocą MutationObserver .

Oto przykład funkcji, która oddzwoni, gdy rozmiar podanego elementu zmieni się w wyniku jednego z tych zdarzeń:

var onResize = function(element, callback) {
  if (!onResize.watchedElementData) {
    // First time we are called, create a list of watched elements
    // and hook up the event listeners.
    onResize.watchedElementData = [];

    var checkForChanges = function() {
      onResize.watchedElementData.forEach(function(data) {
        if (data.element.offsetWidth !== data.offsetWidth ||
            data.element.offsetHeight !== data.offsetHeight) {
          data.offsetWidth = data.element.offsetWidth;
          data.offsetHeight = data.element.offsetHeight;
          data.callback();
        }
      });
    };

    // Listen to the window's size changes
    window.addEventListener('resize', checkForChanges);

    // Listen to changes on the elements in the page that affect layout 
    var observer = new MutationObserver(checkForChanges);
    observer.observe(document.body, { 
      attributes: true,
      childList: true,
      characterData: true,
      subtree: true 
    });
  }

  // Save the element we are watching
  onResize.watchedElementData.push({
    element: element,
    offsetWidth: element.offsetWidth,
    offsetHeight: element.offsetHeight,
    callback: callback
  });
};
nkron
źródło
1
Niestety wydaje się, że MutationObserver nie może wykryć zmian w Shadow DOM.
Ajedi32,
@nkron, Niestety, jeśli używasz zmiany rozmiaru CSS3 , zmiana rozmiaru może nastąpić, gdy użytkownik zmieni rozmiar ręcznie. Tego wydarzenia nie można złapać.
Pacerier
@ Ajedi32 Tak, ale możesz używać MutationObserver wewnątrz ShadowDOM. Chodzi mi o to, że zamiast oglądać, bodyjak w tym przykładzie, możesz obejrzeć element bezpośrednio. To jest faktycznie bardziej wydajne i wydajne niż obserwowanie całego ciała.
trusktr
@ Ajedi32 Cóż, jedynym problemem jest to, że jeśli drzewo DOM Shadow jest zamknięte, nie będziesz mieć dostępu do wewnętrznych, ale nie sądzę, że jest to zwykle coś, co musisz zrobić, a jeśli musisz obserwować takie zamknięte drzewo może mieć problem z projektem. Najlepiej byłoby obserwować tylko własne elementy, do których masz dostęp. Komponent Shadow-DOM, który wymaga obserwowania jego wewnętrznego rozmiaru, może nie zostać poprawnie zaprojektowany. Czy masz konkretny przykład? Jeśli tak, to może pomogę zaproponować rozwiązanie (ponieważ ja też go interesuję, ale nie mam jeszcze przykładu).
trusktr
@trusktr W tej chwili nie mam MCVE, ale w zasadzie każdy element Shadow DOM, który może zmienić swój rozmiar w wyniku niektórych danych wejściowych użytkownika, wystarczy, aby wywołać ten problem. Pole, które po kliknięciu powiększa się o dodatkowe informacje. Zauważ, że obserwuję stronę, a nie pojedynczy element, ponieważ chcę automatycznie przewijać w dół, gdy rozmiar strony wzrośnie, bez względu na powód. Rozwijanie elementu Shadow DOM to tylko jeden z wielu możliwych powodów wydłużenia strony.
Ajedi32,
24

ResizeSensor.js jest częścią ogromnej biblioteki, ale ograniczyłem jej funkcjonalność do TEGO :

function ResizeSensor(element, callback)
{
    let zIndex = parseInt(getComputedStyle(element));
    if(isNaN(zIndex)) { zIndex = 0; };
    zIndex--;

    let expand = document.createElement('div');
    expand.style.position = "absolute";
    expand.style.left = "0px";
    expand.style.top = "0px";
    expand.style.right = "0px";
    expand.style.bottom = "0px";
    expand.style.overflow = "hidden";
    expand.style.zIndex = zIndex;
    expand.style.visibility = "hidden";

    let expandChild = document.createElement('div');
    expandChild.style.position = "absolute";
    expandChild.style.left = "0px";
    expandChild.style.top = "0px";
    expandChild.style.width = "10000000px";
    expandChild.style.height = "10000000px";
    expand.appendChild(expandChild);

    let shrink = document.createElement('div');
    shrink.style.position = "absolute";
    shrink.style.left = "0px";
    shrink.style.top = "0px";
    shrink.style.right = "0px";
    shrink.style.bottom = "0px";
    shrink.style.overflow = "hidden";
    shrink.style.zIndex = zIndex;
    shrink.style.visibility = "hidden";

    let shrinkChild = document.createElement('div');
    shrinkChild.style.position = "absolute";
    shrinkChild.style.left = "0px";
    shrinkChild.style.top = "0px";
    shrinkChild.style.width = "200%";
    shrinkChild.style.height = "200%";
    shrink.appendChild(shrinkChild);

    element.appendChild(expand);
    element.appendChild(shrink);

    function setScroll()
    {
        expand.scrollLeft = 10000000;
        expand.scrollTop = 10000000;

        shrink.scrollLeft = 10000000;
        shrink.scrollTop = 10000000;
    };
    setScroll();

    let size = element.getBoundingClientRect();

    let currentWidth = size.width;
    let currentHeight = size.height;

    let onScroll = function()
    {
        let size = element.getBoundingClientRect();

        let newWidth = size.width;
        let newHeight = size.height;

        if(newWidth != currentWidth || newHeight != currentHeight)
        {
            currentWidth = newWidth;
            currentHeight = newHeight;

            callback();
        }

        setScroll();
    };

    expand.addEventListener('scroll', onScroll);
    shrink.addEventListener('scroll', onScroll);
};

Jak tego użyć:

let container = document.querySelector(".container");
new ResizeSensor(container, function()
{
    console.log("dimension changed:", container.clientWidth, container.clientHeight);
});
Martin Wantke
źródło
To właśnie robią wykresy.
Antoniossss,
Dzięki za kompresowanie tego. Wypróbuję to :)
Tony K.
W tym wyrażeniu brakuje czegośparseInt( getComputedStyle(element) )
GetFree
2
Jestem zdezorientowany, w jaki sposób „niech zIndex = parseInt (getComputedStyle (element));” ważny? Zakładam, że miałeś na myśli: „niech zIndex = parseInt (getComputedStyle (element) .zIndex);
Ryan Badour
1
Powyższe rozwiązanie / kod nie wykrywa wszystkich zdarzeń zmiany rozmiaru (np. Jeśli rozmiar kontenera zostanie zmieniony na stały, to nie wykrywa zmiany rozmiaru zdarzeń potomnych. Zobacz jsfiddle.net/8md2rfsy/1 ). Przyjęta odpowiedź działa (odkomentuj odpowiednie linie).
newBee
21

NIE polecam setTimeout()hackowania, ponieważ spowalnia działanie! Zamiast tego można użyć obserwatorów mutacji DOM do nasłuchiwania zmiany rozmiaru Div.

JS:

var observer = new MutationObserver(function(mutations) {
    console.log('size changed!');
  });
  var target = document.querySelector('.mydiv');
  observer.observe(target, {
    attributes: true
  });

HTML:

<div class='mydiv'>
</div>

Oto skrzypce
Spróbuj zmienić rozmiar div.


Możesz dodatkowo zawinąć swoją metodę w debouncecelu poprawy wydajności. debounceuruchomi twoją metodę co x milisekund zamiast wyzwalać każdą milisekundę zmiany rozmiaru DIV.

JerryGoyal
źródło
9
Jedynym problemem z tym jest to, że nie zgłasza zmiany, chyba że mutacja faktycznie stanie się z elementem. Jeśli rozmiar elementu zmieni się z powodu dodania innych elementów, to nie zadziała.
Tony K.
Zgadzam się z @TonyK., To mój jedyny problem z MutationObserver i teraz szukam biblioteki zmiany rozmiaru elementu
Murhaf Sousli
@JerryGoyal To nie działa, gdy div zawiera znacznik <img>. W pierwszej kolejności obraz nie jest ładowany, a rozmiar div jest ustawiany na 0, a po załadowaniu rozmiar div jest zmieniany, ale obserwator nie jest uruchamiany
Santhosh
15

Najlepszym rozwiązaniem byłoby użycie tak zwanych zapytań o elementy . Nie są one jednak standardowe, nie istnieje żadna specyfikacja - a jedyną opcją jest użycie jednej z dostępnych wielopełniaczy / bibliotek, jeśli chcesz iść tą drogą.

Ideą zapytań o elementy jest umożliwienie określonego kontenera na stronie odpowiedzi na miejsce, które jest do niego przewidziane. Umożliwi to jednokrotne napisanie komponentu, a następnie upuszczenie go w dowolnym miejscu na stronie, a jednocześnie dostosuje jego zawartość do bieżącego rozmiaru. Bez względu na rozmiar okna. To pierwsza różnica między zapytaniami o elementy a zapytaniami o media. Wszyscy mają nadzieję, że w pewnym momencie zostanie utworzona specyfikacja, która ustandaryzuje zapytania elementów (lub coś, co osiągnie ten sam cel) i sprawi, że będą one rodzime, czyste, proste i niezawodne. Większość ludzi zgadza się, że zapytania o media są dość ograniczone i nie pomagają w budowie modułowej i prawdziwej reakcji.

Istnieje kilka wypełniaczy / bibliotek, które rozwiązują problem na różne sposoby (choć można to nazwać obejściami zamiast rozwiązań):

Widziałem inne rozwiązania podobnych proponowanych problemów. Zwykle używają timerów lub rozmiaru okna / okna pod maską, co nie jest prawdziwym rozwiązaniem. Co więcej, myślę, że najlepiej byłoby to rozwiązać głównie w CSS, a nie w javascript lub HTML.

Stan
źródło
+1 za zapytania dotyczące elementów, ale nie mam nadziei, że wkrótce je zobaczę. W tym momencie nie ma nawet projektu specyfikacji AFAIK ... może jedna z zainteresowanych stron powinna ją złożyć i przesłać?
Rob Evans
1
To oczywiście dobry pomysł. Osiągnięcie ostatecznej wersji zajmie dużo czasu, należy zwrócić uwagę. Dobre zaprojektowanie tego nie wydaje mi się trywialne. Kompatybilność z różnymi przeglądarkami, zależności cykliczne, debugowanie, czytelność i konserwacja itp. Zacznijmy więc jak najwcześniej :)
Stan
@Stan, myślę, że ta odpowiedź może być lepsza poprzez dodanie dalszych wyjaśnień na temat tego, o co chodzi w zapytaniu o element.
Pacerier
15

Odkryłem, że ta biblioteka działa, gdy rozwiązanie MarcJ nie:

https://github.com/sdecima/javascript-detect-element-resize

Jest bardzo lekki i wykrywa nawet naturalne zmiany rozmiaru za pomocą CSS lub po prostu ładowania / renderowania HTML.

Przykładowy kod (pobrany z linku):

<script type="text/javascript" src="detect-element-resize.js"></script>
<script type="text/javascript">
  var resizeElement = document.getElementById('resizeElement'),
      resizeCallback = function() {
          /* do something */
      };
  addResizeListener(resizeElement, resizeCallback);
  removeResizeListener(resizeElement, resizeCallback);
</script>
joshcomley
źródło
To jest to. 147 linii, wykorzystuje zdarzenia przewijania. Przeczytaj ten kod, jeśli chcesz uzyskać pełną i prawdziwą odpowiedź na temat tego, jak najlepiej wykrywać zmiany wymiarów div. Wskazówka: to nie jest „po prostu skorzystaj z tej biblioteki”.
Seph Reed,
1
Ludzie powinni spojrzeć na ten problem, zanim wypróbują tę bibliotekę.
Louis,
13

Spójrz na ten http://benalman.com/code/projects/jquery-resize/examples/resize/

Ma różne przykłady. Spróbuj zmienić rozmiar okna i zobacz, jak dopasowuje się elementy wewnątrz elementów kontenera.

Przykład z skrzypcami js, aby wyjaśnić, jak to działa.
Spójrz na to skrzypce http://jsfiddle.net/sgsqJ/4/

W tym zdarzeniu resize () jest powiązane z elementami posiadającymi klasę „test”, a także z obiektem okna i przy zmianie rozmiaru wywołanie zwrotne obiektu okna $ ('. Test'). Wywoływana jest funkcja resize ().

na przykład

$('#test_div').bind('resize', function(){
            console.log('resized');
});

$(window).resize(function(){
   $('#test_div').resize();
});
Bharat Patil
źródło
Ciekawy. To jsfiddle konsekwentnie powoduje awarię Chrome, gdy inspektor jest otwarty i klikam link „Dodaj więcej tekstu”. Firefox zgłasza błędy „za dużo rekurencji”.
EricP
3
Proszę nie używać wtyczki zmiany rozmiaru jQuery. Jest to naprawdę powolne i również niedokładne. Zobacz moją odpowiedź, aby uzyskać lepsze rozwiązanie.
Marc J. Schmidt
24
To NIE wykrywa bezpośrednio zmian wymiarów. to tylko produkt uboczny nasłuchiwania zdarzeń zmiany rozmiaru. zmiany wymiarów mogą zachodzić na wiele innych sposobów.
vsync
4
Co się stanie, jeśli zmiana rozmiaru div wynika ze zmiany stylu lub domu, a nie przez zmianę rozmiaru okna?
awe
9

Możesz użyć iframelub objectużyć contentWindowlub contentDocumentzmienić rozmiar. Bez setIntervallubsetTimeout

Kroki:

  1. Ustaw pozycję elementu na relative
  2. Dodaj wewnątrz przezroczystego absolutnie ukrytego IFRAME
  3. Słuchaj IFRAME.contentWindow- onresizewydarzenie

Przykład HTML:

<div style="height:50px;background-color:red;position:relative;border:1px solid red">
<iframe style=width:100%;height:100%;position:absolute;border:none;background-color:transparent allowtransparency=true>
</iframe>
This is my div
</div>

JavaScript:

$('div').width(100).height(100);
$('div').animate({width:200},2000);

$('object').attr({
    type : 'text/html'
})
$('object').on('resize,onresize,load,onload',function(){
    console.log('ooooooooonload')
})

$($('iframe')[0].contentWindow).on('resize',function(){
    console.log('div changed')
})

Przykład biegania

JsFiddle: https://jsfiddle.net/qq8p470d/


Zobacz więcej:

Aminadav Glickshtein
źródło
1
Uwaga dla tych, którzy nie używają jquery, IFRAME.contentWindownie jest dostępna, dopóki onloadzdarzenie nie zostanie uruchomione w ramce iframe. Możesz także dodać styl visibility:hidden;, ponieważ iframe może blokować wejście myszy do elementów znajdujących się za nim (wydaje się, że przynajmniej „unosi się” w IE).
James Wilkins
3
Chociaż to działa, jest to ciężkie rozwiązanie, którego narzutem jest tworzenie zupełnie nowego kontekstu przeglądania, aby obserwować zmianę rozmiaru. Ponadto w niektórych przypadkach zmiana displaywłaściwości elementu nie jest idealna ani wykonalna .
trusktr
zgodzili się, że to okropne rozwiązanie, jak stwierdzono powyżej
29
Jak napisano tak straszne rozwiązanie?
Aminadav Glickshtein
ta metoda działa dobrze z odtwarzaczami wideo i odtwarzaczami audio, które wymagają dynamicznej zmiany rozmiaru treści. Korzystam z tej metody, aby zmienić rozmiar wyjściowego pliku wavesurfer.js.
David Clews
8

Tylko obiekt okna generuje zdarzenie „zmiana rozmiaru”. Jedyny sposób, w jaki wiem, aby robić to, co chcesz, to uruchomić licznik interwałowy, który okresowo sprawdza rozmiar.

Pointy
źródło
7

var div = document.getElementById('div');
div.addEventListener('resize', (event) => console.log(event.detail));

function checkResize (mutations) {
    var el = mutations[0].target;
    var w = el.clientWidth;
    var h = el.clientHeight;
    
    var isChange = mutations
      .map((m) => m.oldValue + '')
      .some((prev) => prev.indexOf('width: ' + w + 'px') == -1 || prev.indexOf('height: ' + h + 'px') == -1);

    if (!isChange)
      return;

    var event = new CustomEvent('resize', {detail: {width: w, height: h}});
    el.dispatchEvent(event);
}

var observer = new MutationObserver(checkResize); 
observer.observe(div, {attributes: true, attributeOldValue: true, attributeFilter: ['style']});
#div {width: 100px; border: 1px solid #bbb; resize: both; overflow: hidden;}
<div id = "div">DIV</div>

Aikon Mogwai
źródło
7

Zadziwiające, jak stary jest ten problem, jest to nadal problem w większości przeglądarek.

Jak powiedzieli inni, Chrome 64+ jest teraz dostarczany z Resize Observes natywnie, jednak specyfikacja jest nadal dopracowywana, a Chrome jest obecnie (stan na 29.01.2019) za najnowszą edycją specyfikacji .

Widziałem kilka dobrych polifillów ResizeObserver na wolności, jednak niektóre nie są zgodne ze specyfikacją, a inne mają pewne problemy z obliczeniami.

Desperacko potrzebowałem tego zachowania, aby stworzyć responsywne komponenty internetowe, które można by wykorzystać w dowolnej aplikacji. Aby działały ładnie, muszą cały czas znać swoje wymiary, więc ResizeObservers brzmiało idealnie i postanowiłem stworzyć polifill, który byłby zgodny ze specyfikacją tak dokładnie, jak to możliwe.

Repo: https://github.com/juggle/resize-observer

Demo: https://codesandbox.io/s/myqzvpmmy9

Trem
źródło
Jest to zdecydowanie odpowiedź dla każdego, kto ma do czynienia z kompatybilnością przeglądarki IE11 +.
ekfuhrmann
3

Za pomocą Clay.js ( https://github.com/zzarcon/clay ) dość łatwo jest wykryć zmiany wielkości elementu:

var el = new Clay('.element');

el.on('resize', function(size) {
    console.log(size.height, size.width);
});
Zzarcon
źródło
7
Podaj odpowiednie ujawnienie, biorąc pod uwagę, że jest to Twoja biblioteka.
jhpratt GOFUNDME RELICENSING
To zadziałało dla mnie, gdzie ResizeSensor nie, łamie mój modalny styl. Dziękuję Zzarcon :)
Máxima Alekz
3

Ten post na blogu pomógł mi skutecznie wykryć zmiany rozmiaru elementów DOM.

http://www.backalleycoder.com/2013/03/18/cross-browser-event-based-element-resize-detection/

Jak korzystać z tego kodu ...

AppConfig.addResizeListener(document.getElementById('id'), function () {
  //Your code to execute on resize.
});

Kod spakowany użyty w przykładzie ...

var AppConfig = AppConfig || {};
AppConfig.ResizeListener = (function () {
    var attachEvent = document.attachEvent;
    var isIE = navigator.userAgent.match(/Trident/);
    var requestFrame = (function () {
        var raf = window.requestAnimationFrame || window.mozRequestAnimationFrame || window.webkitRequestAnimationFrame ||
            function (fn) { return window.setTimeout(fn, 20); };
        return function (fn) { return raf(fn); };
    })();

    var cancelFrame = (function () {
        var cancel = window.cancelAnimationFrame || window.mozCancelAnimationFrame || window.webkitCancelAnimationFrame ||
               window.clearTimeout;
        return function (id) { return cancel(id); };
    })();

    function resizeListener(e) {
        var win = e.target || e.srcElement;
        if (win.__resizeRAF__) cancelFrame(win.__resizeRAF__);
        win.__resizeRAF__ = requestFrame(function () {
            var trigger = win.__resizeTrigger__;
            trigger.__resizeListeners__.forEach(function (fn) {
                fn.call(trigger, e);
            });
        });
    }

    function objectLoad(e) {
        this.contentDocument.defaultView.__resizeTrigger__ = this.__resizeElement__;
        this.contentDocument.defaultView.addEventListener('resize', resizeListener);
    }

    AppConfig.addResizeListener = function (element, fn) {
        if (!element.__resizeListeners__) {
            element.__resizeListeners__ = [];
            if (attachEvent) {
                element.__resizeTrigger__ = element;
                element.attachEvent('onresize', resizeListener);
            } else {
                if (getComputedStyle(element).position === 'static') element.style.position = 'relative';
                var obj = element.__resizeTrigger__ = document.createElement('object');
                obj.setAttribute('style', 'display: block; position: absolute; top: 0; left: 0; height: 100%; width: 100%; overflow: hidden; pointer-events: none; z-index: -1;');
                obj.__resizeElement__ = element;
                obj.onload = objectLoad;
                obj.type = 'text/html';
                if (isIE) element.appendChild(obj);
                obj.data = 'about:blank';
                if (!isIE) element.appendChild(obj);
            }
        }
        element.__resizeListeners__.push(fn);
    };

    AppConfig.removeResizeListener = function (element, fn) {
        element.__resizeListeners__.splice(element.__resizeListeners__.indexOf(fn), 1);
        if (!element.__resizeListeners__.length) {
            if (attachEvent) element.detachEvent('onresize', resizeListener);
            else {
                element.__resizeTrigger__.contentDocument.defaultView.removeEventListener('resize', resizeListener);
                element.__resizeTrigger__ = !element.removeChild(element.__resizeTrigger__);
            }
        }
    }
})();

Uwaga: AppConfig to przestrzeń nazw / obiekt, którego używam do organizowania funkcji wielokrotnego użytku. Wyszukaj i zastąp nazwę dowolną nazwą.

Ben Gripka
źródło
3

Moja wtyczka jQuery umożliwia zdarzenie „zmiana rozmiaru” dla wszystkich elementów, nie tylko window.

https://github.com/dustinpoissant/ResizeTriggering

$("#myElement") .resizeTriggering().on("resize", function(e){
  // Code to handle resize
}); 
Dustin Poissant
źródło
1
Twój link wskazywał na lokalizację, która już nie istnieje. Poszukałem nazwy wtyczki i znalazłem stronę, która odpowiada Twojemu opisowi. Ponieważ wydaje się, że jesteś autorem wtyczki, edytowałem również język Twojej odpowiedzi, aby wyraźnie zaznaczyć, że to twoja praca. Reguły tej witryny są takie, że kiedy piszesz odpowiedzi, zalecając użycie czegoś, co opracowałeś, musisz wyraźnie określić związek.
Louis
Nie jest to świetne rozwiązanie, ponieważ uruchamiasz jakąś funkcję w setTimer, co oznacza, że ​​przeglądarka stale pali procesor, nawet jeśli nic się nie dzieje. Właściciele laptopów i telefonów komórkowych czerpią korzyści z rozwiązania wykorzystującego zdarzenia przeglądarki tylko wtedy, gdy ma miejsce jakieś działanie, na przykład użycie zdarzenia onScroll.
Roman Zenka
2

Możesz wypróbować kod z poniższego fragmentu, który zaspokoi Twoje potrzeby za pomocą zwykłego javascript. ( uruchom fragment kodu i kliknij link do pełnej strony, aby wyświetlić alert o zmianie rozmiaru div, jeśli chcesz go przetestować ).

Na podstawie faktu, że jest to setInterval 100 milisekund, odważyłbym się powiedzieć, że mój komputer nie uważał, że jest zbyt obciążony procesorem. (0,1% procesora wykorzystano jako całość dla wszystkich otwartych kart w Chrome w czasie testowania). Ale z drugiej strony jest to tylko jeden div, jeśli chcesz to zrobić dla dużej liczby elementów, to tak, może to być bardzo wymagające procesora.

Zawsze możesz użyć zdarzenia kliknięcia, aby mimo to zatrzymać wąchanie div-resize .

var width = 0; 
var interval = setInterval(function(){
if(width <= 0){
width = document.getElementById("test_div").clientWidth;
} 
if(document.getElementById("test_div").clientWidth!==width) {   
  alert('resized div');
  width = document.getElementById("test_div").clientWidth;
}    

}, 100);
<div id="test_div" style="width: 100%; min-height: 30px; border: 1px dashed pink;">
        <input type="button" value="button 1" />
        <input type="button" value="button 2" />
        <input type="button" value="button 3" />
</div>

Możesz sprawdzić skrzypce także

AKTUALIZACJA

var width = 0; 
function myInterval() {
var interval = setInterval(function(){
if(width <= 0){
width = document.getElementById("test_div").clientWidth;
} 
if(document.getElementById("test_div").clientWidth!==width) {   
  alert('resized');
  width = document.getElementById("test_div").clientWidth;
}    

}, 100);
return interval;
}
var interval = myInterval();
document.getElementById("clickMe").addEventListener( "click" , function() {
if(typeof interval!=="undefined") {
clearInterval(interval);
alert("stopped div-resize sniffing");
}
});
document.getElementById("clickMeToo").addEventListener( "click" , function() {
myInterval();
alert("started div-resize sniffing");
});
<div id="test_div" style="width: 100%; min-height: 30px; border: 1px dashed pink;">
        <input type="button" value="button 1" id="clickMe" />
        <input type="button" value="button 2" id="clickMeToo" />
        <input type="button" value="button 3" />
</div>

Zaktualizowano skrzypce

Peter Darmis
źródło
2

Jest to właściwie dokładna kopia najważniejszej odpowiedzi, ale zamiast linku liczy się tylko część kodu, przetłumaczona na IMO, bardziej czytelna i łatwiejsza do zrozumienia. Kilka innych drobnych zmian obejmuje użycie cloneNode () i niewprowadzanie html do łańcucha js. Małe rzeczy, ale możesz skopiować i wkleić to tak, jak jest i będzie działać.

Jego działanie polega na tym, aby dwa niewidoczne div wypełniały oglądany element, a następnie umieszczając w nich wyzwalacz i ustawiając pozycję przewijania, która spowoduje zmianę przewijania, jeśli zmieni się rozmiar.

Prawdziwe uznanie należy się Marcowi J, ale jeśli szukasz odpowiedniego kodu, oto on:

    window.El = {}

    El.resizeSensorNode = undefined;
    El.initResizeNode = function() {
        var fillParent = "display: block; position: absolute; left: 0; top: 0; right: 0; bottom: 0; overflow: hidden; z-index: -1; visibility: hidden;";
        var triggerStyle = "position: absolute; left: 0; top: 0; transition: 0s;";

        var resizeSensor = El.resizeSensorNode = document.createElement("resizeSensor");
        resizeSensor.style = fillParent;

        var expandSensor = document.createElement("div");
        expandSensor.style = fillParent;
        resizeSensor.appendChild(expandSensor);

        var trigger = document.createElement("div");
        trigger.style = triggerStyle;
        expandSensor.appendChild(trigger);

        var shrinkSensor = expandSensor.cloneNode(true);
        shrinkSensor.firstChild.style = triggerStyle + " width: 200%; height: 200%";
        resizeSensor.appendChild(shrinkSensor);
    }


    El.onSizeChange = function(domNode, fn) {
        if (!domNode) return;
        if (domNode.resizeListeners) {
            domNode.resizeListeners.push(fn);
            return;
        }

        domNode.resizeListeners = [];
        domNode.resizeListeners.push(fn);

        if(El.resizeSensorNode == undefined)
            El.initResizeNode();

        domNode.resizeSensor = El.resizeSensorNode.cloneNode(true);
        domNode.appendChild(domNode.resizeSensor);

        var expand = domNode.resizeSensor.firstChild;
        var expandTrigger = expand.firstChild;
        var shrink = domNode.resizeSensor.childNodes[1];

        var reset = function() {
            expandTrigger.style.width = '100000px';
            expandTrigger.style.height = '100000px';

            expand.scrollLeft = 100000;
            expand.scrollTop = 100000;

            shrink.scrollLeft = 100000;
            shrink.scrollTop = 100000;
        };

        reset();

        var hasChanged, frameRequest, newWidth, newHeight;
        var lastWidth = domNode.offsetWidth;
        var lastHeight = domNode.offsetHeight;


        var onResized = function() {
            frameRequest = undefined;

            if (!hasChanged) return;

            lastWidth = newWidth;
            lastHeight = newHeight;

            var listeners = domNode.resizeListeners;
            for(var i = 0; listeners && i < listeners.length; i++) 
                listeners[i]();
        };

        var onScroll = function() {
            newWidth = domNode.offsetWidth;
            newHeight = domNode.offsetHeight;
            hasChanged = newWidth != lastWidth || newHeight != lastHeight;

            if (hasChanged && !frameRequest) {
                frameRequest = requestAnimationFrame(onResized);
            }

            reset();
        };


        expand.addEventListener("scroll", onScroll);
        shrink.addEventListener("scroll", onScroll);
    }
Seph Reed
źródło
1

Czyste rozwiązanie JavaScript, ale działa tylko wtedy, gdy rozmiar elementu zmienia się za pomocą przycisku zmiany rozmiaru css :

  1. przechowuj rozmiar elementu z offsetWidth i offsetHeight ;
  2. dodaj detektor zdarzeń onclick do tego elementu;
  3. gdy uruchomiony, porównaj curent offsetWidthi offsetHeightz zapisanymi wartościami, a jeśli się różnią, to, co chcesz i zaktualizować te wartości.
roipoussiere
źródło
1

Oto uproszczona wersja rozwiązania @nkron, mająca zastosowanie do pojedynczego elementu (zamiast tablicy elementów w odpowiedzi @ nkron, złożoność, której nie potrzebowałem).

function onResizeElem(element, callback) {    
  // Save the element we are watching
  onResizeElem.watchedElementData = {
    element: element,
    offsetWidth: element.offsetWidth,
    offsetHeight: element.offsetHeight,
    callback: callback
  };

  onResizeElem.checkForChanges = function() {
    const data = onResizeElem.watchedElementData;
    if (data.element.offsetWidth !== data.offsetWidth || data.element.offsetHeight !== data.offsetHeight) {
      data.offsetWidth = data.element.offsetWidth;
      data.offsetHeight = data.element.offsetHeight;
      data.callback();
    }
  };

  // Listen to the window resize event
  window.addEventListener('resize', onResizeElem.checkForChanges);

  // Listen to the element being checked for width and height changes
  onResizeElem.observer = new MutationObserver(onResizeElem.checkForChanges);
  onResizeElem.observer.observe(document.body, {
    attributes: true,
    childList: true,
    characterData: true,
    subtree: true
  });
}

Detektor i obserwator zdarzeń można usunąć poprzez:

window.removeEventListener('resize', onResizeElem.checkForChanges);
onResizeElem.observer.disconnect();
Gman
źródło
0
jQuery(document).ready( function($) {

function resizeMapDIVs() {

// check the parent value...

var size = $('#map').parent().width();



if( $size < 640 ) {

//  ...and decrease...

} else {

//  ..or increase  as necessary

}

}

resizeMapDIVs();

$(window).resize(resizeMapDIVs);

});
Fábio Zangirolami
źródło
0

używając odpowiedzi Bharat Patil po prostu zwróć false w wywołaniu zwrotnym powiązania, aby uniknąć błędu maksymalnego stosu, patrz przykład poniżej:

$('#test_div').bind('resize', function(){
   console.log('resized');
   return false;
});
biały Królik
źródło
0

To naprawdę stare pytanie, ale pomyślałem, że opublikuję moje rozwiązanie tego problemu.

Próbowałem użyć, ResizeSensorponieważ wszyscy wydawali się mieć dość dużą sympatię. Po wdrożeniu uświadomiłem sobie, że pod maską element zapytania wymaga, aby dany element miał pozycję relativelubabsolute zastosował się do niego, co nie działało w mojej sytuacji.

Skończyło się to na Rxjs intervalzamiast na prostych setTimeoutlub requestAnimationFramepodobnych wcześniejszych implementacjach.

Co jest miłego w obserwowalnym smaku interwału, to fakt, że możesz zmodyfikować strumień, jednak każdy inny obserwowalny może być obsługiwany. Dla mnie wystarczyła podstawowa implementacja, ale można było zwariować i wykonywać różnego rodzaju scalenia itp.

W poniższym przykładzie śledzę zmiany szerokości wewnętrznej div (zielonej). Ma szerokość ustawioną na 50%, ale maksymalną szerokość 200 pikseli. Przeciągnięcie suwaka wpływa na szerokość otoku otoki (szary). Widać, że obserwowalny jest uruchamiany tylko wtedy, gdy zmienia się szerokość wewnętrznego div, co dzieje się tylko wtedy, gdy szerokość div zewnętrznego jest mniejsza niż 400 pikseli.

const { interval } = rxjs;
const { distinctUntilChanged, map, filter } = rxjs.operators;


const wrapper = document.getElementById('my-wrapper');
const input = document.getElementById('width-input');




function subscribeToResize() {
  const timer = interval(100);
  const myDiv = document.getElementById('my-div');
  const widthElement = document.getElementById('width');
  const isMax = document.getElementById('is-max');
  
  /*
    NOTE: This is the important bit here 
  */
  timer
    .pipe(
      map(() => myDiv ? Math.round(myDiv.getBoundingClientRect().width) : 0),
      distinctUntilChanged(),
      // adding a takeUntil(), here as well would allow cleanup when the component is destroyed
    )
      .subscribe((width) => {        
        widthElement.innerHTML = width;
        isMax.innerHTML = width === 200 ? 'Max width' : '50% width';

      });
}

function defineRange() {
  input.min = 200;
  input.max = window.innerWidth;
  input.step = 10;
  input.value = input.max - 50;
}

function bindInputToWrapper() {
  input.addEventListener('input', (event) => {
    wrapper.style.width = `${event.target.value}px`;
  });
}

defineRange();
subscribeToResize();
bindInputToWrapper();
.inner {
  width: 50%;
  max-width: 200px;
}




/* Aesthetic styles only */

.inner {
  background: #16a085;
}

.wrapper {
  background: #ecf0f1;
  color: white;
  margin-top: 24px;
}

.content {
  padding: 12px;
}

body {
  
  font-family: sans-serif;
  font-weight: bold;
}
<script src="https://unpkg.com/rxjs/bundles/rxjs.umd.min.js"></script>

<h1>Resize Browser width</h1>

<label for="width-input">Adjust the width of the wrapper element</label>
<div>
  <input type="range" id="width-input">
</div>

<div id="my-wrapper" class="wrapper">
  <div id="my-div" class="inner">
    <div class="content">
      Width: <span id="width"></span>px
      <div id="is-max"></div>  
    </div>
  </div>
</div>

Scrimothy
źródło
-1

Window.onResizeIstnieje tylko w specyfikacji, ale zawsze możesz użyć IFramedo wygenerowania nowego Windowobiektu w swoim DIV.

Sprawdź tę odpowiedź . Nowa mała wtyczka jquery jest przenośna i łatwa w użyciu. Zawsze możesz sprawdzić kod źródłowy, aby zobaczyć, jak to się robi.

<!-- (1) include plugin script in a page -->
<script src="/src/jquery-element-onresize.js"></script>

// (2) use the detectResizing plugin to monitor changes to the element's size:
$monitoredElement.detectResizing({ onResize: monitoredElement_onResize });

// (3) write a function to react on changes:
function monitoredElement_onResize() {    
    // logic here...
}
rbtbar
źródło
Jest to wysoce nieskuteczne rozwiązanie, jeśli masz divy o zmiennym rozmiarze.
suricactus,
-1

myślałem, że nie da się tego zrobić, ale potem pomyślałem o tym, możesz ręcznie zmienić rozmiar div za pomocą style = "resize: zarówno;" aby to zrobić, kliknij go, więc dodałeś funkcję onclick, aby sprawdzić wysokość i szerokość elementu i zadziałało. Tylko 5 linii czystego javascript (na pewno może być nawet krótszy) http://codepen.io/anon/pen/eNyyVN

<div id="box" style="
                height:200px; 
                width:640px;
                background-color:#FF0066;
                resize: both;
                overflow: auto;" 
                onclick="myFunction()">
    <p id="sizeTXT" style="
                font-size: 50px;">
       WxH
    </p>
    </div>

<p>This my example demonstrates how to run a resize check on click for resizable div.</p>

<p>Try to resize the box.</p>


<script>
function myFunction() {
var boxheight = document.getElementById('box').offsetHeight;
var boxhwidth = document.getElementById('box').offsetWidth;
var txt = boxhwidth +"x"+boxheight;
document.getElementById("sizeTXT").innerHTML = txt;
}
</script>
PixelPimp
źródło
2
Oblicza się to tylko po uruchomieniu zdarzenia kliknięcia, które nie jest częścią wymagania pytania.
Rob Evans