Comment détecter la dimension de DIV changé?

351

J'ai l'exemple html suivant, il y a un DIV qui a 100% de largeur. Il contient quelques éléments. Lors du redimensionnement des fenêtres, les éléments intérieurs peuvent être repositionnés et la dimension du div peut changer. Je demande s'il est possible d'accrocher l'événement de changement de dimension de la div? et comment faire ça? Je lie actuellement la fonction de rappel à l'événement jQuery resize sur le DIV cible, cependant, aucun journal de console n'est sorti, voir ci-dessous:

Avant de redimensionner entrez la description de l'image ici

<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
la source
7
Cela ne fonctionnera pas car vous liez l'événement resize à l'élément div spécifié. Mais l'événement de redimensionnement se déclenchera pour la fenêtre et non pour votre élément.
Fatih Acet
Vous pouvez utiliser setIntervalici comme solution possible. Vous pouvez toujours vous lier clearIntervalà un clic de bouton pour arrêter la boucle une fois votre travail terminé.
Peter Darmis
1
Depuis 2020, ResizeObserver fonctionne dans tous les principaux navigateurs (Chrome, Safari, Firefox, Edge) sauf IE. caniuse.com/#feat=resizeobserver
Dan

Réponses:

264

Il existe une méthode très efficace pour déterminer si la taille d'un élément a été modifiée.

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

Cette bibliothèque possède une classe ResizeSensorqui peut être utilisée pour la détection de redimensionnement.
Il utilise une approche basée sur les événements, il est donc extrêmement rapide et ne perd pas de temps CPU.

Exemple:

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

Veuillez ne pas utiliser le plugin jQuery onresize car il utilise setTimeout()en combinaison avec la lecture du DOM clientHeight/ clientWidthpropriétés dans une boucle pour vérifier les changements.
C'est incroyablement lent et inexact, car cela provoque un thrash de mise en page .

Divulgation: je suis directement associé à cette bibliothèque.

Marc J. Schmidt
la source
8
@jake, le support IE11 est maintenant implémenté.
Marc J. Schmidt
15
@Aletheios, vous devriez regarder de plus près. requestAnimationFrame n'est pas pour la détection de dimension lente, mais exactement le contraire: il lisse tous ces nombres extrêmes d'événements qui sont déclenchés par le capteur de redimensionnement basé sur les événements réels pour gagner du temps sur le processeur. Voir les documents mis à jour sur github.com/marcj/css-element-queries/blob/master/src/…
Marc J. Schmidt
6
@Aletheios Je suppose que vous manquez un point: setTimeout est non seulement inexact, mais lent comme l'enfer. Un requestAnimationFrame qui vérifie un booléen simple est des ordres de grandeur plus rapide qu'un setTimeout vérifiant tout le temps par rapport aux propriétés DOM. À côté de cela, setTimeout est également appelé toutes les quelques millisecondes.
Marc J. Schmidt
9
@Orwellophile, vous n'avez évidemment rien compris. Arrêtez de voter en aval lorsque vous ne savez pas comment cela fonctionne en interne. Encore une fois: css-element-query: utilise un événement de redimensionnement réel qui est déclenché chaque fois que la dimension change. Comme vous obtiendrez trop d'événements (pour drag'n'drop par exemple), il a ajouté requestAnimationFrame vérifiant une simple variable booléenne pour lisser ces événements déclenchés jusqu'à 1 / image. plugin jquery: utilise un setTimeout vérifiant tous les accessoires de disposition DOM (clientHeight, etc.) où chaque vérification est très lente mais doit être vérifiée souvent pour avoir la même fonctionnalité.
Marc J. Schmidt
9
@Aletheios, @Orwellophile et tous ceux qui pourraient avoir été induits en erreur par eux: requestAnimationFramevs setTimeoutn'est pas pertinent, cette bibliothèque ne fait pas de boucle . requestAnimationFramen'est utilisé que pour anti-rebond / limitation de la fréquence d'appels de vos rappels, il n'interroge pas . Contrairement à cela, MutationObserver pourrait théoriquement être utilisé avec un effet similaire, mais pas dans les navigateurs plus anciens. C'est extrêmement intelligent.
Han Seoul-Oh
251

Une nouvelle norme pour cela est l'API Resize Observer, disponible dans 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>

