Wie erkennt man, dass sich die Dimension von DIV geändert hat?

351

Ich habe das folgende Beispiel-HTML, es gibt einen DIV, der 100% Breite hat. Es enthält einige Elemente. Während der Größenänderung von Fenstern können die inneren Elemente neu positioniert werden, und die Abmessung des Div kann sich ändern. Ich frage, ob es möglich ist, das Dimensionsänderungsereignis des Div zu verknüpfen. und wie geht das? Ich binde derzeit die Rückruffunktion an das jQuery-Größenänderungsereignis auf dem Ziel-DIV. Es wird jedoch kein Konsolenprotokoll ausgegeben, siehe unten:

Vor dem Ändern der Größe Geben Sie hier die Bildbeschreibung ein

<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
quelle
7
Dies funktioniert nicht, da Sie das Größenänderungsereignis an das angegebene div-Element binden. Das Größenänderungsereignis wird jedoch für das Fenster ausgelöst, nicht für Ihr Element.
Fatih Acet
Sie könnten setIntervalhier als mögliche Lösung verwenden. Sie können jederzeit clearIntervalan einen Knopfdruck binden , um die Schleife zu stoppen, sobald Ihre Arbeit erledigt ist.
Peter Darmis
1
Seit 2020 funktioniert ResizeObserver in allen gängigen Browsern (Chrome, Safari, Firefox, Edge) mit Ausnahme des IE. caniuse.com/#feat=resizeobserver
Dan

Antworten:

264

Es gibt eine sehr effiziente Methode, um festzustellen, ob die Größe eines Elements geändert wurde.

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

Diese Bibliothek verfügt über eine Klasse ResizeSensor, die zur Größenänderungserkennung verwendet werden kann.
Es verwendet einen ereignisbasierten Ansatz, ist also verdammt schnell und verschwendet keine CPU-Zeit.

Beispiel:

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

Verwenden Sie das jQuery onresize-Plugin nicht, da es setTimeout()in Kombination mit dem Lesen des DOM clientHeight/ der clientWidthEigenschaften in einer Schleife verwendet wird, um nach Änderungen zu suchen .
Dies ist unglaublich langsam und ungenau, da es zu Layout-Thrashing führt .

Offenlegung: Ich bin direkt mit dieser Bibliothek verbunden.

Marc J. Schmidt
quelle
8
@jake, IE11-Unterstützung ist jetzt implementiert.
Marc J. Schmidt
15
@Aletheios sollten Sie genauer hinsehen. requestAnimationFrame ist nicht für die langsame Dimensionserkennung gedacht, sondern genau das Gegenteil: Es glättet alle extremen Ereignisse, die vom realen ereignisbasierten Größenänderungssensor ausgelöst werden, um CPU-Zeit zu sparen. Siehe aktualisierte Dokumente unter github.com/marcj/css-element-queries/blob/master/src/…
Marc J. Schmidt
6
@Aletheios Ich denke, Sie verpassen einen Punkt: setTimeout ist nicht nur ungenau, sondern auch höllisch langsam. Ein requestAnimationFrame, der nach einem einfachen Booleschen Wert sucht, ist um Größenordnungen schneller als ein setTimeout, der ständig DOM-Eigenschaften überprüft. Daneben wird setTimeout auch alle paar Millisekunden aufgerufen.
Marc J. Schmidt
9
@Orwellophile du hast offensichtlich nichts verstanden. Hören Sie auf, Antworten herunterzustimmen, wenn Sie keine Ahnung haben, wie es intern funktioniert. Nochmals: css-element-query: Verwendet ein echtes Größenänderungsereignis, das jedes Mal ausgelöst wird, wenn sich die Dimension ändert. Da Sie zu viele Ereignisse erhalten würden (z. B. für Drag'n'Drop), wurde requestAnimationFrame hinzugefügt, um nach einer einfachen booleschen Variablen zu suchen und diese ausgelösten Ereignisse auf 1 / Frame zu glätten. jquery plugin: Verwendet ein setTimeout, das ständig DOM-Layout-Requisiten (clientHeight usw.) überprüft, wobei jede Überprüfung sehr langsam ist, aber häufig überprüft werden muss, um dieselbe Funktionalität zu haben.
Marc J. Schmidt
9
@Aletheios, @Orwellophile und alle, die von ihnen irregeführt wurden: requestAnimationFramevs setTimeoutist irrelevant, diese Bibliothek führt keine Schleife durch . requestAnimationFramewird nur zum Entprellen / Drosseln der Häufigkeit von Rückrufen verwendet, es wird nicht abgefragt . MutationObserver könnte theoretisch ähnlich verwendet werden, jedoch nicht in älteren Browsern. Das ist außerordentlich klug.
Han Seoul-Oh
251

