Обнаружение изменений в DOM
Я хочу выполнить функцию, когда некоторые div или входные данные добавляются в html. Это возможно?
например, добавляется текстовый ввод, затем должна быть вызвана функция.
7 ответов:
обновление 2015, новый
MutationObserverподдерживается современными браузерами:Chrome 18+, Firefox 14+, IE 11+, Safari 6+
Если вам нужно поддерживать старые, вы можете попытаться вернуться к другим подходам, как те, которые упомянуты в этом 5 (!) год назад ответ ниже. Там есть драконы. Наслаждайтесь :)
кто-то еще менять документ? Потому что если у вас есть полный контроль над изменениями вы просто нужно создать свой собственный
domChangedAPI - с функцией или пользовательским событием-и запускать / вызывать его везде, где вы изменяете вещи.The DOM Level-2 имеет типы событий мутации, но более старая версия IE не поддерживает его. Обратите внимание, что события мутации устарело в спецификации событий DOM3 и производительность.
вы можете попробовать эмулировать событие мутации с
onpropertychangeв IE (и вернитесь к подходу грубой силы, если ни один из них не доступен).на полное domChange интервал может быть чрезмерным убийством. Представьте, что вам нужно сохранить текущее состояние всего документа и изучить каждое свойство каждого элемента, чтобы быть одинаковым.
может быть, если вас интересуют только элементы и их порядок (как вы упомянули в своем вопросе), a
getElementsByTagName("*")может работать. Это сработает автоматически, если вы добавите элемент, удалите элемент, заменить элементы или изменить структуру документа.я написал доказательство концепции:
(function (window) { var last = +new Date(); var delay = 100; // default delay // Manage event queue var stack = []; function callback() { var now = +new Date(); if (now - last > delay) { for (var i = 0; i < stack.length; i++) { stack[i](); } last = now; } } // Public interface var onDomChange = function (fn, newdelay) { if (newdelay) delay = newdelay; stack.push(fn); }; // Naive approach for compatibility function naive() { var last = document.getElementsByTagName('*'); var lastlen = last.length; var timer = setTimeout(function check() { // get current state of the document var current = document.getElementsByTagName('*'); var len = current.length; // if the length is different // it's fairly obvious if (len != lastlen) { // just make sure the loop finishes early last = []; } // go check every element in order for (var i = 0; i < len; i++) { if (current[i] !== last[i]) { callback(); last = current; lastlen = len; break; } } // over, and over, and over again setTimeout(check, delay); }, delay); } // // Check for mutation events support // var support = {}; var el = document.documentElement; var remain = 3; // callback for the tests function decide() { if (support.DOMNodeInserted) { window.addEventListener("DOMContentLoaded", function () { if (support.DOMSubtreeModified) { // for FF 3+, Chrome el.addEventListener('DOMSubtreeModified', callback, false); } else { // for FF 2, Safari, Opera 9.6+ el.addEventListener('DOMNodeInserted', callback, false); el.addEventListener('DOMNodeRemoved', callback, false); } }, false); } else if (document.onpropertychange) { // for IE 5.5+ document.onpropertychange = callback; } else { // fallback naive(); } } // checks a particular event function test(event) { el.addEventListener(event, function fn() { support[event] = true; el.removeEventListener(event, fn, false); if (--remain === 0) decide(); }, false); } // attach test events if (window.addEventListener) { test('DOMSubtreeModified'); test('DOMNodeInserted'); test('DOMNodeRemoved'); } else { decide(); } // do the dummy test var dummy = document.createElement("div"); el.appendChild(dummy); el.removeChild(dummy); // expose window.onDomChange = onDomChange; })(window);использование:
onDomChange(function(){ alert("The Times They Are a-Changin'"); });это работает на IE 5.5+, FF 2+, Chrome, Safari 3+ и Opera 9.6+
Это окончательный подход до сих пор, с наименьшим кодом:
IE9+, FF, Webkit:
используя MutationObserver и возвращаясь к устаревшим событий мутации если нужно:
(пример ниже, если только для изменений DOM, касающихся добавленных или удаленных узлов)var observeDOM = (function(){ var MutationObserver = window.MutationObserver || window.WebKitMutationObserver; return function( obj, callback ){ if( !obj || !obj.nodeType === 1 ) return; // validation if( MutationObserver ){ // define a new observer var obs = new MutationObserver(function(mutations, observer){ callback(mutations); }) // have the observer observe foo for changes in children obs.observe( obj, { childList:true, subtree:true }); } else if( window.addEventListener ){ obj.addEventListener('DOMNodeInserted', callback, false); obj.addEventListener('DOMNodeRemoved', callback, false); } } })(); //------------< DEMO BELOW >---------------- // add item var itemHTML = "<li><button>list item (click to delete)</button></li>", listElm = document.querySelector('ol'); document.querySelector('body > button').onclick = function(e){ listElm.insertAdjacentHTML("beforeend", itemHTML); } // delete item listElm.onclick = function(e){ if( e.target.nodeName == "BUTTON" ) e.target.parentNode.parentNode.removeChild(e.target.parentNode); } // Observe a specific DOM element: observeDOM( listElm, function(m){ var addedNodes = [], removedNodes = []; m.forEach(record => record.addedNodes.length & addedNodes.push(...record.addedNodes)) m.forEach(record => record.removedNodes.length & removedNodes.push(...record.removedNodes)) console.clear(); console.log('Added:', addedNodes, 'Removed:', removedNodes); }); // Insert 3 DOM nodes at once after 3 seconds setTimeout(function(){ listElm.removeChild(listElm.lastElementChild); listElm.insertAdjacentHTML("beforeend", Array(4).join(itemHTML)); }, 3000);<button>Add Item</button> <ol> <li><button>list item (click to delete)</button></li> <li><button>list item (click to delete)</button></li> <li><button>list item (click to delete)</button></li> <li><button>list item (click to delete)</button></li> <li><em>…More will be added after 3 seconds…</em></li> </ol>
Я недавно написал плагин, который делает именно это - jquery.инициализировать
вы используете его так же, как
.eachфункции$(".some-element").initialize( function(){ $(this).css("color", "blue"); });в отличие от
.eachэто-он принимает ваш селектор, в этом случае.some-elementи ждать новых элементов с этим селектором в будущем, если такой элемент будет добавлен, он будет слишком инициализирован.в нашем случае инициализировать функцию просто изменить цвет элемента на синий. Так что если мы добавим новые элемент (независимо от того, если с ajax или даже F12 инспектор или что-нибудь), как:
$("<div/>").addClass('some-element').appendTo("body"); //new element will have blue color!плагин будет инициализировать его мгновенно. Также плагин гарантирует, что один элемент инициализируется только один раз. Так что если вы добавляете элемент, то
.detach()это от тела, а затем добавить его снова, он не будет инициализирован.$("<div/>").addClass('some-element').appendTo("body").detach() .appendTo(".some-container"); //initialized only onceплагин основан на
MutationObserver- он будет работать на IE9 и 10 с зависимостями, как описано на readme page.
или вы можете просто Создайте свое собственное событие, которые работают везде
$("body").on("domChanged", function () { //dom is changed }); $(".button").click(function () { //do some change $("button").append("<span>i am the new change</span>"); //fire event $("body").trigger("domChanged"); });полный пример http://jsfiddle.net/hbmaam/Mq7NX/
Это пример использования MutationObserver С Mozilla адаптировано из этой блоге
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();
Как насчет расширения jQuery для этого?
(function () { var ev = new $.Event('remove'), orig = $.fn.remove; var evap = new $.Event('append'), origap = $.fn.append; $.fn.remove = function () { $(this).trigger(ev); return orig.apply(this, arguments); } $.fn.append = function () { $(this).trigger(evap); return origap.apply(this, arguments); } })(); $(document).on('append', function (e) { /*write your logic here*/ }); $(document).on('remove', function (e) { /*write your logic here*/ ) });в jQuery 1.9+ имеет встроенную поддержку для этого(я слышал, не проверял).
Использовать JQuery MutationObserver как показано в блог
Chrome 18+, Firefox 14+, IE 11+, Safari 6+
// The node to be monitored var target = $( "#content" )[0]; // Create an observer instance var observer = new MutationObserver(function( mutations ) { mutations.forEach(function( mutation ) { var newNodes = mutation.addedNodes; // DOM NodeList if( newNodes !== null ) { // If there are new nodes added var $nodes = $( newNodes ); // jQuery set $nodes.each(function() { var $node = $( this ); if( $node.hasClass( "message" ) ) { // do something } }); } }); }); // 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();