Redimensionner l'observateur

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

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

Problème avec Firefox: https://bugzil.la/1272409

Problème de Safari: http://wkb.ug/157743

Assistance actuelle: http://caniuse.com/#feat=resizeobserver

Daniel Herr
la source
3
pas encore utile pour les sites de production, car seul Chrome le prend en charge pour le moment. Mais certainement la meilleure solution ici! btw, voici un bref aperçu de l'état de mise en œuvre: chromestatus.com/feature/5705346022637568
Philipp
8
@Philipp Il y a un polyfill.
Daniel Herr
2
Il semble que ce soit encore une fonctionnalité expérimentale dans tous les navigateurs caniuse.com/#feat=resizeobserver
Francesc Rosas
5
Même 2 ans après cette réponse, le support du navigateur est sombre: caniuse.com/#search=resizeobserver
Scrimothy
2
Fonctionne comme un charme dans les derniers Chrome et FF.
Klemen Tušar
32

Vous devez lier l' resizeévénement à l' windowobjet, pas à un élément html générique.

Vous pouvez ensuite utiliser ceci:

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

et dans la fonction de rappel, vous pouvez vérifier la nouvelle largeur de votre divappel

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

Donc, la réponse à votre question est non , vous ne pouvez pas lier l' resizeévénement à un div.

Alessandro Vendruscolo
la source
124
cette réponse n'est pas assez bonne. des changements de taille peuvent se produire sans aucun redimensionnement de fenêtre. du tout.
vsync
9
par exemple lorsque vous avez un élément séparateur ou que vous ajoutez simplement du texte ou un autre contenu à un élément.
Günter Zöchbauer
@anu pas vrai, vous pouvez par exemple avoir une modification DOM via javascript, ce qui entraîne l'application de CSS à une nouvelle structure, ce qui entraîne une disposition différente - la taille de chaque conteneur.
Antoniossss
29

À long terme, vous pourrez utiliser ResizeObserver .

new ResizeObserver(callback).observe(element);

Malheureusement, il n'est actuellement pas pris en charge par défaut dans de nombreux navigateurs.

En attendant, vous pouvez utiliser des fonctions comme les suivantes. Depuis, la majorité des changements de taille des éléments proviendront du redimensionnement de la fenêtre ou de la modification de quelque chose dans le DOM. Vous pouvez écouter la fenêtre de redimensionnement de la fenêtre avec Redimensionner l'événement et vous pouvez écouter les changements DOM en utilisant MutationObserver .

Voici un exemple de fonction qui vous rappellera lorsque la taille de l'élément fourni change à la suite de l'un de ces événements:

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
la source
1
Malheureusement, il semble que MutationObserver ne puisse pas détecter les changements dans le Shadow DOM.
Ajedi32
@nkron, Malheureusement, si vous utilisez le redimensionnement CSS3 , le redimensionnement peut se produire lorsque l'utilisateur redimensionne manuellement. Cet événement ne peut pas être capturé.
Pacerier
@ Ajedi32 Oui, mais vous pouvez utiliser MutationObserver à l'intérieur du ShadowDOM. Ce que je veux dire, c'est qu'au lieu de regarder le bodycomme dans cet exemple, vous pouvez regarder un élément directement. C'est en fait plus performant et efficace que de regarder tout le corps.
trusktr
@ Ajedi32 Eh bien, le seul problème est que si l'arborescence du Shadow DOM est fermée, vous ne pourrez pas accéder aux éléments internes, mais je ne pense pas que ce soit généralement quelque chose que vous devez faire, et si vous devez observer un arbre fermé comme ça alors il peut y avoir un problème dans la conception. Idéalement, vous ne devriez avoir qu'à observer vos propres éléments auxquels vous avez accès. Un composant Shadow-DOM qui vous oblige à observer son dimensionnement interne peut ne pas être correctement conçu. Avez-vous un exemple précis? Si c'est le cas, je peux peut-être aider à suggérer une solution (parce que ça m'intéresse aussi, mais je n'ai pas encore d'exemple).
trusktr
@trusktr Je n'ai pas de MCVE pour le moment, mais fondamentalement, tout élément du Shadow DOM qui peut changer sa taille à la suite d'une entrée utilisateur serait suffisant pour déclencher ce problème. Une boîte qui se développe avec des informations supplémentaires lorsque vous cliquez dessus, par exemple. Notez que j'observe la page, pas l'élément individuel, car je veux faire défiler automatiquement vers le bas lorsque la taille de la page augmente, quelle qu'en soit la raison. Un élément Shadow DOM en expansion n'est qu'une des nombreuses raisons possibles pour lesquelles la page peut s'allonger.
Ajedi32
24