Ein neuer Standard hierfür ist die Resize Observer-API, die in Chrome 64 verfügbar ist.

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>

Größe des Beobachters ändern

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

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

Firefox-Problem: https://bugzil.la/1272409

Safari-Problem: http://wkb.ug/157743

Aktueller Support: http://caniuse.com/#feat=resizeobserver

Daniel Herr
quelle
3
Für Produktionsstandorte noch nicht nützlich, da es derzeit nur von Chrome unterstützt wird. Aber definitiv die beste Lösung hier! Übrigens, hier ist ein kurzer Überblick über den Implementierungsstatus: chromestatus.com/feature/5705346022637568
Philipp
8
@Philipp Es gibt eine Polyfüllung.
Daniel Herr
2
Es scheint, dass es immer noch eine experimentelle Funktion in allen Browsern ist. Caniuse.com/#feat=resizeobserver
Francesc Rosas
5
Selbst 2 Jahre nach dieser Antwort ist die Browserunterstützung düster: caniuse.com/#search=resizeobserver
Scrimothy
2
Funktioniert wie ein Zauber in den neuesten Chrome und FF.
Klemen Tušar
32

Sie müssen das resizeEreignis an das bindenwindow Objekt , nicht an ein generisches HTML-Element.

Sie könnten dann Folgendes verwenden:

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

und innerhalb der Rückruffunktion können Sie die neue Breite Ihres divAnrufs überprüfen

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

Die Antwort auf Ihre Frage lautet also nein . Sie können das resizeEreignis nicht an ein div binden .

Alessandro Vendruscolo
quelle
124
Diese Antwort ist nicht gut genug. Größenänderungen können auftreten, ohne dass die Größe eines Fensters geändert wird. überhaupt.
vsync
9
Zum Beispiel, wenn Sie ein Splitter-Element haben oder nur Text oder anderen Inhalt an ein Element anhängen.
Günter Zöchbauer
@anu nicht wahr, Sie können zB DOM-Änderungen über Javascript vornehmen lassen, wodurch CSS auf neue Strukturen angewendet wird, was zu einer anderen Layoutgröße jedes Containers führt.
Antoniossss
29

Langfristig können Sie die ResizeObserver verwenden .

new ResizeObserver(callback).observe(element);

Leider wird es derzeit in vielen Browsern nicht standardmäßig unterstützt.

In der Zwischenzeit können Sie folgende Funktionen verwenden. Da die meisten Änderungen der Elementgröße auf die Größenänderung des Fensters oder auf Änderungen im DOM zurückzuführen sind. Sie können Fenster hören mit dem Fenster der Größe ändern Resize - Ereignis und Sie können hören DOM ändert MutationObserver .

