javascript - onresize - resizeobserver




Como detectar a dimensão do DIV mudou? (16)

A longo prazo, você poderá usar o https://wicg.github.io/ResizeObserver .

new ResizeObserver(callback).observe(element);

Infelizmente, atualmente não é suportado por padrão em muitos navegadores.

Nesse meio tempo, você pode usar a função como a seguir. Como a maioria das alterações no tamanho do elemento virá do redimensionamento da janela ou da alteração de algo no DOM. Você pode ouvir o redimensionamento da janela com o evento de resize da janela e ouvir as alterações do DOM usando o MutationObserver .

Aqui está um exemplo de uma função que o chamará de volta quando o tamanho do elemento fornecido for alterado como resultado de um desses eventos:

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

Eu tenho o seguinte exemplo de html, há um DIV que tem 100% de largura. Contém alguns elementos. Ao executar o redimensionamento das janelas, os elementos internos podem ser reposicionados e a dimensão da div pode mudar. Eu estou perguntando se é possível ligar o evento de alteração de dimensão do div? e como fazer isso? Eu atualmente vinculo a função de retorno de chamada ao evento de redimensionamento do jQuery no DIV de destino, no entanto, nenhum log de console é gerado, veja abaixo:

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

A melhor solução seria usar as chamadas Consultas de Elemento . No entanto, eles não são padrão, nenhuma especificação existe - e a única opção é usar um dos polyfills / libraries disponíveis, se você quiser seguir esse caminho.

A ideia por trás das consultas de elemento é permitir que um determinado contêiner na página responda ao espaço fornecido a ele. Isso permitirá gravar um componente uma vez e soltá-lo em qualquer lugar da página, enquanto ele ajustará seu conteúdo ao tamanho atual. Não importa o tamanho da janela. Essa é a primeira diferença que vemos entre consultas de elementos e consultas de mídia. Todos esperam que, em algum momento, seja criada uma especificação que padronize as consultas de elementos (ou algo que atinja o mesmo objetivo) e torne-as nativas, limpas, simples e robustas. A maioria das pessoas concorda que as consultas de mídia são bastante limitadas e não ajudam no design modular e na capacidade de resposta real.

Existem alguns polyfills / libraries que resolvem o problema de maneiras diferentes (podem ser chamados de soluções alternativas em vez de soluções):

Eu vi outras soluções para problemas semelhantes propostos. Geralmente eles usam temporizadores ou o tamanho da janela / viewport sob o capô, o que não é uma solução real. Além disso, acho que idealmente isso deve ser resolvido principalmente em CSS, e não em javascript ou html.


Dê uma olhada neste http://benalman.com/code/projects/jquery-resize/examples/resize/

Tem vários exemplos. Tente redimensionar sua janela e ver como os elementos dentro dos elementos do contêiner são ajustados.

Exemplo com js violino para explicar como fazê-lo funcionar.
Dê uma olhada neste violino http://jsfiddle.net/sgsqJ/4/

Nesse caso, resize () é ligado a um elemento com classe "test" e também ao objeto window e ao callback de redimensionamento do objeto window $ ('. Test'). Resize () é chamado.

por exemplo

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

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

Este post me ajudou a detectar eficientemente mudanças de tamanho nos elementos DOM.

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

Como usar este código ...

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

Código empacotado usado pelo exemplo ...

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

Nota: AppConfig é um namespace / objeto que eu uso para organizar funções reutilizáveis. Sinta-se à vontade para pesquisar e substituir o nome por qualquer coisa que desejar.


Eu não recomendo setTimeout() hack, pois diminui o desempenho! Em vez disso, você pode usar observadores de mutação do DOM para ouvir a alteração do tamanho 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>

Here's o violino
Tente mudar o tamanho div.

Você pode envolver ainda mais seu método no método de debounce para melhorar a eficiência. debounce acionará seu método a cada milésimo de segundo em vez de acionar cada milésimo de segundo em que o DIV está sendo redimensionado.


Existe um método muito eficiente para determinar se o tamanho de um elemento foi alterado.

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

