Detectar cuándo cambia la altura de un div usando jQuery

141

Tengo un div que contiene algo de contenido que se agrega y elimina dinámicamente, por lo que su altura cambia con frecuencia. También tengo un div que está absolutamente ubicado directamente debajo de javascript, por lo que, a menos que pueda detectar cuándo cambia la altura del div, no puedo reposicionar el div debajo de él.

Entonces, ¿cómo puedo detectar cuándo cambia la altura de ese div? Supongo que hay algún evento jQuery que necesito usar, pero no estoy seguro de a cuál conectar.

Bob Somers
fuente
44
Me pregunto por qué uno no querría usar un complemento al usar jQuery.
Andre Calil
1
@ user007 ¿Qué hace que cambie el tamaño de sus elementos?
A. Wolff
@roasted mi altura de div cambia cuando se agrega algún elemento, jquery lo hace
user007
1
@ user007, de modo que cuando agrega algo al DIV, puede activar un evento de cambio de tamaño personalizado para el DIV. Otra forma (la peor OMI) podría ser usar un método personalizado para agregar contenido que simplemente extienda el método nativo jquery append (), usarlo con una devolución de llamada, por ejemplo
A. Wolff
@roasted Gracias por el consejo.
user007

Respuestas:

64

Use un sensor de cambio de tamaño de la biblioteca css-element-queries:

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

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

Utiliza un enfoque basado en eventos y no pierde el tiempo de su CPU. Funciona en todos los navegadores incl. IE7 +.

Marc J. Schmidt
fuente
55
buena solución, utiliza un nuevo elemento invisible como una superposición en el objetivo deseado y utiliza el evento "scroll" en la superposición para activar los cambios.
Eike Thies
2
Esta es la única solución que funciona en todos los casos, incluso cuando el contenido y los atributos no cambian pero las dimensiones cambian (ejemplo: dimensiones porcentuales usando CSS)
FF_Dev
2
La mejor solución con diferencia.
dlsso
1
Esta no es la respuesta más votada aquí es una tragedia. Esto realmente funciona al 100%.
Jimbo Jonny
Esto no parece funcionar para elementos que comienzan ocultos.
greatwitenorth
48

En algún momento escribí un complemento para Attrchange listener que básicamente agrega una función de escucha en el cambio de atributos. Aunque lo digo como un complemento, en realidad es una función simple escrita como un complemento jQuery ... así que si quieres ... elimina el código específico del complemento y usa las funciones principales.

Nota: Este código no usa sondeo

echa un vistazo a esta simple demostración 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 de complemento: http://meetselva.github.io/attrchange/

Versión minificada: (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
fuente
25
Esto resuelve el problema si cambia el tamaño del div manualmente, esto no resuelve la pregunta original ¿no? Si agrega contenido dinámicamente, no detecta el cambio de altura: jsfiddle.net/aD49d/25
smets.kevin
También me interesaría saber si hay una manera de hacer que esto funcione para contenido dinámico.
EricP
44
@JoeCoder Este código de intercambio se basa en eventos DOM activados por el navegador cuando hay una acción del usuario. Sin embargo, los contenidos dinámicos en ese ejemplo se incluyen mediante programación que desafortunadamente no desencadena ningún evento. La "encuesta" parece ser otra opción más fácil aquí ... otra opción podría activarse manualmente después de incluir el contenido dinámico.
Selvakumar Arumugam
28

Puede usar el evento DOMSubtreeModified

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

Pero esto se disparará incluso si las dimensiones no cambian, y reasignar la posición cada vez que se dispare puede tener un impacto en el rendimiento. En mi experiencia al usar este método, verificar si las dimensiones han cambiado es menos costoso y, por lo tanto, podría considerar combinar los dos.

O si está alterando directamente el div (en lugar de que el div sea alterado por la entrada del usuario de maneras impredecibles, como si es contentEditable), simplemente puede disparar un evento personalizado siempre que lo haga.

Desventaja: IE y Opera no implementan este evento.

párpado
fuente
12
IE y Opera no implementan este evento: quirksmode.org/dom/events/index.html
DEfusion
14
DomSubtreeModified consiguió depricated
Adonis K. Kakoulidis
2
La forma moderna de hacerlo sería utilizar un MutationObserver: developer.mozilla.org/en-US/docs/Web/API/MutationObserver
Christian Bankester
21

Así es como recientemente manejé este problema:

$('#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');
}

Sé que llego unos años tarde a la fiesta, solo creo que mi respuesta puede ayudar a algunas personas en el futuro, sin tener que descargar ningún complemento.

Kyle
fuente
1
Supongo que, de alguna manera, esta no es la mejor manera de hacerlo, pero en realidad me gusta bastante, ya que significa que puedes activarlo como una devolución de llamada al final de una animación. El complemento de cambio de tamaño seguirá disparando a lo largo de una animación, lo que podría ser útil, pero también podría causar problemas. Este método al menos le da el control de cuándo disparar la verificación de altura.
andyface
¿Qué hace exactamente tu código? ¿Por qué no podemos usar el código que escribes dentro del evento de vinculación en la función en lugar de vinculación y activación?
user1447420
Pero esto no es automático, ¿no?
Albert Català
17

Puedes usar la MutationObserverclase.

MutationObserverproporciona a los desarrolladores una forma de reaccionar a los cambios en un DOM. Está diseñado como un reemplazo para los eventos de mutación definidos en la especificación de eventos DOM3.

Ejemplo ( fuente )

// 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
fuente
Esta es la respuesta correcta. También eche un vistazo a MutationSummary, una biblioteca que se basa en MutationObservers: github.com/rafaelw/mutation-summary
Christian Bankester
9

Hay un complemento jQuery que puede manejar esto muy bien

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

Aquí hay una demostración con diferentes escenarios sobre cuándo cambia la altura, si cambia el tamaño del div con borde rojo.

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

Val
fuente
Es hermoso, pero ¿no sondea por defecto la interfaz de usuario cada milisegundo para detectar cambios? ¿Es eso eficiente?
Michael Scott Cuthbert
1
@MichaelScottCuthbert Fue escrito hace mucho tiempo, pero lo usé setTimeout, por lo que sería 1 ms + [el tiempo que lleva hacer las comprobaciones] en resumen, pero la idea era escuchar constantemente, una vez que lo hiciste volver a activarlo, es como una fila. Estoy seguro de que podría hacerlo mucho mejor, pero no mucho tiempo.
Val
Ah, sí, veo que estuviste aproximadamente un año antes de la solución de Vega (que finalmente utilicé) y antes de que todos los oyentes DOMSubtreeModified, etc. tuvieran un amplio apoyo. Sin embargo, aprendí un montón leyendo tu código, así que creo que vale la pena mantener los enlaces.
Michael Scott Cuthbert
7

En respuesta a user007:

Si la altura de su elemento está cambiando debido a los elementos que se le agregan usando, .append()no debería necesitar detectar el cambio de altura. Simplemente agregue la reposición de su segundo elemento en la misma función donde está agregando el nuevo contenido a su primer elemento.

Como en:

Ejemplo de trabajo

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

Puede hacer un simple 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 es un ejemplo con una clase. Pero puedes hacer algo más fácil.

Estefano Salazar
fuente
0

Puede usar esto, pero solo es compatible con Firefox y Chrome.

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

Ronald Rivera
fuente
0

Bastante básico pero funciona:

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

jQuery(window).resize(function () {
    editoHeightSize();
});
Zac
fuente
esto solo se activará si se cambia el tamaño de la ventana, pero OP desea vincular un evento de cambio de tamaño a un contenedor específico
Fanie Void