Detectando quando a altura de uma div muda usando jQuery

141

Eu tenho uma div que contém algum conteúdo que está sendo adicionado e removido dinamicamente, portanto, sua altura muda frequentemente. Eu também tenho uma div que está absolutamente posicionada diretamente abaixo do javascript, portanto, a menos que eu possa detectar quando a altura da div muda, não posso reposicionar a div abaixo dela.

Então, como posso detectar quando a altura dessa div muda? Suponho que exista algum evento jQuery que preciso usar, mas não tenho certeza em qual conectar.

Bob Somers
fonte
4
Eu me pergunto por que alguém não gostaria de usar um plugin ao usar o jQuery.
Andre Calil
1
@ user007 O que altera o tamanho dos seus elementos?
A.Wolff
@roasted minha altura div alterações quando algum item anexado a ela, de acréscimo é feito por jquery
user007
1
@ user007, portanto, quando você acrescenta algo ao DIV, pode disparar um evento de redimensionamento personalizado para o DIV. Uma outra maneira (pior IMO) poderia ser a utilização de um método personalizado para acrescentar conteúdo que simplesmente estender método nativo jQuery append (), usá-lo com uma chamada de retorno por exemplo
A. Wolff
@roasted Obrigado pela dica.
user007

Respostas:

64

Use um sensor de redimensionamento da biblioteca css-element-queries:

https://github.com/marcj/css-element-queries

new ResizeSensor(jQuery('#myElement'), function() {
    console.log('myelement has been resized');
});

Ele usa uma abordagem baseada em eventos e não perde tempo com a CPU. Funciona em todos os navegadores, incl. IE7 +.

Marc J. Schmidt
fonte
5
solução agradável, ele usa um novo elemento invisível como uma sobreposição no destino desejado e utiliza o evento "scroll" na sobreposição para acionar alterações.
Eike Thies
2
Esta é a única solução que funciona em todos os casos, incluindo quando o conteúdo e atributos não muda, mas dimensões alterações (exemplo: dimensões percentuais usando css)
FF_Dev
2
Melhor solução de longe.
dlsso
1
Essa não ser a resposta mais votada aqui é uma tragédia. Isso realmente funciona 100%.
precisa
Isso não parece funcionar para elementos que começam ocultos.
greatwitenorth
48

Eu escrevi um plugin em algum momento para o ouvinte attrchange que basicamente adiciona uma função de ouvinte na alteração de atributo. Embora eu o diga como um plug-in, na verdade é uma função simples escrita como um plugin jQuery .. então, se você quiser .. retire o código específico do plug-in e use as funções principais.

Nota: este código não usa polling

confira esta demonstração simples http://jsfiddle.net/aD49d/

$(function () {
    var prevHeight = $('#test').height();
    $('#test').attrchange({
        callback: function (e) {
            var curHeight = $(this).height();            
            if (prevHeight !== curHeight) {
               $('#logger').text('height changed from ' + prevHeight + ' to ' + curHeight);

                prevHeight = curHeight;
            }            
        }
    }).resizable();
});

Página do plug-in: http://meetselva.github.io/attrchange/

Versão reduzida: (1.68kb)

