Wykrywanie zmiany wysokości elementu div za pomocą jQuery

141

Mam element div, który zawiera zawartość, która jest dodawana i usuwana dynamicznie, więc jej wysokość często się zmienia. Mam również element div, który jest całkowicie umieszczony bezpośrednio pod spodem za pomocą javascript, więc jeśli nie mogę wykryć, kiedy zmienia się wysokość elementu div, nie mogę zmienić jego położenia poniżej.

Jak więc mogę wykryć, kiedy zmienia się wysokość tego elementu div? Zakładam, że jest jakieś zdarzenie jQuery, którego muszę użyć, ale nie jestem pewien, do którego z nich się podłączyć.

Bob Somers
źródło
4
Zastanawiam się, dlaczego nie chciałoby się używać wtyczki podczas korzystania z jQuery.
Andre Calil
1
@ user007 Co powoduje zmianę rozmiaru elementów?
A. Wolff
@roasted moja wysokość div zmienia się, gdy jakiś element jest do niego dołączany, dołączanie jest wykonywane przez jquery
user007
1
@ user007, więc kiedy dodasz coś do DIV, możesz wywołać niestandardowe zdarzenie zmiany rozmiaru dla DIV. Innym sposobem (najgorsze IMO) mogłoby być wykorzystanie metody niestandardowej dla dołączania treści, które po prostu rozszerzenia natywne metody jQuery append (), użyj go z zwrotna przykład
A. Wolff
@roasted Dzięki za cynk.
user007

Odpowiedzi:

64

Użyj czujnika zmiany rozmiaru z biblioteki css-element-queries:

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

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

Wykorzystuje podejście oparte na zdarzeniach i nie marnuje czasu na procesor. Działa we wszystkich przeglądarkach, w tym. IE7 +.

Marc J. Schmidt
źródło
5
ładne rozwiązanie, wykorzystuje niewidoczny nowy element jako nakładkę na żądany cel i wykorzystuje zdarzenie „scroll” na nakładce, aby wywołać zmiany.
Eike Thies
2
Jest to jedyne rozwiązanie, które działa we wszystkich przypadkach, w tym zawartość i atrybuty nie zmienia jednak wymiary zmiany (na przykład: wymiary procentowych za pomocą CSS)
FF_Dev
2
Zdecydowanie najlepsze rozwiązanie.
dlsso
1
To nie jest tutaj najczęściej głosowana odpowiedź, to tragedia. To naprawdę działa w 100%.
Jimbo Jonny
Wydaje się, że to nie działa w przypadku elementów, które początkowo są ukryte.
greatwitenorth
48

Kiedyś napisałem wtyczkę do nasłuchiwania zmian, która w zasadzie dodaje funkcję odbiornika przy zmianie atrybutu. Chociaż mówię to jako wtyczka, w rzeczywistości jest to prosta funkcja napisana jako wtyczka jQuery ... więc jeśli chcesz ... usuń kod wtyczki i użyj jej podstawowych funkcji.

Uwaga: ten kod nie używa odpytywania

sprawdź to proste 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();
});

Strona wtyczki: http://meetselva.github.io/attrchange/

Wersja zminimalizowana: (1,68 kb)