ResizeSensor.js fait partie d'une immense bibliothèque, mais j'ai réduit sa fonctionnalité à CECI :

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

Comment l'utiliser:

let container = document.querySelector(".container");
new ResizeSensor(container, function()
{
    console.log("dimension changed:", container.clientWidth, container.clientHeight);
});
Martin Wantke
la source
C'est ce que font les graphiques js.
Antoniossss
Merci d'avoir compressé cela. Je vais l'essayer :)
Tony K.
Il manque quelque chose dans cette expressionparseInt( getComputedStyle(element) )
GetFree
2
Je suis confus comment est "let zIndex = parseInt (getComputedStyle (element));" valide? Je suppose que vous vouliez dire: "let zIndex = parseInt (getComputedStyle (element) .zIndex);
Ryan Badour le
1
La solution / code ci-dessus ne détecte pas tous les événements de redimensionnement (par exemple, si un conteneur est redimensionné en fixe, il ne détecte pas le redimensionnement pour les événements enfants. Voir jsfiddle.net/8md2rfsy/1 ). La réponse acceptée fonctionne (décommentez les lignes respectives).
newBee
21

Je ne recommande pas de setTimeout()pirater car cela ralentit les performances! Au lieu de cela, vous pouvez utiliser des observateurs de mutation DOM pour écouter le changement de taille 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>

Voici le violon
Essayez de changer la taille de div.


Vous pouvez envelopper davantage votre méthode dans la debounceméthode pour améliorer l'efficacité. debouncedéclenchera votre méthode toutes les x millisecondes au lieu de déclencher toutes les millisecondes le DIV est redimensionné.

JerryGoyal
la source
9
Le seul problème avec celui-ci est qu'il ne signale pas de changement à moins qu'une mutation n'arrive réellement à l'élément. Si l'élément est redimensionné en raison de l'ajout d'autres éléments, cela ne fonctionnera pas.
Tony K.
Je suis d'accord avec @TonyK., C'est mon seul problème avec MutationObserver et maintenant je cherche une bibliothèque de redimensionnement d'élément
Murhaf Sousli
@JerryGoyal Cela ne fonctionne pas lorsque le div contient la balise <img>. Dans le premier cas, l'image n'est pas chargée et la taille div est définie sur 0 et après le chargement, la taille div est modifiée, mais l'observateur n'est pas déclenché
Santhosh
15

La meilleure solution serait d'utiliser ce que l'on appelle les requêtes d'éléments . Cependant, ils ne sont pas standard, aucune spécification n'existe - et la seule option est d'utiliser l'une des polyfills / bibliothèques disponibles, si vous voulez procéder de cette façon.

L'idée derrière les requêtes d'éléments est de permettre à un certain conteneur sur la page de répondre à l'espace qui lui est fourni. Cela permettra d'écrire un composant une fois puis de le déposer n'importe où sur la page, tout en ajustant son contenu à sa taille actuelle. Quelle que soit la taille de la fenêtre. C'est la première différence que nous voyons entre les requêtes d'élément et les requêtes de média. Tout le monde espère qu'à un moment donné, une spécification sera créée qui normalisera les requêtes d'éléments (ou quelque chose qui atteint le même objectif) et les rendra natives, propres, simples et robustes. La plupart des gens conviennent que les requêtes médias sont assez limitées et n'aident pas à la conception modulaire et à la vraie réactivité.

Il existe quelques polyfills / bibliothèques qui résolvent le problème de différentes manières (pourraient être appelées solutions de contournement au lieu de solutions):

J'ai vu d'autres solutions à des problèmes similaires proposées. Habituellement, ils utilisent des minuteries ou la taille de la fenêtre / fenêtre sous le capot, ce qui n'est pas une vraie solution. De plus, je pense que, idéalement, cela devrait être résolu principalement en CSS, et non en javascript ou en html.