(function(e){function t(){var e=document.createElement("p");var t=false;if(e.addEventListener)e.addEventListener("DOMAttrModified",function(){t=true},false);else if(e.attachEvent)e.attachEvent("onDOMAttrModified",function(){t=true});else return false;e.setAttribute("id","target");return t}function n(t,n){if(t){var r=this.data("attr-old-value");if(n.attributeName.indexOf("style")>=0){if(!r["style"])r["style"]={};var i=n.attributeName.split(".");n.attributeName=i[0];n.oldValue=r["style"][i[1]];n.newValue=i[1]+":"+this.prop("style")[e.camelCase(i[1])];r["style"][i[1]]=n.newValue}else{n.oldValue=r[n.attributeName];n.newValue=this.attr(n.attributeName);r[n.attributeName]=n.newValue}this.data("attr-old-value",r)}}var r=window.MutationObserver||window.WebKitMutationObserver;e.fn.attrchange=function(i){var s={trackValues:false,callback:e.noop};if(typeof i==="function"){s.callback=i}else{e.extend(s,i)}if(s.trackValues){e(this).each(function(t,n){var r={};for(var i,t=0,s=n.attributes,o=s.length;t<o;t++){i=s.item(t);r[i.nodeName]=i.value}e(this).data("attr-old-value",r)})}if(r){var o={subtree:false,attributes:true,attributeOldValue:s.trackValues};var u=new r(function(t){t.forEach(function(t){var n=t.target;if(s.trackValues){t.newValue=e(n).attr(t.attributeName)}s.callback.call(n,t)})});return this.each(function(){u.observe(this,o)})}else if(t()){return this.on("DOMAttrModified",function(e){if(e.originalEvent)e=e.originalEvent;e.attributeName=e.attrName;e.oldValue=e.prevValue;s.callback.call(this,e)})}else if("onpropertychange"in document.body){return this.on("propertychange",function(t){t.attributeName=window.event.propertyName;n.call(e(this),s.trackValues,t);s.callback.call(this,t)})}return this}})(jQuery)
Selvakumar Arumugam
fonte
25
Isso resolve o problema se você redimensionar a div manualmente, isso não resolve a questão original, não? Se você adicionar conteúdo dinamicamente ele não detectar a mudança de altura: jsfiddle.net/aD49d/25
smets.kevin
Eu também estaria interessado em saber se havia uma maneira de fazer isso funcionar com conteúdo dinâmico.
EricP
4
@JoeCoder Este código de troca depende de eventos DOM acionados pelo navegador quando há uma ação do usuário. No entanto, o conteúdo dinâmico desse exemplo é incluído programaticamente, o que infelizmente não aciona nenhum evento. "Polling" parece ser outra opção mais fácil aqui .. outra opção seria capaz de disparar manualmente após a inclusão do conteúdo dinâmico.
Selvakumar Arumugam 22/09
28

Você pode usar o evento DOMSubtreeModified