Hier ist ein Beispiel für eine Funktion, die Sie zurückruft, wenn sich die Größe des bereitgestellten Elements aufgrund eines dieser Ereignisse ändert:

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
quelle
1
Leider scheint MutationObserver keine Änderungen im Shadow DOM erkennen zu können.
Ajedi32
@nkron, Wenn Sie die CSS3-Größenänderung verwenden , kann die Größenänderung leider auftreten, wenn der Benutzer die Größe manuell ändert. Dieses Ereignis kann nicht abgefangen werden.
Pacerier
@ Ajedi32 Ja, aber Sie können MutationObserver im ShadowDOM verwenden. Ich meine, anstatt das bodywie in diesem Beispiel zu sehen, können Sie ein Element direkt ansehen. Das ist tatsächlich performanter und effizienter als den ganzen Körper zu beobachten.
Trusktr
@ Ajedi32 Nun, das einzige Problem ist, wenn der Shadow DOM-Baum geschlossen ist, können Sie nicht auf die Interna zugreifen, aber ich denke nicht, dass Sie dies normalerweise tun müssen und wenn Sie es beobachten müssen Bei einem solchen geschlossenen Baum kann es dann zu einem Problem bei der Konstruktion kommen. Im Idealfall sollten Sie nur Ihre eigenen Elemente beobachten müssen, auf die Sie Zugriff haben. Eine Shadow-DOM-Komponente, bei der Sie die interne Größe beachten müssen, ist möglicherweise nicht richtig entworfen. Haben Sie ein konkretes Beispiel? Wenn ja, kann ich vielleicht helfen, eine Lösung vorzuschlagen (weil ich auch daran interessiert bin, aber noch kein Beispiel habe).
Trusktr
@trusktr Ich habe momentan kein MCVE, aber im Grunde würde jedes Shadow DOM-Element, das seine Größe aufgrund von Benutzereingaben ändern kann, ausreichen, um dieses Problem auszulösen. Ein Feld, das beispielsweise beim Klicken um einige zusätzliche Informationen erweitert wird. Beachten Sie, dass ich die Seite und nicht das einzelne Element beobachte, da ich unabhängig vom Grund automatisch nach unten scrollen möchte, wenn die Größe der Seite zunimmt. Das Erweitern eines Shadow DOM-Elements ist nur einer von vielen möglichen Gründen, warum die Seite möglicherweise länger wird.
Ajedi32
24

ResizeSensor.js ist Teil einer großen Bibliothek, aber ich reduzierte seine Funktionalität zu THIS :

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

Wie man es benutzt:

let container = document.querySelector(".container");
new ResizeSensor(container, function()
{
    console.log("dimension changed:", container.clientWidth, container.clientHeight);
});
Martin Wantke
quelle
Dies ist, was Diagramme js tun.
Antoniossss
Danke, dass du das komprimiert hast. Ich werde es ausprobieren :)
Tony K.
In diesem Ausdruck fehlt etwasparseInt( getComputedStyle(element) )
GetFree
2
Ich bin verwirrt, wie ist "let zIndex = parseInt (getComputedStyle (Element));" gültig? Ich nehme an, Sie meinten: "let zIndex = parseInt (getComputedStyle (Element) .zIndex);
Ryan Badour
1
Die obige Lösung / der obige Code erkennt nicht alle Größenänderungsereignisse (z. B. wenn die Größe eines Containers auf einen festen Wert geändert wird , wird die Größenänderung für untergeordnete Ereignisse nicht erkannt . Siehe jsfiddle.net/8md2rfsy/1 ). Die akzeptierte Antwort funktioniert (kommentieren Sie die entsprechenden Zeilen aus).
NewBee
21

Ich empfehle setTimeout()KEINEN Hack, da dies die Leistung verlangsamt! Stattdessen können Sie DOM-Mutationsbeobachter verwenden, um die Änderung der Div-Größe zu verfolgen.

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>

Hier ist die Geige
Versuchen Sie, die Div-Größe zu ändern.


Sie können Ihre Methode weiter in die debounceMethode einbinden, um die Effizienz zu verbessern. debouncelöst Ihre Methode alle x Millisekunden aus, anstatt jede Millisekunde auszulösen, bei der die Größe des DIV geändert wird.

JerryGoyal
quelle
9
Das einzige Problem bei diesem ist, dass es keine Änderung meldet, es sei denn, dem Element passiert tatsächlich eine Mutation. Wenn die Größe des Elements geändert wird, weil andere Elemente hinzugefügt wurden, funktioniert dies nicht.
Tony K.
Ich stimme @TonyK zu. Dies ist mein einziges Problem mit MutationObserver und jetzt suche ich nach einer Bibliothek
zur Größenänderung von
@JerryGoyal Es funktioniert nicht, wenn das div das <img> -Tag enthält. In der ersten Instanz wird das Bild nicht geladen und die Div-Größe wird auf 0 gesetzt. Nach dem Laden wird die Div-Größe geändert, aber der Beobachter wird nicht ausgelöst
Santhosh
15

