Событие jQuery: обнаружение изменений в html / тексте div


266

У меня есть DIV , который имеет свое содержание все время меняется, будь то ajax requests, jquery functions, и blurт.д. и т.п.

Есть ли способ, которым я могу обнаружить какие-либо изменения в моем div в любой момент времени?

Я не хочу использовать интервалы или значение по умолчанию.

Нечто подобное сделало бы

$('mydiv').contentchanged() {
 alert('changed')
}

5
Возможный дубликат: stackoverflow.com/questions/10328102/…
Роб

14
@Rob Это связывает keypressсобытие с contenteditable <div>элементом. Я не уверен, что решения там применимы к этому. Они определенно не поймут никаких программных изменений в содержании элемента.
Энтони Грист

Ответы:


409

Если вы не хотите использовать таймер и проверить innerHTML, вы можете попробовать это событие

$('mydiv').bind('DOMSubtreeModified', function(){
  console.log('changed');
});

Более подробная информация и данные о поддержке браузера находятся здесь .

Внимание: в новых версиях jQuery bind () устарела, поэтому вместо него следует использовать on ():

$('body').on('DOMSubtreeModified', 'mydiv', function(){
  console.log('changed');
});

14
Помните, что DOMSubtreeModified не поддерживается в IE8 (и ниже).
Гэвин


3
Mozilla 33: упала в рекурсии для элемента <body>. Нужно было найти другой путь
Chaki_Black

17
Серьезно. НЕ используйте это событие, оно приведет к краху всей вашей работы, потому что оно все время срабатывает. Вместо этого используйте события ниже $ ('. MyDiv'). Bind ('DOMNodeInserted DOMNodeRemoved', function () {});
Джордж СЕДРА

19
Этот метод устарел! Вместо этого используйте:$("body").on('DOMSubtreeModified', "mydiv", function() { });
Asif

55

Использование Javascript MutationObserver

  //More Details https://developer.mozilla.org/en-US/docs/Web/API/MutationObserver
 // select the target node
var target = document.querySelector('mydiv')
// create an observer instance
var observer = new MutationObserver(function(mutations) {
  console.log($('mydiv').text());   
});
// 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);

6
Это правильный ответ, так как теперь он более предпочтителен, чем использование DOMSubtreeModified
Джоэл Дейви,

3
Я получаю ошибку с этим, хотя я дал правильный селектор. «VM21504: 819 Uncaught TypeError: Не удалось выполнить« наблюдение »на« MutationObserver »: параметр 1 не относится к типу« Узел »."
Самир


42

Вы можете попробовать это

$('.myDiv').bind('DOMNodeInserted DOMNodeRemoved', function() {

});

но это может не работать в Internet Explorer, не проверял



3
ПРИМЕЧАНИЕ !: если вы используете это для триггера, и на страницу будет внесено много изменений - она ​​будет запускать вашу функцию X раз (возможно, даже X = 1000 или более), что может быть очень неэффективно. Одно простое решение - определить «работающий» логический var, который будет ... if (running == true) {return} ... без запуска вашего кода, если он уже запущен. Установите running = true сразу после логики if, и running = false до выхода из функции. Вы также можете использовать таймер, чтобы ограничить вашу функцию, чтобы иметь возможность запускаться каждые X секунд. работает = истина; SetTimeout (функция () {работает = ложь}, 5000); (или что-то лучше)
JxAxMxIxN

Я использовал это в окне выбора, в котором были добавлены и удалены параметры. Это работало замечательно, когда элементы были добавлены, но удаление, казалось, было 1 пунктом позади. Когда последний вариант был удален, он не сработал.
CodeMonkey

2
@JxAxMxIxN Вы также можете увеличить таймер тайм-аута, снова очистив и установив тайм-аут:clearTimeout(window.something); window.something = setTimeout(...);
Ctrl-C

согласился - твой путь - путь вперед - с тех пор, как я изучал Python, я избавился от многих моих плохих практик кодирования на нескольких языках (не на всех, только на большом количестве;)
JxAxMxIxN

33

Вы ищете MutationObserver или Mutation Events . Ни везде, ни в мире разработчиков не поддерживаются.

Если вы знаете (и можете быть уверены, что размер div изменится), вы можете использовать событие изменения размера кроссбраузера .


1
Это тот самый. В частности, DOMSubtreeModified . Может оказаться полезной библиотека итоговых мутаций и этот список событий дерева DOM .
БенджаминRH

1
это событие устарело developer.mozilla.org/en-US/docs/Web/Guide/Events/…
Adrien Be

9
Если кому-то еще приходилось пытаться прочитать все везде, это правильный ответ. События мутации поддерживались в предыдущих браузерах, Mutation Observer - это то, что будет поддерживаться в современных браузерах и будет поддерживаться в будущем. Смотрите ссылку для поддержки: CANIUSE Mutation Observer
Джош Мак

20

Следующий код работает для меня.

$("body").on('DOMSubtreeModified', "mydiv", function() {
    alert('changed');
});

Надеюсь, это поможет кому-то :)


Это тот же ответ, что и у @Artley
Black

@ Чёрный Спасибо! Я только что проверил ответ Артли. Я позабочусь об этом в следующий раз.
Санчит Гупта

17

Нет встроенного решения этой проблемы, это проблема вашего дизайна и шаблона кодирования.

Вы можете использовать шаблон издателя / подписчика. Для этого вы можете использовать пользовательские события jQuery или собственный механизм событий.

Первый,

function changeHtml(selector, html) {
    var elem = $(selector);
    jQuery.event.trigger('htmlchanging', { elements: elem, content: { current: elem.html(), pending: html} });
    elem.html(html);
    jQuery.event.trigger('htmlchanged', { elements: elem, content: html });
}