Stan
la source
+1 pour les requêtes sur les éléments, mais je n'espère pas les voir de sitôt. Il n'y a même pas de projet de spécification à ce stade de l'AFAIK ... Peut-être que l'un de nous, les parties intéressées, devrait en rédiger un et le soumettre?
Rob Evans
1
C'est une bonne idée bien sûr. Il faudra beaucoup de temps pour arriver à une version finale, beaucoup d'attention doit être attirée. Concevoir cela dans le bon sens ne me semble pas anodin. Compatibilité entre navigateurs, dépendances circulaires, débogage, lisibilité et maintenance, etc. Commençons donc le plus tôt possible :)
Stan
@Stan, je pense que cette réponse peut être meilleure en ajoutant plus de détails sur ce qu'est Element Query.
Pacerier
15

J'ai trouvé que cette bibliothèque fonctionnait lorsque la solution de MarcJ n'a pas:

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

Il est très léger et détecte même les redimensionnements naturels via CSS ou simplement le chargement / rendu HTML.

Échantillon de code (extrait du lien):

<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
la source
Ça y est. 147 lignes, utilise des événements de défilement. Lisez ce code si vous voulez une réponse complète et vraie sur la meilleure façon de détecter les changements de dimension div. Astuce: ce n'est pas "juste utiliser cette bibliothèque".
Seph Reed
1
Les gens devraient se pencher sur ce problème avant d'essayer cette bibliothèque.
Louis
13

Jetez un œil à ce http://benalman.com/code/projects/jquery-resize/examples/resize/

Il a plusieurs exemples. Essayez de redimensionner votre fenêtre et voyez comment les éléments à l'intérieur des éléments du conteneur ont été ajustés.

Exemple avec js violon pour expliquer comment le faire fonctionner.
Jetez un œil à ce violon http://jsfiddle.net/sgsqJ/4/

Dans cet événement resize () est lié à un élément ayant la classe "test" et aussi à l'objet window et dans le rappel de redimensionnement de l'objet window $ ('. Test'). Resize () est appelé.

par exemple

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

$(window).resize(function(){
   $('#test_div').resize();
});
Bharat Patil
la source
Intéressant. Que jsfiddle plante systématiquement le chrome avec l'inspecteur est ouvert et je clique sur le lien "Ajouter plus de texte". Firefox génère des erreurs "trop ​​de récursivité".
EricP
3
Veuillez ne pas utiliser le plugin de redimensionnement de jQuery. C'est vraiment lent et pas aussi précis. Voir ma réponse pour obtenir une meilleure solution.
Marc J. Schmidt
24
Cela ne détecte PAS directement les changements de dimension. c'est juste un sous-produit d'un écouteur d'événement de redimensionnement. un changement de dimension peut se produire de bien d'autres façons.
vsync
4
Que se passe-t-il si le redimensionnement du div provient d'un changement de style ou de dom et non d'un redimensionnement de fenêtre?
admiration le
9

Vous pouvez utiliser iframeou objectutiliser contentWindowou contentDocumentsur redimensionner. Sans setIntervalousetTimeout

Les marches:

  1. Définissez la position de votre élément sur relative
  2. Ajoutez à l'intérieur un IFRAME caché absolu transparent
  3. Écoutez IFRAME.contentWindow- onresizeévénement

Un exemple de 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>

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

Exemple d'exécution

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


Voir plus:

Aminadav Glickshtein
la source
1
Une note pour ceux qui n'utilisent pas jquery, IFRAME.contentWindown'est pas disponible jusqu'à ce que l' onloadévénement se déclenche sur l'iframe. En outre, vous voudrez peut-être ajouter le style visibility:hidden;, car l'iframe peut bloquer l'entrée de la souris sur les éléments derrière lui (semble au moins "flottant" dans IE).
James Wilkins
3
Bien que cela fonctionne, c'est une solution lourde avec la surcharge de créer un tout nouveau contexte de navigation juste pour observer le changement de taille. De plus, dans certains cas, il n'est pas idéal ni faisable de modifier la displaypropriété de l'élément.
trusktr
d'accord, c'est une solution terrible comme indiqué ci
29er
Comment a écrit une solution aussi terrible?
Aminadav Glickshtein
cette méthode fonctionne bien avec les lecteurs vidéo et les lecteurs audio qui nécessitent un redimensionnement de contenu dynamique. Im utilisant cette méthode pour redimensionner la sortie wavesurfer.js.
David Clews
8