Die beste Lösung wäre die Verwendung der sogenannten Elementabfragen . Sie sind jedoch nicht Standard, es gibt keine Spezifikation - und die einzige Option besteht darin, eine der verfügbaren Polyfüllungen / Bibliotheken zu verwenden, wenn Sie diesen Weg gehen möchten.

Die Idee hinter Elementabfragen besteht darin, einem bestimmten Container auf der Seite zu ermöglichen, auf den dafür bereitgestellten Speicherplatz zu reagieren. Auf diese Weise können Sie eine Komponente einmal schreiben und dann an einer beliebigen Stelle auf der Seite ablegen, während der Inhalt an die aktuelle Größe angepasst wird. Egal wie groß das Fenster ist. Dies ist der erste Unterschied zwischen Elementabfragen und Medienabfragen. Jeder hofft, dass irgendwann eine Spezifikation erstellt wird, die Elementabfragen (oder etwas, das das gleiche Ziel erreicht) standardisiert und sie nativ, sauber, einfach und robust macht. Die meisten Menschen sind sich einig, dass Medienabfragen sehr begrenzt sind und nicht zu einem modularen Design und einer echten Reaktionsfähigkeit beitragen.

Es gibt einige Polyfills / Bibliotheken, die das Problem auf unterschiedliche Weise lösen (könnte jedoch als Problemumgehung anstelle von Lösungen bezeichnet werden):

Ich habe andere Lösungen für ähnliche Probleme vorgeschlagen. Normalerweise verwenden sie Timer oder die Fenster- / Ansichtsfenstergröße unter der Haube, was keine echte Lösung ist. Darüber hinaus denke ich, dass dies idealerweise hauptsächlich in CSS und nicht in Javascript oder HTML gelöst werden sollte.

Stan
quelle
+1 für Elementabfragen, aber ich hoffe nicht, sie bald zu sehen. Derzeit gibt es noch nicht einmal einen Entwurf einer Spezifikation AFAIK ... Vielleicht sollte einer von uns Interessenten einen zusammenstellen und einreichen?
Rob Evans
1
Das ist natürlich eine gute Idee. Es wird lange dauern, bis eine endgültige Version erreicht ist. Es muss viel Aufmerksamkeit auf sich gezogen werden. Dies auf eine gute Art und Weise zu gestalten, scheint mir nicht trivial zu sein. Browserübergreifende Kompatibilität, zirkuläre Abhängigkeiten, Debugging, Lesbarkeit und Wartung usw. Beginnen wir also so früh wie möglich :)
Stan
@Stan, ich denke, diese Antwort kann besser sein, indem mehr Details zu Element Query hinzugefügt werden.
Pacerier
15

Ich fand, dass diese Bibliothek funktioniert, als MarcJs Lösung nicht:

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

Es ist sehr leicht und erkennt sogar natürliche Größenänderungen über CSS oder einfach das Laden / Rendern von HTML.

Codebeispiel (aus dem Link entnommen):

<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
quelle
Das ist es. 147 Zeilen, verwendet Bildlaufereignisse. Lesen Sie diesen Code durch, wenn Sie die vollständige und zutreffende Antwort darauf erhalten möchten, wie Änderungen der Div-Dimension am besten erkannt werden. Hinweis: Es ist nicht "nur diese Bibliothek verwenden".
Seph Reed
1
Leute sollten sich dieses Problem ansehen , bevor sie diese Bibliothek ausprobieren.
Louis
13

Schauen Sie sich diese http://benalman.com/code/projects/jquery-resize/examples/resize/ an.

Es hat verschiedene Beispiele. Versuchen Sie, die Größe Ihres Fensters zu ändern, und sehen Sie, wie Elemente in Containerelementen angepasst werden.

Beispiel mit js Geige, um zu erklären, wie es funktioniert.
Schauen Sie sich diese Geige an http://jsfiddle.net/sgsqJ/4/

