Rilevare quando l'altezza di un div cambia usando jQuery


141

Ho un div che contiene alcuni contenuti che vengono aggiunti e rimossi in modo dinamico, quindi la sua altezza cambia spesso. Ho anche un div che è assolutamente posizionato direttamente sotto con javascript, quindi a meno che non riesca a rilevare quando l'altezza del div cambia, non posso riposizionare il div sotto di esso.

Quindi, come posso rilevare quando l'altezza di quel div cambia? Presumo ci sia qualche evento jQuery che devo usare, ma non sono sicuro a quale agganciarmi.


4
Mi chiedo perché non si vorrebbe usare un plugin quando si usa jQuery.
Andre Calil,

1
@ user007 Cosa cambia la dimensione dei tuoi elementi?
A. Wolff,

@roasted il mio div div cambia quando un oggetto viene aggiunto ad esso, append è fatto da jquery
user007

1
@ user007, quindi quando si aggiunge qualcosa al DIV, è possibile attivare un evento di ridimensionamento personalizzato per il DIV. Un altro modo (peggiore IMO) potrebbe essere quello di utilizzare un metodo personalizzato per aggiungere contenuti che semplicemente estendono il metodo append () jquery nativo, usandolo con un callback, ad es.
A. Wolff

@roasted Grazie per la punta.
user007

Risposte:


64

Utilizzare un sensore di ridimensionamento dalla libreria css-element-queries:

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

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

Utilizza un approccio basato sugli eventi e non fa perdere tempo alla CPU. Funziona in tutti i browser incl. IE7 +.


5
bella soluzione, utilizza un nuovo elemento invisibile come overlay sulla destinazione desiderata e utilizza l'evento "scroll" sull'overlay per attivare le modifiche.
Eike Thies,

2
Questa è l'unica soluzione che funziona in tutti i casi, anche quando il contenuto e gli attributi non cambiano ma le dimensioni cambiano (esempio: dimensioni percentuali usando css)
FF_Dev

2
La migliore soluzione di gran lunga.
dlsso,

1
Questa non essere la risposta più votata qui è una tragedia. Funziona davvero al 100%.
Jimbo Jonny,

Questo non sembra funzionare per elementi che iniziano nascosti.
greatwitenorth

48

Qualche tempo fa ho scritto un plug-in per il listener attrchange che sostanzialmente aggiunge una funzione listener al cambio di attributo. Anche se lo dico come un plugin, in realtà è una semplice funzione scritta come un plugin jQuery .. quindi se vuoi .. togli il codice specifico del plugin e usa le funzioni principali.

Nota: questo codice non utilizza il polling

Dai un'occhiata a questa semplice demo 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();
});

Pagina del plug-in: http://meetselva.github.io/attrchange/

Versione ridotta: (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
Questo risolve il problema se si ridimensiona il div manualmente, questo non risolve la domanda originale no? Se aggiungi contenuto in modo dinamico, non rileva il cambiamento di altezza: jsfiddle.net/aD49d/25
smets.kevin

Sarei anche interessato a sapere se c'era un modo per far funzionare questo per contenuti dinamici.
EricP,

4
@JoeCoder Questo codice di scambio si basa sugli eventi DOM attivati ​​dal browser quando viene eseguita un'azione da parte dell'utente. Tuttavia, i contenuti dinamici in quell'esempio sono inclusi a livello di programmazione che sfortunatamente non attiva alcun evento. Il "polling" sembra essere un'altra opzione più semplice qui. Un'altra opzione sarebbe in grado di attivarsi manualmente dopo l'inclusione del contenuto dinamico.
Selvakumar Arumugam,

28

È possibile utilizzare l'evento DOMSubtreeModified

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

Ma questo sparerà anche se le dimensioni non cambiano e riassegnare la posizione ogni volta che spara può subire un colpo di prestazione. Nella mia esperienza con questo metodo, verificare se le dimensioni sono cambiate è meno costoso e quindi potresti prendere in considerazione la combinazione delle due.

Oppure, se stai modificando direttamente il div (piuttosto che il div viene modificato dall'input dell'utente in modi imprevedibili, come se fosse contentEditable), puoi semplicemente generare un evento personalizzato ogni volta che lo fai.

Unico inconveniente: IE e Opera non implementano questo evento.


12
IE & Opera non implementano questo evento: quirksmode.org/dom/events/index.html
DEfusion,

14
DomSubtreeModified è stato depredato
Adonis K. Kakoulidis

2
Il modo moderno per farlo sarebbe quello di utilizzare un MutationObserver: developer.mozilla.org/en-US/docs/Web/API/MutationObserver
Christian Bankester,

21

Ecco come di recente ho gestito questo 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');
}

