Обнаружение изменения высоты div с помощью jQuery

141

У меня есть div, который содержит некоторый контент, который добавляется и удаляется динамически, поэтому его высота часто меняется. У меня также есть div, который абсолютно позиционируется непосредственно под ним с помощью javascript, поэтому, если я не могу определить, когда изменяется высота div, я не могу переместить div под ним.

Итак, как я могу определить, когда изменяется высота этого div? Я предполагаю, что мне нужно использовать какое-то событие jQuery, но я не уверен, к какому из них подключиться.

Боб Сомерс
источник
4
Интересно, почему бы не использовать плагин при использовании jQuery?
Андре Калил
1
@ user007 Что меняет размер ваших элементов?
A. Wolff
@roasted мой div высота меняется, когда к нему добавляется какой-то элемент, добавление выполняется jquery
user007
1
@ user007, поэтому, когда вы добавляете что-то в DIV, вы можете инициировать настраиваемое событие изменения размера для DIV. Другой способ (худший IMO) может заключаться в использовании пользовательского метода для добавления контента, который просто расширяет собственный метод jquery append (), использует его с обратным вызовом, например
А. Вольф
@roasted Спасибо за подсказку.
user007 07

Ответы:

64

Используйте датчик изменения размера из библиотеки css-element-query:

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

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

Он использует подход, основанный на событиях, и не тратит ваше время на процессор. Работает во всех браузерах, в т.ч. IE7 +.

Марк Дж. Шмидт
источник
5
хорошее решение, он использует невидимый новый элемент в качестве наложения на желаемую цель и использует событие «прокрутки» на наложении для запуска изменений.
Eike Thies
2
Это единственное решение, которое работает во всех случаях, в том числе когда содержимое и атрибуты не меняются, а размеры меняются (пример: процентные измерения с использованием CSS)
FF_Dev
2
Лучшее решение на сегодняшний день.
dlsso
1
Это не самый популярный ответ - трагедия. Это действительно работает на 100%.
Джимбо Джонни
Это не работает для элементов, которые начинаются скрытыми.
greatwitenorth
48

Когда-то я написал плагин для прослушивателя attrchange, который в основном добавляет функцию прослушивателя при изменении атрибута. Хотя я говорю это как плагин, на самом деле это простая функция, написанная как плагин jQuery ... так что, если вы хотите ... удалите специальный код плагина и используйте основные функции.

Примечание: этот код не использует опрос

посмотрите эту простую демонстрацию 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();
});

Страница плагина: http://meetselva.github.io/attrchange/

Минифицированная версия: (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
Это решает проблему, если вы изменяете размер div вручную, это не решает исходный вопрос, нет? Если вы добавляете контент динамически, он не обнаруживает изменения высоты: jsfiddle.net/aD49d/25
smets.kevin
Мне также было бы интересно узнать, есть ли способ заставить эту работу работать для динамического контента.
EricP
4
@JoeCoder Этот код attrchange полагается на события DOM, запускаемые браузером, когда есть действие пользователя. Однако динамическое содержимое в этом примере включено программно, что, к сожалению, не вызывает никаких событий. «Опрос» кажется здесь еще одним более простым вариантом ... другой вариант можно будет запускать вручную после включения динамического содержимого.
Сельвакумар Арумугам
28

Вы можете использовать событие DOMSubtreeModified

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

Но это сработает, даже если размеры не изменятся, а переназначение позиции при срабатывании может снизить производительность. По моему опыту использования этого метода, проверка того, изменились ли размеры, менее затратна, поэтому вы можете подумать об их объединении.

Или, если вы напрямую изменяете div (а не изменяете div непредсказуемым образом, например, если он является contentEditable), вы можете просто запускать настраиваемое событие всякий раз, когда вы это делаете.

Обратная сторона: IE и Opera не реализуют это событие.

без век
источник
12
IE и Opera не реализуют это событие: quirksmode.org/dom/events/index.html
DEfusion
14
DomSubtreeModified получил депригацию
Адонис К. Какулидис
2
Современный способ сделать это - использовать MutationObserver: developer.mozilla.org/en-US/docs/Web/API/MutationObserver
Christian Bankester
22

Вот как я недавно справился с этой проблемой:

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

Я знаю, что опаздываю на вечеринку на несколько лет, просто думаю, что мой ответ может помочь некоторым людям в будущем, без необходимости загружать какие-либо плагины.

Кайл
источник
1
Я думаю, что в некотором смысле это не лучший способ сделать это, но на самом деле мне он очень нравится, так как это означает, что вы можете запускать его как обратный вызов в конце анимации. Плагин изменения размера будет продолжать работать на протяжении всей анимации, что может быть полезно, но также может вызвать проблемы. Этот метод, по крайней мере, дает вам контроль над тем, когда запускать проверку высоты.
andyface
Что именно делает ваш код? Почему мы не можем просто использовать код, который вы пишете внутри события привязки, в функции привязки и запуска?
user1447420
Но это не происходит автоматически, не так ли?
Альберт Катала,
19

Вы можете использовать MutationObserverкласс.

MutationObserverпредоставляет разработчикам способ реагировать на изменения в DOM. Он разработан как замена для событий мутации, определенных в спецификации событий DOM3.

Пример ( источник )

// 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
источник
Это правильный ответ. Также взгляните на MutationSummary, библиотеку, основанную на MutationObservers: github.com/rafaelw/mutation-summary
Christian Bankester
9

Есть плагин jQuery, который отлично справляется с этим.

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

вот его демонстрация с различными сценариями изменения высоты, если вы измените размер div с красной рамкой.

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

Вал
источник
Это красиво, но разве он по умолчанию не опрашивает пользовательский интерфейс каждую миллисекунду на предмет изменений? Это эффективно?
Майкл Скотт Катберт
1
@MichaelScottCuthbert Это было написано давно, но я использовал setTimeout, так что это будет 1 мс + [время, необходимое для выполнения проверок] вкратце, но идея заключалась в том, чтобы постоянно слушать, как только вы сделаете это снова, это похоже на очередь. Я уверен, что смогу сделать это намного лучше, но не так много времени.
Val
Ах, да, я вижу, что вы были примерно за год до решения Vega (которое я в конечном итоге использовал) и до того, как все слушатели DOMSubtreeModified и т. Д. Получили широкую поддержку. Я многому научился, читая ваш код, поэтому думаю, что стоит поддерживать ссылки.
Майкл Скотт Катберт,
7

В ответ на user007:

Если высота вашего элемента изменяется из-за добавления к нему элементов с помощью, .append()вам не нужно обнаруживать изменение высоты. Просто добавьте репозицию второго элемента в ту же функцию, в которой вы добавляете новое содержимое к первому элементу.

Как в:

Рабочий пример

$('.class1').click(function () {
    $('.class1').append("<div class='newClass'><h1>This is some content</h1></div>");
    $('.class2').css('top', $('.class1').offset().top + $('.class1').outerHeight());
});
апаул
источник
2

Вы можете сделать простой 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();

РЕДАКТИРОВАТЬ:

Это пример с классом. Но вы можете сделать что-нибудь простое.

Эстефано Салазар
источник
0

Вы можете использовать это, но он поддерживает только Firefox и Chrome.

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

Рональд Ривера
источник
0

Довольно простой, но работает:

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

jQuery(window).resize(function () {
    editoHeightSize();
});
Зак
источник
это будет срабатывать только в том случае, если размер окна изменится, но OP хочет привязать событие изменения размера к определенному контейнеру
Fanie Void