2008-10-05 39 views
139

Tengo un div que contiene algún contenido que se agrega y quita dinámicamente, por lo que su altura cambia con frecuencia. También tengo un div que está absolutamente posicionado directamente debajo con javascript, así que a menos que pueda detectar cuándo cambia la altura del div, no puedo reposicionar el div debajo de él.Detectando cuando cambia la altura de un div usando jQuery

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

+4

Me pregunto por qué uno no querría usar un plugin cuando se utiliza jQuery. –

+1

@ user007 ¿Qué hace que cambie el tamaño de tus elementos? –

+0

@roasted mi altura de div cambia cuando se agrega un elemento, se agrega mediante jquery – user007

Respuesta

28

Usted puede utilizar el evento DOMSubtreeModified

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

Pero esto se disparará aunque si las dimensiones no cambian, y la reasignación de la posición cada vez que se dispara puede tener un impacto en el rendimiento. En mi experiencia con este método, comprobar 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 fuera contentEditable), puede simplemente iniciar un evento personalizado siempre que lo haga.

Desventaja: IE y Opera no implementan este evento.

+12

IE y Opera no implementan este evento: http://www.quirksmode.org/dom/events/index.html – DEfusion

+2

True. Nota agregada. – eyelidlessness

+14

DomSubtreeModified se ha privado de –

9

hay un plugin jQuery que puede lidiar con esto muy bien

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

que aquí hay una demostración de la misma con diferentes escenarios en cuanto a cuando el cambio de altura, si cambia el tamaño de la div rojo con borde.

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

+0

Es hermoso, pero, ¿no lo hace de manera predeterminada sondear la interfaz de usuario cada milisegundo para ver los cambios? ¿Eso es eficiente? –

+1

@MichaelScottCuthbert Fue escrito hace mucho tiempo, pero usé 'setTimeout', por lo que sería 1ms + [tiempo que lleva hacer las comprobaciones] en resumen, pero la idea era escuchar constantemente, una vez que lo hiciste, eso lo desencadenó de nuevo, es como una cola. Estoy seguro de que podría hacerlo mucho mejor, pero no mucho más. – Val

+0

Ah, sí, veo que estaba aproximadamente un año antes de la solución de Vega (que finalmente utilicé) y antes de que todos los oyentes de DOMSubtreeModified, etc. recibieran un amplio apoyo. Aprendí muchísimo leyendo tu código, así que creo que vale la pena mantener los enlaces. –

20

Esto es como recientemente he manejado 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 soy unos años tarde a la fiesta, al igual que mi respuesta puede ayudar a algunas personas en el futuro, sin tener que descargar ningún complemento.

+1

Supongo que de alguna manera esta no es la mejor manera de hacerlo, pero en realidad me gusta bastante, ya que significa que puede 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 control sobre cuándo disparar el control de altura. – andyface

+0

¿Qué exactamente hace tu código? ¿Por qué no podemos usar el código que escribimos dentro del evento bind en la función de vinculación y activación? – user1447420

+0

Pero esto no es automático, ¿verdad? –

7

En respuesta a user007:

Si la altura de su elemento está cambiando debido a los artículos que se adjunta a él utilizando .append() que no es necesario para 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:

Working Example

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

escribí un plugin hace algún tiempo para attrchange oyente que básicamente añade una función de detector de cambio de atributo. Aunque lo digo como un plugin, en realidad es una función simple escrita como un plugin jQuery ... así que si quieres ... quita el código specfic del plugin y usa las funciones principales.

Nota: Este código no utiliza el sondeo

la salida 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 Plugin:http://meetselva.github.io/attrchange/

minified versión: (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) 
+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: http://jsfiddle.net/aD49d/25/ –

+0

También me interesaría saber si hay alguna manera de hacer que esto funcione para el contenido dinámico. – Dwayne

+4

@JoeCoder Este código de intercambio se basa en los eventos DOM activados por el navegador cuando hay una acción del usuario. Sin embargo, los contenidos dinámicos en ese ejemplo se incluyen programáticamente, lo que desafortunadamente no desencadena ningún evento. El "sondeo" parece ser otra opción más fácil aquí. Otra opción podría activarse manualmente después de que se incluye el contenido dinámico. –

62

utilizar un sensor de cambio de tamaño de la biblioteca CSS-elemento-consultas:

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

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

Se utiliza un enfoque basado en el evento y no pierda su tiempo de CPU. Funciona en todos los navegadores incl. IE7 +.

+5

buena solución, utiliza un nuevo elemento invisible como una superposición en el objetivo deseado y utiliza el evento "desplazamiento" en la superposición para activar los cambios. –

+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) –

+2

La mejor respuesta, necesita más votos – Alex

13

Puede usar la clase MutationObserver.

MutationObserver proporciona 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 (source)

// 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(); 
+2

Esto solo funcionaría en IE11 + http://caniuse.com/#feat=mutationobserver – Rafael

+0

Esta es la respuesta correcta. También eche un vistazo a MutationSummary, una biblioteca que se basa en MutationObservers: https://github.com/rafaelw/mutation-summary –

2

Usted puede hacer una setInterval sencilla.

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

EDIT:

Este es un ejemplo con una clase. Pero puedes hacer algo más fácil.

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

0

bastante básico pero funciona:

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

jQuery(window).resize(function() { 
    editoHeightSize(); 
}); 
Cuestiones relacionadas