(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
źródło
25
To rozwiązuje problem, jeśli ręcznie zmienisz rozmiar div, nie rozwiązuje to pierwotnego pytania, nie? Jeśli dodasz zawartość dynamicznie, nie wykryje ona zmiany wysokości: jsfiddle.net/aD49d/25
smets.kevin
Chciałbym również wiedzieć, czy istnieje sposób, aby to działało w przypadku zawartości dynamicznej.
EricP
4
@JoeCoder Ten kod zmiany opiera się na zdarzeniach DOM wyzwalanych przez przeglądarkę, gdy występuje akcja użytkownika. Jednak zawartość dynamiczna w tym przykładzie jest dołączana programowo, co niestety nie wyzwala żadnych zdarzeń. "Polling" wydaje się być kolejną łatwiejszą opcją. Inna opcja mogłaby zostać uruchomiona ręcznie po dołączeniu dynamicznej zawartości.
Selvakumar Arumugam
28

Możesz użyć zdarzenia DOMSubtreeModified

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

Ale to się uruchomi, nawet jeśli wymiary się nie zmienią, a ponowne przypisanie pozycji za każdym razem, gdy wystrzeli, może mieć wpływ na wydajność. Z mojego doświadczenia w stosowaniu tej metody wynika, że ​​sprawdzenie, czy wymiary uległy zmianie, jest tańsze, więc możesz rozważyć połączenie tych dwóch.

Lub jeśli bezpośrednio zmieniasz element div (zamiast zmieniać go pod wpływem danych wejściowych użytkownika w nieprzewidywalny sposób, np. Jeśli jest to contentEditable), możesz po prostu uruchomić zdarzenie niestandardowe w dowolnym momencie.

Wada: IE i Opera nie implementują tego zdarzenia.

bez powiek
źródło
12
IE i Opera nie implementują tego wydarzenia: quirksmode.org/dom/events/index.html
DEfusion
14
DomSubtreeModified został pozbawiony praw
Adonis K.Kakoulidis
2
Nowoczesnym sposobem na to byłoby użycie MutationObserver: developer.mozilla.org/en-US/docs/Web/API/MutationObserver
Christian Bankester
21

Oto jak ostatnio poradziłem sobie z tym problemem:

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

Wiem, że spóźniłem się na imprezę kilka lat, myślę, że moja odpowiedź może pomóc niektórym ludziom w przyszłości, bez konieczności pobierania jakichkolwiek wtyczek.

Kyle
źródło
1
Wydaje mi się, że pod pewnymi względami nie jest to najlepszy sposób, aby to zrobić, ale tak naprawdę to mi się podoba, ponieważ oznacza to, że możesz wywołać wywołanie zwrotne na końcu animacji. Wtyczka zmiany rozmiaru będzie się uruchamiać przez cały czas trwania animacji, co może być pomocne, ale może też powodować problemy. Ta metoda przynajmniej daje Ci kontrolę nad tym, kiedy uruchomić kontrolę wysokości.
andyface
Co dokładnie robi twój kod? Dlaczego nie możemy po prostu użyć kodu, który piszesz w zdarzeniu bind w funkcji, w której występuje wiązanie i wyzwalanie?
user1447420
Ale to nie jest automatyczne, prawda?
Albert Català,
17

Możesz użyć MutationObserverclass.

MutationObserverzapewnia programistom sposób reagowania na zmiany w DOM. Został zaprojektowany jako zamiennik zdarzeń mutacji zdefiniowanych w specyfikacji DOM3 Events.

Przykład ( źródło )

// 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
źródło
To jest poprawna odpowiedź. Przyjrzyj się również MutationSummary, bibliotece opartej
Christian Bankester
9

Istnieje wtyczka jQuery, która bardzo dobrze sobie z tym radzi

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

tutaj jest demo z różnymi scenariuszami zmiany wysokości, jeśli zmienisz rozmiar czerwonego obramowania div.

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

Val
źródło
Jest piękny, ale czy domyślnie nie sonduje interfejsu użytkownika co milisekundę pod kątem zmian? Czy to jest wydajne?
Michael Scott Cuthbert,
1
@MichaelScottCuthbert To było napisane dawno temu, ale użyłem setTimeout, więc w skrócie będzie to 1ms + [czas potrzebny na sprawdzenie], ale chodziło o to, aby słuchać bez przerwy, kiedy to zrobisz, uruchom go ponownie, to tak kolejka. Jestem pewien, że mógłbym to zrobić dużo lepiej, ale nie za dużo czasu.
Val
Ach, tak, widzę, że byłeś około rok przed rozwiązaniem Vegi (z którego ostatecznie korzystałem) i zanim wszyscy słuchacze DOMSubtreeModified itp. Otrzymali szerokie wsparcie. Wiele się nauczyłem czytając Twój kod, więc myślę, że warto zachować linki.
Michael Scott Cuthbert,
7

W odpowiedzi na user007:

Jeśli wysokość twojego elementu zmienia się z powodu dołączania do niego elementów za pomocą .append(), nie powinieneś potrzebować wykrywać zmiany wysokości. Po prostu dodaj zmianę położenia drugiego elementu w tej samej funkcji, w której dodajesz nową zawartość do pierwszego elementu.

Jak w:

Przykład roboczy

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

Możesz zrobić prosty 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();

EDYTOWAĆ:

To jest przykład z klasą. Ale możesz zrobić coś łatwiejszego.

Estefano Salazar
źródło
0

Możesz tego użyć, ale obsługuje tylko przeglądarki Firefox i Chrome.

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

Ronald Rivera
źródło
0

Całkiem proste, ale działa:

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

jQuery(window).resize(function () {
    editoHeightSize();
});
Zac
źródło
zostanie to uruchomione tylko wtedy, gdy rozmiar okna zostanie zmieniony, ale OP chce powiązać zdarzenie zmiany rozmiaru z określonym kontenerem
Fanie Void