javascript - Cum să detectezi când dimensiunea unui DIV se schimbă?


Am următorul exemplu de HTML, există un DIV care are lățimea de 100%. Conține câteva elemente. În timpul redimensionării ferestrei, elementele interne pot fi repoziționate, iar dimensiunea div-ului se poate schimba. Întreb dacă este posibil să se atașeze un eveniment pentru schimbarea dimensiunii div-ului și cum se poate face asta? În prezent, atașez funcția callback la evenimentul jQuery resize pe DIV-ul țintă, însă nu apare niciun log în consolă, vezi mai jos:

<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>

Răspuns #1

Un standard mai nou pentru aceasta este API-ul Resize Observer, cu suport bun în browsere.

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>

Observator de redimensionare

Documentație: https://developer.mozilla.org/en-US/docs/Web/API/Resize_Observer_API

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

Suport actual: http://caniuse.com/#feat=resizeobserver

Polyfills: https://github.com/pelotoncycle/resize-observer https://github.com/que-etc/resize-observer-polyfill https://github.com/juggle/resize-observer

Răspunsul #2

Există o metodă foarte eficientă de a determina dacă dimensiunea unui element a fost schimbată.

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

Această bibliotecă are o clasă ResizeSensor care poate fi utilizată pentru detectarea redimensionării.
Folosește o abordare bazată pe evenimente, astfel că este extrem de rapidă și nu irosește timpul CPU.

Exemplu :

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

Vă rugăm să nu folosiți pluginul jQuery onresize deoarece utilizează setTimeout() în combinație cu citirea proprietăților DOM clientHeight/clientWidth într-un loop pentru a verifica modificările.
Acesta este incredibil de lent și inexact deoarece provoacă perturbarea layout-ului.

Dezvăluire: Sunt direct asociat cu această bibliotecă.

Răspunsul #3

Pe termen lung, vei putea folosi ResizeObserver.

new ResizeObserver(callback).observe(element);

Din păcate, în prezent nu este acceptat implicit de multe browsere.

Între timp, poți folosi o funcție precum cea următoare. Deoarece majoritatea schimbărilor de dimensiune a elementelor vor proveni din redimensionarea ferestrei sau din schimbarea a ceva în DOM. Poți asculta redimensionarea ferestrei cu evenimentul resize al ferestrei și poți asculta schimbările din DOM folosind MutationObserver.

Iată un exemplu de funcție care te va suna înapoi atunci când dimensiunea elementului furnizat se schimbă ca rezultat al oricăruia dintre aceste evenimente:

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

Răspunsul #4

NU recomand trucul setTimeout() deoarece încetinește performanța! În schimb, puteți folosi metoda DOM ResizeObserver pentru a asculta schimbările de dimensiune ale Div-ului.

const myObserver = new ResizeObserver(entries => {
 // this will get called whenever div dimension changes
  entries.forEach(entry => {
    console.log('width', entry.contentRect.width);
    console.log('height', entry.contentRect.height);
  });
});

const someEl = document.querySelector('.some-element');

// start listening to changes
myObserver.observe(someEl);

// later, stop listening to changes
myObserver.disconnect();

Răspuns vechi folosind MutationObserver:

Pentru ascultarea atributelor elementelor HTML, a subarborelui și a modificărilor de clasă:

JS :

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

HTML :

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

Iată fiddle-ul.. Încearcă să schimbi dimensiunea div-ului.


Poți să înfășori în continuare metoda ta în metoda debounce pentru a îmbunătăți eficiența. debounce va declanșa metoda ta la fiecare x milisecunde în loc să o declanșeze la fiecare milisecundă în care DIV-ul este redimensionat.

Răspunsul #5

ResizeSensor.js face parte dintr-o bibliotecă uriașă, dar i-am redus funcționalitatea la ASTA:

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

Cum să îl folosești:

let container = document.querySelector(".container");
new ResizeSensor(container, function()
{
    console.log("dimension changed:", container.clientWidth, container.clientHeight);
});

Răspunsul #6

Trebuie să asociezi evenimentul resize obiectului window, nu unui element generic HTML.

Ai putea apoi să folosești asta:

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

și în cadrul funcției de callback poți verifica noua lățime a div-ului tău apelând

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

Deci, răspunsul la întrebarea ta este nu, nu poți să atașezi evenimentul resize la un div.

Răspunsul #7

Cea mai bună soluție ar fi să folosiți așa-numitele Element Queries. Totuși, acestea nu sunt standard, nu există nicio specificație - și singura opțiune este să folosiți unul dintre polyfill-urile/bibliotecile disponibile, dacă doriți să mergeți pe această cale.

Ideea din spatele interogărilor de elemente este de a permite unui anumit container de pe pagină să răspundă la spațiul care i se oferă. Aceasta va permite să scrii un component o singură dată și apoi să-l plasezi oriunde pe pagină, în timp ce își va ajusta conținutul în funcție de dimensiunea sa actuală. Indiferent de dimensiunea ferestrei. Aceasta este prima diferență pe care o observăm între interogările de elemente și interogările media. Toată lumea speră că la un moment dat va fi creată o specificație care să standardizeze interogările de elemente (sau ceva care să atingă același scop) și să le facă native, curate, simple și robuste. Majoritatea oamenilor sunt de acord că interogările media sunt destul de limitate și nu ajută la designul modular și la adevărata responsabilitate.

Există câteva polyfill-uri/biblioteci care rezolvă problema în moduri diferite (ar putea fi numite mai degrabă soluții temporare decât soluții propriu-zise):

  • Interogări CSS pentru Elemente - https://github.com/marcj/css-element-queries
  • BoomQueries - https://github.com/BoomTownROI/boomqueries
  • eq.js - https://github.com/Snugug/eq.js
  • ElementQuery - https://github.com/tysonmatanich/elementQuery
  • Și câteva altele, pe care nu le voi lista aici, dar ești liber să le cauți. Nu aș putea să spun care dintre opțiunile disponibile în prezent este cea mai bună. Va trebui să încerci câteva și să decizi.

Am văzut alte soluții propuse pentru probleme similare. De obicei, acestea folosesc contoare de timp sau dimensiunea ferestrei/viewport-ului în spate, ceea ce nu este o soluție reală. În plus, cred că ideal ar fi ca aceasta să fie rezolvată în principal în CSS, și nu în javascript sau html.

Răspunsul #8

Am descoperit că această bibliotecă funcționează atunci când soluția lui MarcJ nu a funcționat:

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

Este foarte ușor și detectează chiar și redimensionările naturale prin CSS sau pur și simplu încărcarea/randarea HTML.

Exemplu de cod (preluat din link):

<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>

Răspunsul #9

Aruncă o privire la asta http://benalman.com/code/projects/jquery-resize/examples/resize/

Are diverse exemple. Încearcă să redimensionezi fereastra și vezi cum elementele din interiorul containerelor se ajustează.

Exemplu cu js fiddle pentru a explica cum să funcționeze.
Aruncă o privire la acest fiddle http://jsfiddle.net/sgsqJ/4/

În acel eveniment resize() este legat de elementele care au clasa „test” și, de asemenea, de obiectul window, iar în callback-ul resize al obiectului window se apelează $ ('.test').resize().

de ex.

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

$(window).resize(function(){
   $('#test_div').resize();
});

Răspuns #10

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>