So di essere in ritardo di qualche anno alla festa, penso solo che la mia risposta potrebbe aiutare alcune persone in futuro, senza dover scaricare alcun plugin.


1
Immagino che in qualche modo questo non sia il modo migliore per farlo, ma in realtà mi piace molto, poiché significa che puoi attivarlo come richiamata alla fine di un'animazione. Il plug-in di ridimensionamento continuerà a essere attivato durante un'animazione, il che potrebbe essere utile, ma potrebbe anche causare problemi. Questo metodo ti dà almeno il controllo di quando sparare il controllo dell'altezza.
Andyface,

Cosa fa esattamente il tuo codice? Perché non possiamo semplicemente usare il codice che scrivi all'interno dell'evento bind nella funzione istanziata di binding e trigger?
user1447420

Ma questo non è automatico, vero?
Albert Català,

17

Puoi usare la MutationObserverclasse.

MutationObserverfornisce agli sviluppatori un modo per reagire alle modifiche in un DOM. È progettato come un sostituto per gli eventi di mutazione definiti nella specifica Eventi DOM3.

Esempio ( fonte )

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

Questa è la risposta corretta
Dai

9

Esiste un plug-in jQuery in grado di gestirlo molto bene

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

ecco una demo di esso con diversi scenari su quando l'altezza cambia, se si ridimensiona il div bordato rosso.

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


È bello, ma per impostazione predefinita non esegue il polling dell'interfaccia utente ogni millisecondo per le modifiche? È efficiente?
Michael Scott Cuthbert,

1
@MichaelScottCuthbert È stato scritto molto tempo fa, ma l'ho usato setTimeout, quindi sarebbe 1ms + [tempo impiegato per fare i controlli] in breve, ma l'idea era di ascoltare costantemente, una volta che l'hai fatto scattare di nuovo, è come una coda. Sono sicuro di poterlo fare molto meglio, ma non molto tempo.
Val

Ah, sì, vedo che eri circa un anno prima della soluzione di Vega (che alla fine ho usato) e prima che tutti gli ascoltatori DOMSubtreeModified, ecc. Fossero ampiamente supportati. Ho imparato molto leggendo il tuo codice, quindi penso che valga la pena mantenere i collegamenti.
Michael Scott Cuthbert,

7

In risposta a user007:

Se l'altezza del tuo elemento sta cambiando a causa dell'aggiunta di elementi ad esso, .append()non dovresti aver bisogno di rilevare il cambiamento di altezza. Aggiungi semplicemente il riposizionamento del secondo elemento nella stessa funzione in cui aggiungi il nuovo contenuto al primo elemento.

Come in:

Esempio di lavoro

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

2

Puoi creare un semplice 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();

MODIFICARE:

Questo è un esempio con una classe. Ma puoi fare qualcosa di più semplice.


0

Puoi usarlo, ma supporta solo Firefox e Chrome.

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


0

Abbastanza semplice ma funziona:

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

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

questo verrà attivato solo se la finestra viene ridimensionata, ma OP desidera associare un evento di ridimensionamento a un contenitore specifico
Fanie Void,
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.