Seul l'objet fenêtre génère un événement "redimensionner". La seule façon que je sache de faire ce que vous voulez faire est d'exécuter une minuterie d'intervalle qui vérifie périodiquement la taille.

Pointu
la source
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
la source
7

Aussi vieux que soit ce problème, c'est toujours un problème dans la plupart des navigateurs.

Comme d'autres l'ont dit, Chrome 64+ est désormais livré avec Resize Observes en mode natif, cependant, la spécification est toujours affinée et Chrome est actuellement (à partir du 2019-01-29) derrière la dernière édition de la spécification .

J'ai vu quelques bons polyfills ResizeObserver dans la nature, cependant, certains ne suivent pas la spécification de près et d'autres ont des problèmes de calcul.

J'avais désespérément besoin de ce comportement pour créer des composants Web réactifs pouvant être utilisés dans n'importe quelle application. Pour les faire fonctionner correctement, ils ont besoin de connaître leurs dimensions à tout moment, donc ResizeObservers semblait idéal et j'ai décidé de créer un polyfill qui suivait la spécification aussi près que possible.

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

Démo: https://codesandbox.io/s/myqzvpmmy9

Trem
la source
C'est définitivement la réponse à tous ceux qui traitent avec la compatibilité du navigateur IE11 +.
ekfuhrmann
3

En utilisant Clay.js ( https://github.com/zzarcon/clay ), il est assez simple de détecter les changements de taille d'élément:

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

el.on('resize', function(size) {
    console.log(size.height, size.width);
});
Zzarcon
la source
7
Veuillez fournir la divulgation appropriée étant donné qu'il s'agit de votre bibliothèque.
jhpratt GOFUNDME RELICENSING
Cela a fonctionné pour moi, où ResizeSensor ne fonctionne pas, cela brise mon style modal. Merci Zzarcon :)
Máxima Alekz
3

Ce billet de blog m'a aidé à détecter efficacement les changements de taille des éléments DOM.

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

Comment utiliser ce code ...

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

Code packagé utilisé par l'exemple ...

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

Remarque: AppConfig est un espace de nom / objet que j'utilise pour organiser des fonctions réutilisables. N'hésitez pas à rechercher et à remplacer le nom par tout ce que vous souhaitez.

Ben Gripka
la source
3

Mon plugin jQuery active l'événement "resize" sur tous les éléments et pas seulement window .

https://github.com/dustinpoissant/ResizeTriggering

$("#myElement") .resizeTriggering().on("resize", function(e){
  // Code to handle resize
}); 
Dustin Poissant
la source
1
Votre lien pointait vers un emplacement qui n'existe plus. J'ai recherché le nom du plugin et trouvé une page qui correspond à votre description. Puisque vous semblez être l'auteur du plugin, j'ai également édité la langue de votre réponse pour qu'il soit explicite que c'est votre travail. Les règles de ce site sont telles que lorsque vous écrivez des réponses recommandent d'utiliser quelque chose que vous avez développé, vous devez rendre la relation explicite.
Louis
Ce n'est pas une excellente solution car vous exécutez une fonction dans setTimer encore et encore, ce qui signifie que le navigateur brûle constamment le CPU même si rien ne se passe. Les propriétaires d'ordinateurs portables et de téléphones portables bénéficient d'une solution qui n'utilise les événements du navigateur qu'en cas d'action, comme l'utilisation de l'événement onScroll.
Roman Zenka
2

Vous pouvez essayer le code dans l'extrait de code suivant, il couvre vos besoins en utilisant du javascript simple. ( exécutez l'extrait de code et cliquez sur le lien de la page complète pour déclencher l'alerte que le div est redimensionné si vous souhaitez le tester. ).

Basé sur le fait qu'il s'agit d'un setInterval de 100 millisecondes, j'ose dire que mon PC n'a pas trouvé qu'il avait trop faim de CPU. (0,1% du processeur a été utilisé au total pour tous les onglets ouverts dans Chrome au moment du test.). Mais là encore, c'est pour une seule div, si vous souhaitez le faire pour une grande quantité d'éléments, alors oui, cela pourrait être très gourmand en CPU.

Vous pouvez toujours utiliser un événement de clic pour arrêter le reniflement de redimensionnement div de toute façon.

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>