Теперь вы можете подписаться на события divhtmlchanging / divhtmloted следующим образом:

$(document).bind('htmlchanging', function (e, data) {
    //your before changing html, logic goes here
});

$(document).bind('htmlchanged', function (e, data) {
    //your after changed html, logic goes here
});

Теперь вы должны изменить свои изменения содержимого div с помощью этой changeHtml()функции. Таким образом, вы можете отслеживать или делать необходимые изменения соответствующим образом, поскольку связываете аргумент данных обратного вызова, содержащий информацию.

Вы должны изменить HTML вашего div таким образом;

changeHtml('#mydiv', '<p>test content</p>');

А также, вы можете использовать это для любого элемента (ов) HTML, кроме элемента ввода. В любом случае вы можете изменить это, чтобы использовать с любым элементом (ами).


Чтобы наблюдать за изменениями в конкретном элементе и воздействовать на них, просто измените функцию changeHtml, чтобы использовать «elem.trigger (...)» вместо «jQuery.event.trigger (...)», а затем выполнить привязку к элементу следующим образом: $ ('# my_element_id'). on ('htmloted', функция (e, data) {...}
KenB

8
«Это проблема с вашим дизайном и шаблоном кодирования», что делать, если вы включаете сторонние скрипты, поэтому не можете контролировать их исходный код? но вам нужно обнаружить их изменения в одном div?
DrLightman

Основное правило @DrLightman - выбирать стороннюю библиотеку с предоставленным событием обратного вызова
Марсель Джаман

8

Используйте MutationObserver, как показано в этом фрагменте, предоставленном Mozilla , и адаптирован из этого поста в блоге.

Кроме того, вы можете использовать пример JQuery, показанный в этой ссылке

Chrome 18+, Firefox 14+, IE 11+, Safari 6+

// Select the node that will be observed for mutations
var targetNode = document.getElementById('some-id');

// Options for the observer (which mutations to observe)
var config = { attributes: true, childList: true };

// Callback function to execute when mutations are observed
var callback = function(mutationsList) {
    for(var mutation of mutationsList) {
        if (mutation.type == 'childList') {
            console.log('A child node has been added or removed.');
        }
        else if (mutation.type == 'attributes') {
            console.log('The ' + mutation.attributeName + ' attribute was modified.');
        }
    }
};

// Create an observer instance linked to the callback function
var observer = new MutationObserver(callback);

// Start observing the target node for configured mutations
observer.observe(targetNode, config);

// Later, you can stop observing
observer.disconnect();

3

Вы можете сохранить старый innerHTML элемента div в переменной. Установите интервал, чтобы проверить, соответствует ли старый контент текущему контенту. Когда это не правда, сделай что-нибудь.


1

Попробуйте MutationObserver:

поддержка браузера: http://caniuse.com/#feat=mutationobserver

<html>
  <!-- example from Microsoft https://developer.microsoft.com/en-us/microsoft-edge/platform/documentation/dev-guide/dom/mutation-observers/ -->

  <head>
    </head>
  <body>
    <script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
    <script type="text/javascript">
      // Inspect the array of MutationRecord objects to identify the nature of the change
function mutationObjectCallback(mutationRecordsList) {
  console.log("mutationObjectCallback invoked.");

  mutationRecordsList.forEach(function(mutationRecord) {
    console.log("Type of mutation: " + mutationRecord.type);
    if ("attributes" === mutationRecord.type) {
      console.log("Old attribute value: " + mutationRecord.oldValue);
    }
  });
}
      
// Create an observer object and assign a callback function
var observerObject = new MutationObserver(mutationObjectCallback);

      // the target to watch, this could be #yourUniqueDiv 
      // we use the body to watch for changes
var targetObject = document.body; 
      
// Register the target node to observe and specify which DOM changes to watch
      
      
observerObject.observe(targetObject, { 
  attributes: true,
  attributeFilter: ["id", "dir"],
  attributeOldValue: true,
  childList: true
});

// This will invoke the mutationObjectCallback function (but only after all script in this
// scope has run). For now, it simply queues a MutationRecord object with the change information
targetObject.appendChild(document.createElement('div'));

// Now a second MutationRecord object will be added, this time for an attribute change
targetObject.dir = 'rtl';


      </script>
    </body>
  </html>


0

При добавлении некоторого содержимого в div, будь то через jQuery или напрямую через de DOM-API, по умолчанию используется .appendChild()функция. Что вы можете сделать, это переопределить .appendChild()функцию текущего объекта и внедрить в него наблюдателя. Теперь, переопределив нашу .appendChild()функцию, нам нужно позаимствовать эту функцию у другого объекта, чтобы иметь возможность добавлять содержимое. Для этого мы вызываем .appendChild()другой div, чтобы, наконец, добавить содержимое. Конечно, это имеет значение и для .removeChild().

var obj = document.getElementById("mydiv");
    obj.appendChild = function(node) {
        alert("changed!");

        // call the .appendChild() function of some other div
        // and pass the current (this) to let the function affect it.
        document.createElement("div").appendChild.call(this, node);
        }
    };

Здесь вы можете найти наивный пример. Вы можете расширить это самостоятельно, я думаю. http://jsfiddle.net/RKLmA/31/

Кстати: это показывает, что JavaScript соответствует принципу OpenClosed. :)


Он не работает с append child ... На самом деле я изменяю его HTML через другие функции.
BoqBoq

Вроде removeChild () replaceChild () и т. Д. Но вы правы в innerHTML. Вам следует как-то избегать этого.
Андриес
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.