Voglio eseguire una funzione quando alcuni div o input vengono aggiunti all'html. È possibile?
Ad esempio, viene inserito un input di testo, quindi è necessario chiamare la funzione.
Voglio eseguire una funzione quando alcuni div o input vengono aggiunti all'html. È possibile?
Ad esempio, viene inserito un input di testo, quindi è necessario chiamare la funzione.
Risposte:
MutationObserver
è supportato dai browser moderni:Chrome 18+, Firefox 14+, IE 11+, Safari 6+
Se hai bisogno di supportare quelli più anziani, potresti provare a ricorrere ad altri approcci come quelli menzionati in questa risposta di 5 (!) Anni di seguito. Ci sono draghi. Godere :)
Qualcun altro sta cambiando il documento? Perché se hai il pieno controllo delle modifiche devi solo creare la tua domChanged
API - con una funzione o un evento personalizzato - e attivarla / chiamarla ovunque modifichi le cose.
Il DOM Level-2 ha tipi di eventi di mutazione , ma vecchia versione di IE non lo supportano. Si noti che gli eventi di mutazione sono deprecati nelle specifiche Eventi DOM3 e hanno una penalità di prestazione .
Puoi provare a emulare l'evento di mutazione con onpropertychange
in IE (e ricorrere all'approccio a forza bruta se non è disponibile uno di essi).
Per un domChange completo un intervallo potrebbe essere un over-kill. Immagina di dover archiviare lo stato corrente dell'intero documento ed esaminare ogni proprietà di ogni elemento per essere la stessa.
Forse se sei interessato solo agli elementi e al loro ordine (come hai menzionato nella tua domanda), a getElementsByTagName("*")
può funzionare. Questo si attiverà automaticamente se aggiungi un elemento, rimuovi un elemento, sostituisci elementi o modifichi la struttura del documento.
Ho scritto una prova di concetto:
(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);
Uso:
onDomChange(function(){
alert("The Times They Are a-Changin'");
});
Funziona su IE 5.5+, FF 2+, Chrome, Safari 3+ e Opera 9.6+
IE9 +, FF, Webkit:
Uso di MutationObserver e , se necessario, ritorno agli eventi di Mutazione obsoleti :
(Esempio di seguito se solo per modifiche DOM relative ai nodi aggiunti o rimossi)
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>
mutations, observer
parametri alla funzione di richiamata per un maggiore controllo.
Di recente ho scritto un plugin che fa esattamente questo: jquery.initialize
Lo usi allo stesso modo della .each
funzione
$(".some-element").initialize( function(){
$(this).css("color", "blue");
});
La differenza .each
è: prende il tuo selettore, in questo caso .some-element
e aspetta nuovi elementi con questo selettore in futuro, se tale elemento verrà aggiunto, verrà anche inizializzato.
Nel nostro caso la funzione di inizializzazione cambia semplicemente il colore dell'elemento in blu. Quindi, se aggiungeremo un nuovo elemento (non importa se con ajax o anche con l'ispettore F12 o altro) come:
$("<div/>").addClass('some-element').appendTo("body"); //new element will have blue color!
Il plug-in lo avvia immediatamente. Inoltre il plugin si assicura che un elemento sia inizializzato solo una volta. Quindi, se aggiungi un elemento, quindi .detach()
dal corpo e poi lo aggiungi di nuovo, non verrà inizializzato di nuovo.
$("<div/>").addClass('some-element').appendTo("body").detach()
.appendTo(".some-container");
//initialized only once
Il plugin si basa su MutationObserver
- funzionerà su IE9 e 10 con dipendenze come dettagliato nella pagina del readme .
oppure puoi semplicemente creare il tuo evento , che si svolge ovunque
$("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");
});
Esempio completo http://jsfiddle.net/hbmaam/Mq7NX/
Questo è un esempio usando MutationObserver di Mozilla adattato da questo post del blog
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();
Utilizzare l' interfaccia MutationObserver come mostrato nel blog di Gabriele Romanato
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();
Che ne dici di estendere un jquery per questo?
(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+ ha creato supporto per questo (ho sentito non testato).