Обнаружение изменений в DOM
Я хочу выполнить функцию, когда некоторые div или входные данные добавляются в html. Это возможно?
например, добавляется текстовый ввод, затем должна быть вызвана функция.
7 ответов:
обновление 2015, новый
MutationObserver
поддерживается современными браузерами:Chrome 18+, Firefox 14+, IE 11+, Safari 6+
Если вам нужно поддерживать старые, вы можете попытаться вернуться к другим подходам, как те, которые упомянуты в этом 5 (!) год назад ответ ниже. Там есть драконы. Наслаждайтесь :)
кто-то еще менять документ? Потому что если у вас есть полный контроль над изменениями вы просто нужно создать свой собственный
domChanged
API - с функцией или пользовательским событием-и запускать / вызывать его везде, где вы изменяете вещи.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();