In diesem Fall ist resize () an ein Element mit der Klasse "test" sowie an das Fensterobjekt gebunden und beim Resize-Rückruf des Fensterobjekts $ ('. Test') wird resize () aufgerufen.

z.B

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

$(window).resize(function(){
   $('#test_div').resize();
});
Bharat Patil
quelle
Interessant. Dass jsfiddle Chrome mit dem Inspector ständig zum Absturz bringt, ist geöffnet und ich klicke auf den Link "Mehr Text hinzufügen". Firefox löst "zu viele Rekursionsfehler" aus.
EricP
3
Bitte verwenden Sie nicht das jQuery-Größenänderungs-Plugin. Es ist sehr langsam und auch nicht genau. Siehe meine Antwort, um eine bessere Lösung zu erhalten.
Marc J. Schmidt
24
Dadurch werden Dimensionsänderungen NICHT direkt erkannt. Es ist nur ein Nebenprodukt eines Ereignis-Listeners zur Größenänderung. Eine Dimensionsänderung kann auf viele andere Arten erfolgen.
vsync
4
Was ist, wenn die Größenänderung des Div von einer Änderung des Stils oder des Dom und nicht von der Fenstergröße abhängt?
Ehrfurcht
9

Sie können verwenden iframeoder objectverwenden contentWindowoder die contentDocumentGröße ändern. Ohne setIntervalodersetTimeout

Die Schritte:

  1. Stellen Sie Ihre Elementposition auf ein relative
  2. Fügen Sie in einem transparenten absoluten versteckten IFRAME hinzu
  3. Hören Sie IFRAME.contentWindow- onresizeEreignis

Ein Beispiel für 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>

Das 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')
})

Laufendes Beispiel

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


Mehr sehen:

Aminadav Glickshtein
quelle
1
Ein Hinweis für diejenigen, die jquery nicht verwenden, IFRAME.contentWindowist erst verfügbar, wenn das onloadEreignis auf dem iframe ausgelöst wird . Möglicherweise möchten Sie auch den Stil hinzufügen visibility:hidden;, da der Iframe die Mauseingabe für dahinter stehende Elemente blockieren kann (scheint zumindest im IE "schwebend" zu sein).
James Wilkins
3
Obwohl dies funktioniert, handelt es sich um eine schwergewichtige Lösung mit dem Aufwand, einen völlig neuen Browserkontext zu erstellen, um die Größenänderung zu beobachten. In einigen Fällen ist es auch nicht ideal oder machbar, die displayEigenschaft des Elements zu ändern .
Trusktr
vereinbart, dies ist eine schreckliche Lösung wie oben angegeben
29er
Wie schrieb so schreckliche Lösung?
Aminadav Glickshtein
Diese Methode eignet sich gut für Video- und Audio-Player, bei denen die Größe dynamischer Inhalte geändert werden muss. Ich benutze diese Methode, um die Größe der Ausgabe von wellsurfer.js zu ändern.
David Clews
8

Nur das Fensterobjekt generiert ein Ereignis zum Ändern der Größe. Ich weiß nur, wie Sie das tun können, was Sie tun möchten, indem Sie einen Intervall-Timer ausführen, der die Größe regelmäßig überprüft.

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

Erstaunlicherweise so alt wie dieses Problem ist, ist dies in den meisten Browsern immer noch ein Problem.

Wie bereits erwähnt, wird Chrome 64+ jetzt nativ mit Resize Observes ausgeliefert. Die Spezifikation wird jedoch noch optimiert, und Chrome liegt derzeit (Stand: 29.01.2019) hinter der neuesten Ausgabe der Spezifikation .

Ich habe ein paar gute ResizeObserver-Polyfills in freier Wildbahn gesehen, einige folgen jedoch nicht genau der Spezifikation und andere haben einige Berechnungsprobleme.

Ich brauchte dieses Verhalten dringend, um einige reaktionsschnelle Webkomponenten zu erstellen, die in jeder Anwendung verwendet werden können. Damit sie gut funktionieren, müssen sie ihre Abmessungen jederzeit kennen. Daher klang ResizeObservers ideal und ich entschied mich, eine Polyfüllung zu erstellen, die der Spezifikation so genau wie möglich entsprach.

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

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