Vous pouvez vérifier le violon aussi

MISE À JOUR

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>

Fiddle mis à jour

Peter Darmis
la source
2

C'est à peu près une copie exacte de la première réponse, mais au lieu d'un lien, c'est juste la partie du code qui compte, traduite pour être IMO plus lisible et plus facile à comprendre. Quelques autres petites modifications incluent l'utilisation de cloneNode () et la non mise en html dans une chaîne js. Petit truc, mais vous pouvez copier et coller cela tel quel et cela fonctionnera.

La façon dont cela fonctionne est de faire en sorte que deux divs invisibles remplissent l'élément que vous regardez, puis de mettre un déclencheur dans chacun, et de définir une position de défilement qui entraînera le déclenchement d'un changement de défilement si la taille change.

Tout le mérite revient à Marc J, mais si vous cherchez juste le code pertinent, le voici:

    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
la source
1

Solution Javascript pure, mais ne fonctionne que si l'élément est redimensionné avec le bouton de redimensionnement css :

  1. stocker la taille de l'élément avec offsetWidth et offsetHeight ;
  2. ajouter un écouteur d'événement onclick sur cet élément;
  3. une fois déclenché, comparez les valeurs actuelles offsetWidthet offsetHeightles valeurs stockées, et si elles sont différentes, faites ce que vous voulez et mettez à jour ces valeurs.
roipoussiere
la source
1

Voici une version simplifiée de la solution de @nkron, applicable à un seul élément (au lieu d'un tableau d'éléments dans la réponse de @ nkron, complexité dont je n'avais pas besoin).

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

L'auditeur et l'observateur d'événements peuvent être supprimés par:

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

en utilisant la réponse Bharat Patil, renvoyez simplement false à l'intérieur de votre rappel de liaison pour éviter une erreur de pile maximale, voir l'exemple ci-dessous:

$('#test_div').bind('resize', function(){
   console.log('resized');
   return false;
});
lapin blanc
la source
0

C'est une très vieille question, mais j'ai pensé que je publierais ma solution à cela.

J'ai essayé d'utiliser ResizeSensorcar tout le monde semblait avoir un gros coup de cœur. Après la mise en œuvre, j'ai réalisé que sous le capot, la requête d'élément nécessite que l'élément en question ait une position relativeouabsolute appliqué, ce qui n'a pas fonctionné pour ma situation.

J'ai fini par gérer cela avec un Rxjs intervalau lieu d'une implémentation droite setTimeoutou requestAnimationFramesimilaire.

Ce qui est bien avec la saveur observable d'un intervalle, c'est que vous pouvez modifier le flux, mais tout autre observable peut être géré. Pour moi, une implémentation de base était suffisante, mais vous pourriez devenir fou et faire toutes sortes de fusions, etc.

Dans l'exemple ci-dessous, je suis le suivi des changements de largeur de la div intérieure (verte). Il a une largeur définie à 50%, mais une largeur maximale de 200 pixels. Faire glisser le curseur affecte la largeur du div wrapper (gris). Vous pouvez voir que l'observable ne se déclenche que lorsque la largeur de la div intérieure change, ce qui ne se produit que si la largeur de la div extérieure est inférieure à 400 pixels.

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
la source
-1

Window.onResizeExiste uniquement dans la spécification, mais vous pouvez toujours l'utiliser IFramepour générer un nouvel Windowobjet à l'intérieur de votre DIV.

Veuillez vérifier cette réponse . Il existe un nouveau petit plugin jquery , portable et facile à utiliser. Vous pouvez toujours vérifier le code source pour voir comment cela se fait.

<!-- (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
la source
C'est une solution très inefficace si vous avez des div redimensionnables.
suricactus
-1

je pensais que cela ne pouvait pas être fait mais ensuite j'y ai pensé, vous pouvez redimensionner manuellement un div via style = "resize: both;" pour ce faire, vous pouvez cliquer dessus, donc vous avez ajouté une fonction onclick pour vérifier la hauteur et la largeur de l'élément et cela a fonctionné. Avec seulement 5 lignes de javascript pur (bien sûr, il pourrait être encore plus court) 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
la source
2
Cela ne calcule que lorsqu'un événement de clic est déclenché, ce qui ne fait pas partie de l'exigence de la question.
Rob Evans