$(something).bind('DOMSubtreeModified' ...

Mas isso será acionado mesmo que as dimensões não sejam alteradas, e reatribuir a posição sempre que disparar pode sofrer um impacto no desempenho. Na minha experiência usando esse método, verificar se as dimensões foram alteradas é menos caro e, portanto, você pode considerar combinar as duas.

Ou se você estiver alterando diretamente o div (em vez de o div ser alterado pela entrada do usuário de maneiras imprevisíveis, como se for contentEditable), você pode simplesmente disparar um evento personalizado sempre que o fizer.

Desvantagem: IE e Opera não implementam este evento.

ausência de pálpebra
fonte
12
IE e Opera não implementar este evento: quirksmode.org/dom/events/index.html
DEfusion
14
O DomSubtreeModified ficou obsoleto.
Adonis K. Kakoulidis
2
A maneira moderna de fazer isso seria usar um MutationObserver: developer.mozilla.org/en-US/docs/Web/API/MutationObserver
Christian Bankester
21

Foi assim que eu lidei com esse problema recentemente:

$('#your-resizing-div').bind('getheight', function() {
    $('#your-resizing-div').height();
});

function your_function_to_load_content() {
    /*whatever your thing does*/
    $('#your-resizing-div').trigger('getheight');
}

Sei que estou alguns anos atrasado para a festa, acho que minha resposta pode ajudar algumas pessoas no futuro, sem precisar baixar nenhum plug-in.

Kyle
fonte
1
Acho que, de certa forma, essa não é a melhor maneira de fazê-lo, mas na verdade gosto bastante, pois significa que você pode acioná-lo como um retorno de chamada no final de uma animação. O plug-in de redimensionamento continuará disparando durante uma animação, o que pode ser útil, mas também pode causar problemas. Este método, pelo menos, permite controlar quando acionar a verificação de altura.
andyface
O que exatamente seu código faz? Por que não podemos simplesmente usar o código que você escreve dentro do evento bind na função insted de binding e trigger?
user1447420
Mas isso não é automático, não é?
Albert Català
17

Você pode usar MutationObserver classe.

MutationObserverfornece aos desenvolvedores uma maneira de reagir às alterações em um DOM. Ele foi projetado como um substituto para os Eventos de Mutação definidos na especificação de Eventos do DOM3.

Exemplo ( origem )

// select the target node
var target = document.querySelector('#some-id');

// create an observer instance
var observer = new MutationObserver(function(mutations) {
  mutations.forEach(function(mutation) {
    console.log(mutation.type);
  });    
});

// configuration of the observer:
var config = { attributes: true, childList: true, characterData: true };

// pass in the target node, as well as the observer options
observer.observe(target, config);

// later, you can stop observing
observer.disconnect();
EFernandes
fonte
Essa é a resposta correta. Também dê uma olhada MutationSummary, uma biblioteca que se baseia em MutationObservers: github.com/rafaelw/mutation-summary
Christian Bankester
9

Existe um plugin jQuery que pode lidar com isso muito bem

http://www.jqui.net/jquery-projects/jquery-mutate-official/

aqui está uma demonstração dele com diferentes cenários sobre quando a altura muda, se você redimensionar a div com borda vermelha.

http://www.jqui.net/demo/mutate/

Val
fonte
É bonito, mas por padrão não faz pesquisas na interface do usuário a cada milissegundo em busca de alterações? Isso é eficiente?
Michael Scott Cuthbert
1
@MichaelScottCuthbert Foi escrito há muito tempo, mas eu usei setTimeout, então seria 1ms + [tempo necessário para fazer as verificações] em suma, mas a ideia era ouvir constantemente, depois que você o acionasse novamente, é como uma fila. Estou certo de que poderia fazê-lo muito melhor, mas não muito tempo.
Val
Ah, sim, vejo que você estava cerca de um ano antes da solução da Vega (que eu finalmente usei) e antes de todos os ouvintes do DOMSubtreeModified etc., eram amplamente suportados. Eu aprendi muito lendo o seu código, então acho que vale a pena manter os links atualizados.
Michael Scott Cuthbert
7

Em resposta ao usuário007:

Se a altura do seu elemento estiver mudando devido à adição de itens a ele usando .append() você não precisará detectar a alteração na altura. Basta adicionar a reposição do seu segundo elemento na mesma função em que você está anexando o novo conteúdo ao seu primeiro elemento.

Como em:

Exemplo de trabalho

$('.class1').click(function () {
    $('.class1').append("<div class='newClass'><h1>This is some content</h1></div>");
    $('.class2').css('top', $('.class1').offset().top + $('.class1').outerHeight());
});
apaul
fonte
2

Você pode criar um simples setInterval.

function someJsClass()
{
  var _resizeInterval = null;
  var _lastHeight = 0;
  var _lastWidth = 0;
  
  this.Initialize = function(){
    var _resizeInterval = setInterval(_resizeIntervalTick, 200);
  };
  
  this.Stop = function(){
    if(_resizeInterval != null)
      clearInterval(_resizeInterval);
  };
  
  var _resizeIntervalTick = function () {
    if ($(yourDiv).width() != _lastWidth || $(yourDiv).height() != _lastHeight) {
      _lastWidth = $(contentBox).width();
      _lastHeight = $(contentBox).height();
      DoWhatYouWantWhenTheSizeChange();
    }
  };
}

var class = new someJsClass();
class.Initialize();

EDITAR:

Este é um exemplo com uma classe. Mas você pode fazer algo mais fácil.

Estefano Salazar
fonte
0

Você pode usar isso, mas ele suporta apenas Firefox e Chrome.

$(element).bind('DOMSubtreeModified', function () {
  var $this = this;
  var updateHeight = function () {
    var Height = $($this).height();
    console.log(Height);
  };
  setTimeout(updateHeight, 2000);
});

Ronald Rivera
fonte
0

Bastante básico, mas funciona:

function dynamicHeight() {
    var height = jQuery('').height();
    jQuery('.edito-wrapper').css('height', editoHeight);
}
editoHeightSize();

jQuery(window).resize(function () {
    editoHeightSize();
});
Zac
fonte
isso só será acionado se a janela é redimensionada, mas OP deseja vincular um evento de redimensionamento para um recipiente específico
Fanie Vazio