Trem
quelle
Dies ist definitiv die Antwort für alle, die sich mit IE11 + Browserkompatibilität beschäftigen.
Ekfuhrmann
3

Mit Clay.js ( https://github.com/zzarcon/clay ) können Änderungen an der Elementgröße ganz einfach erkannt werden :

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

el.on('resize', function(size) {
    console.log(size.height, size.width);
});
Zzarcon
quelle
7
Bitte geben Sie die entsprechende Offenlegung an, da es sich um Ihre Bibliothek handelt.
Jhpratt GOFUNDME RELICENSING
Dies hat bei mir funktioniert, wo ResizeSensor dies nicht tut, es bricht meinen modalen Stil. Vielen Dank, Zzarcon :)
Máxima Alekz
3

Dieser Blog-Beitrag hat mir geholfen, Größenänderungen an DOM-Elementen effizient zu erkennen.

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

Wie man diesen Code benutzt ...

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

Vom Beispiel verwendeter gepackter Code ...

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

Hinweis: AppConfig ist ein Namespace / Objekt, das ich zum Organisieren wiederverwendbarer Funktionen verwende. Fühlen Sie sich frei, den Namen zu suchen und durch alles zu ersetzen, was Sie möchten.

Ben Gripka
quelle
3

Mein jQuery-Plugin aktiviert das Ereignis "Größe ändern" für alle Elemente, nicht nur für das window .

https://github.com/dustinpoissant/ResizeTriggering

$("#myElement") .resizeTriggering().on("resize", function(e){
  // Code to handle resize
}); 
Dustin Poissant
quelle
1
Ihr Link zeigte auf einen Ort, der nicht mehr existiert. Ich habe nach dem Namen des Plugins gesucht und eine Seite gefunden, die Ihrer Beschreibung entspricht. Da Sie anscheinend der Autor des Plugins sind, habe ich auch die Sprache Ihrer Antwort bearbeitet, um deutlich zu machen, dass dies Ihre Arbeit ist. Die Regeln dieser Website sind so, dass Sie beim Schreiben von Antworten, die die Verwendung von etwas empfehlen, das Sie entwickelt haben , die Beziehung explizit angeben müssen .
Louis
Dies ist keine großartige Lösung, da Sie einige Funktionen in setTimer immer wieder ausführen, was bedeutet, dass der Browser die CPU ständig brennt, auch wenn nichts passiert. Besitzer von Laptops und Mobiltelefonen profitieren von einer Lösung, die Browserereignisse nur verwendet, wenn eine Aktion ausgeführt wird, z. B. die Verwendung des onScroll-Ereignisses.
Roman Zenka
2

Sie können den Code im folgenden Snippet ausprobieren. Er deckt Ihre Anforderungen mit einfachem Javascript ab. ( Führen Sie das Code-Snippet aus und klicken Sie auf den Link für die ganze Seite, um die Warnung auszulösen, dass die Größe des Div geändert wird, wenn Sie es testen möchten. )

Aufgrund der Tatsache, dass dies eine setIntervalZeitspanne von 100 Millisekunden ist, würde ich sagen, dass mein PC nicht zu viel CPU-Hunger empfand. (Zum Testzeitpunkt wurden insgesamt 0,1% der CPU für alle geöffneten Registerkarten in Chrome verwendet.) Aber andererseits ist dies nur für einen Div. Wenn Sie dies für eine große Anzahl von Elementen tun möchten, dann könnte es ja sehr CPU-hungrig sein.

Sie können jederzeit ein Klickereignis verwenden, um das Div-Resize- Sniffing zu stoppen .

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>

Sie können die Geige auch überprüfen

AKTUALISIEREN

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>

Geige aktualisiert

Peter Darmis
quelle
2