Esta biblioteca tem uma classe ResizeSensor que pode ser usada para detecção de redimensionamento.
Ele usa uma abordagem baseada em eventos, por isso é extremamente rápido e não desperdiça tempo de CPU.

Exemplo:

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

Por favor, não use o plugin onresize do jQuery, pois ele usa o loop setTimeout() para checar as mudanças.
ISTO É INCRIVELMENTE LENTO E NÃO PRECISO .

Divulgação: estou diretamente associado a essa biblioteca.



Surpreendentemente, por mais antigo que esse problema seja, isso ainda é um problema na maioria dos navegadores.

Como outros disseram, o Chrome 64+ agora vem com Resize Observes nativamente, no entanto, a especificação ainda está sendo ajustada e o Chrome agora está (a partir de 2019-01-29) atrás da última edição da specification .

Eu vi alguns bons polyfills ResizeObserver em estado selvagem, no entanto, alguns não seguem a especificação de perto e outros têm alguns problemas de cálculo.

Eu estava precisando desesperadamente desse comportamento para criar alguns componentes da Web responsivos que poderiam ser usados ​​em qualquer aplicativo. Para fazê-los funcionar bem, eles precisam saber suas dimensões o tempo todo, então ResizeObservers soou ideal e eu decidi criar um polyfill que seguisse as especificações o mais próximo possível.

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

Demonstração: https://codesandbox.io/s/myqzvpmmy9


Usando o Clay.js ( https://github.com/zzarcon/clay ) é bem simples detectar alterações no tamanho do elemento:

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

el.on('resize', function(size) {
    console.log(size.height, size.width);
});

Você pode tentar o código no trecho a seguir, ele cobre suas necessidades usando o javascript simples. ( execute o snippet de código e clique no link de página inteira para acionar o alerta de que o div é redimensionado se você quiser testá-lo. ).

Baseado no fato de que este é um setInterval de 100 milissegundos, eu ousaria dizer que meu PC não encontrou muito com fome de CPU. (0,1% da CPU foi usada como total para todas as guias abertas no Chrome no momento testado.). Mas, novamente, isso é para apenas uma div, se você gostaria de fazer isso para uma grande quantidade de elementos, então sim, poderia ser muito com fome de CPU.

Você sempre pode usar um evento de clique para parar o sniffing div-resize de qualquer maneira.

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>

Você pode verificar o fiddle também

ATUALIZAR

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 atualizado


Você tem que ligar o evento resize no objeto window , não em um elemento html genérico.

Você poderia então usar isto:

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

e dentro da função de retorno de chamada, você pode verificar a nova largura de sua chamada div

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

Portanto, a resposta à sua pergunta é não , você não pode vincular o evento de resize a um div.


Only Window.onResize exists in the specification, but you can always utilize IFrame to generate new Window object inside your DIV.

Please check this answer . There is a new little jquery plugin , that is portable and easy to use. You can always check the source code to see how it's done.

<!-- (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...
}

This is a really old question, but I figured I'd post my solution to this.

I tried to use ResizeSensor since everyone seemed to have a pretty big crush on it. After implementing though, I realized that under the hood the Element Query requires the element in question to have position relative or absolute applied to it, which didn't work for my situation.

I ended up handling this with an Rxjs interval instead of a straight setTimeout or requestAnimationFrame like previous implementations.

What's nice about the observable flavor of an interval is that you get to modify the stream however any other observable can be handled. For me, a basic implementation was enough, but you could go crazy and do all sorts of merges, etc.

In the below example, I'm tracking the inner (green) div's width changes. It has a width set to 50%, but a max-width of 200px. Dragging the slider affects the wrapper (gray) div's width. You can see that the observable only fires when the inner div's width changes, which only happens if the outer div's width is smaller than 400px.

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>


i thought it couldn't be done but then i thought about it, you can manually resize a div via style="resize: both;" in order to do that you ave to click on it so added an onclick function to check element's height and width and it worked. With only 5 lines of pure javascript (sure it could be even shorter) 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>

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>


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

});




html