Dies ist so ziemlich eine exakte Kopie der Top-Antwort, aber anstelle eines Links ist es nur der Teil des Codes, der zählt, übersetzt, um IMO besser lesbar und leichter zu verstehen. Einige andere kleine Änderungen umfassen die Verwendung von cloneNode () und das Nicht-Einfügen von HTML in einen js-String. Kleine Sachen, aber Sie können diese kopieren und einfügen, wie es ist, und es wird funktionieren.

Die Funktionsweise besteht darin, dass zwei unsichtbare Divs das gerade angezeigte Element füllen, dann jeweils einen Auslöser einfügen und eine Bildlaufposition festlegen, die bei Änderung der Größe zu einem Bildlaufwechsel führt.

Alle echten Kredite gehen an Marc J, aber wenn Sie nur nach dem relevanten Code suchen, hier ist er:

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

Hier ist eine vereinfachte Version der Lösung von @nkron, die auf ein einzelnes Element anwendbar ist (anstelle eines Arrays von Elementen in @ nkrons Antwort, Komplexität, die ich nicht brauchte).

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

Der Ereignis-Listener und -Betrachter kann entfernt werden durch:

window.removeEventListener('resize', onResizeElem.checkForChanges);
onResizeElem.observer.disconnect();
Gman
quelle
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
quelle
0

Wenn Sie die Antwort von Bharat Patil verwenden , geben Sie einfach false in Ihrem Rückruf zurück, um einen maximalen Stapelfehler zu vermeiden. Siehe Beispiel unten:

$('#test_div').bind('resize', function(){
   console.log('resized');
   return false;
});
weißer Hase
quelle
0

Dies ist eine wirklich alte Frage, aber ich dachte, ich würde meine Lösung dazu veröffentlichen.

Ich versuchte es zu benutzen, ResizeSensorda jeder ziemlich verknallt zu sein schien. Nach der Implementierung wurde mir jedoch klar, dass die Elementabfrage unter der Haube erfordert, dass das betreffende Element eine Position hat relativeoder darauf absoluteangewendet wird, was für meine Situation nicht funktioniert hat.

Am Ende habe ich dies mit einem Rxjs intervalanstatt mit einem Straight setTimeoutoder requestAnimationFrameähnlichen vorherigen Implementierungen behandelt.

Das Schöne an der beobachtbaren Variante eines Intervalls ist, dass Sie den Stream ändern können, jedoch jede andere beobachtbare Funktion verarbeitet werden kann. Für mich war eine grundlegende Implementierung ausreichend, aber Sie könnten verrückt werden und alle möglichen Zusammenführungen usw. durchführen.

Im folgenden Beispiel verfolge ich die Breitenänderungen des inneren (grünen) Div. Es hat eine Breite von 50%, aber eine maximale Breite von 200px. Das Ziehen des Schiebereglers wirkt sich auf die Breite des Wrapper-Divs (grau) aus. Sie können sehen, dass das Observable nur ausgelöst wird, wenn sich die Breite des inneren Divs ändert. Dies geschieht nur, wenn die Breite des äußeren Divs kleiner als 400 Pixel ist.

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

Nur Window.onResizein der Spezifikation vorhanden, aber Sie können immer verwenden IFrame, um ein neues WindowObjekt in Ihrem DIV zu generieren .

Bitte überprüfen Sie diese Antwort . Es gibt ein neues kleines jquery-Plugin , das portabel und einfach zu bedienen ist. Sie können jederzeit den Quellcode überprüfen, um zu sehen, wie es gemacht wird.

<!-- (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
quelle
Dies ist eine äußerst ineffektive Lösung, wenn Sie veränderbare Divs haben.
Suricactus
-1

Ich dachte, es könnte nicht gemacht werden, aber dann dachte ich darüber nach, Sie können die Größe eines Div manuell über style = "resize: both;" ändern. Um dies zu tun, müssen Sie darauf klicken. Fügen Sie daher eine On-Click-Funktion hinzu, um die Höhe und Breite des Elements zu überprüfen, und es hat funktioniert. Mit nur 5 Zeilen reinem Javascript (sicher könnte es noch kürzer sein) 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
quelle
2
Dies wird nur berechnet, wenn ein Klickereignis ausgelöst wird, das nicht Teil der Anforderung der Frage ist.
Rob Evans