Come posso determinare se un elemento DOM creato dynamicmente è stato aggiunto al DOM?

Secondo le specifiche , solo gli elementi BODY e FRAMESET forniscono un evento “onload” a cui collegarsi, ma vorrei sapere quando un elemento DOM creato dynamicmente è stato aggiunto al DOM in JavaScript.

Le euristiche super-naive che sto attualmente utilizzando, che funzionano, sono le seguenti:

  • Attraversare la proprietà parentNode dell’elemento fino a trovare l’antenato finale (ad esempio parentNode.parentNode.parentNode.etc fino a parentNode è null)
  • Se l’antenato finale ha una proprietà del corpo definita, non nulla
    • supponiamo che l’elemento in questione sia parte della dom
  • altro
    • ripetere di nuovo questi passaggi in 100 millisecondi

Quello che sto cercando è la conferma che quello che sto facendo è sufficiente (di nuovo, funziona sia in IE7 che in FF3) o una soluzione migliore che, per qualche ragione, mi è stato completamente ignaro; forse altre proprietà dovrei controllare, ecc.


EDIT: voglio un modo agnostico del browser per fare questo, non vivo in un mondo one-browser, purtroppo; Detto questo, le informazioni specifiche del browser sono apprezzate, ma ti preghiamo di notare quale browser sai che funziona. Grazie!

AGGIORNAMENTO: per chiunque sia interessato a questo, ecco l’implementazione che ho finalmente utilizzato:

 function isInDOMTree(node) { // If the farthest-back ancestor of our node has a "body" // property (that node would be the document itself), // we assume it is in the page's DOM tree. return !!(findUltimateAncestor(node).body); } function findUltimateAncestor(node) { // Walk up the DOM tree until we are at the top (parentNode // will return null at that point). // NOTE: this will return the same node that was passed in // if it has no ancestors. var ancestor = node; while(ancestor.parentNode) { ancestor = ancestor.parentNode; } return ancestor; } 

Il motivo per cui volevo questo è fornire un modo per sintetizzare l’evento onload per gli elementi DOM. Ecco quella funzione (anche se sto usando qualcosa di leggermente diverso perché lo sto usando in combinazione con MochiKit ):

 function executeOnLoad(node, func) { // This function will check, every tenth of a second, to see if // our element is a part of the DOM tree - as soon as we know // that it is, we execute the provided function. if(isInDOMTree(node)) { func(); } else { setTimeout(function() { executeOnLoad(node, func); }, 100); } } 

Per un esempio, questa configurazione potrebbe essere utilizzata come segue:

 var mySpan = document.createElement("span"); mySpan.innerHTML = "Hello world!"; executeOnLoad(mySpan, function(node) { alert('Added to DOM tree. ' + node.innerHTML); }); // now, at some point later in code, this // node would be appended to the document document.body.appendChild(mySpan); // sometime after this is executed, but no more than 100 ms after, // the anonymous function I passed to executeOnLoad() would execute 

Spero che sia utile a qualcuno.

NOTA: il motivo per cui ho trovato questa soluzione piuttosto che la risposta di Darryl era perché la tecnica getElementById funziona solo se si è all’interno dello stesso documento; Ho alcuni iframe su una pagina e le pagine comunicano tra loro in modi complessi – quando ho provato questo, il problema era che non riusciva a trovare l’elemento perché faceva parte di un documento diverso dal codice che stava eseguendo in .

La risposta più semplice è utilizzare il metodo Node.contains , supportato da Chrome, Firefox (Gecko), Internet Explorer, Opera e Safari. Ecco un esempio:

 var el = document.createElement("div"); console.log(document.body.contains(el)); // false document.body.appendChild(el); console.log(document.body.contains(el)); // true document.body.removeChild(el); console.log(document.body.contains(el)); // false 

Idealmente, dovremmo usare document.contains(el) , ma questo non funziona in IE, quindi usiamo document.body.contains(el) .

Sfortunatamente, è ancora necessario eseguire il polling, ma controllare se un elemento è presente nel documento è molto semplice:

 setTimeout(function test() { if (document.body.contains(node)) { func(); } else { setTimeout(test, 50); } }, 50); 

Se stai bene con l’aggiunta di alcuni CSS alla tua pagina, ecco un’altra tecnica intelligente che utilizza le animazioni per rilevare gli inserimenti dei nodes: http://www.backalleycoder.com/2012/04/25/i-want-a-damnodeinserted/

Non puoi fare un document.getElementById('newElementId'); e vedere se ciò restituisce vero. Se no, come dici tu, aspetta 100ms e riprova?

Invece di camminare sull’albero DOM fino all’elemento del documento, usa element.ownerDocument . vedere qui: https://developer.mozilla.org/en-US/docs/DOM/Node.ownerDocument e fare questo:

 element.ownerDocument.body.contains(element) 

e tu sei bravo.

È ansible eseguire una query su document.getElementsByTagName (“*”). Length o creare una funzione appendChild personalizzata come folllowing:

 var append = function(parent, child, onAppend) { parent.appendChild(child); if (onAppend) onAppend(child); } //inserts a div into body and adds the class "created" upon insertion append(document.body, document.createElement("div"), function(el) { el.className = "created"; }); 

Aggiornare

A richiesta, aggiungendo le informazioni dai miei commenti al mio post

C’è stato un commento di John Resig sulla libreria Peppy su Ajaxian oggi che sembrava suggerire che la sua libreria Sizzle potesse essere in grado di gestire gli eventi di inserimento DOM. Sono curioso di vedere se ci sarà il codice per gestire anche IE

Seguendo l’idea del polling, ho letto che alcune proprietà dell’elemento non sono disponibili fino a quando l’elemento non è stato aggiunto al documento (ad esempio element.scrollTop), forse è ansible eseguire il polling invece di eseguire tutto il DOM traversal.

Un’ultima cosa: in IE, un approccio che potrebbe valere la pena esplorare è giocare con l’evento onpropertychange. Suppongo che l’aggiunta al documento sia destinata a innescare quell’evento per almeno una proprietà.

In un mondo perfetto potresti agganciare gli eventi di mutazione. Ho dei dubbi sul fatto che funzionano in modo affidabile anche sui browser standard. Sembra che tu abbia già implementato un evento di mutazione in modo da poterlo aggiungere per utilizzare un evento di mutazione nativo anziché il timeout di polling sui browser che li supportano.

Ho visto implementazioni di DOM-change che monitorano le modifiche confrontando document.body.innerHTML con l’ultimo salvataggio di .innerHTML , che non è esattamente elegante (ma funziona). Dal momento che in definitiva stai andando a controllare se un nodo specifico è stato ancora aggiunto, allora è meglio controllare solo che ogni interruzione.

I miglioramenti che riesco a pensare sono usando .offsetParent piuttosto che .parentNode in quanto probabilmente .parentNode alcuni genitori dal tuo ciclo (vedi commenti). O usando compareDocumentIndex() su tutti tranne IE e testando .sourceIndex propery per IE (dovrebbe essere -1 se il nodo non è nel DOM).

Ciò potrebbe anche aiutare: X browser compareDocumentIndex Implementaion di John Resig .

Si desidera l’evento DOMNodeInserted (o DOMNodeInsertedIntoDocument ).

Modifica: è del tutto ansible che questi eventi non siano supportati da IE, ma non posso testarlo al momento.

Ecco un’altra soluzione al problema estratto dal codice jQuery. La seguente funzione può essere utilizzata per verificare se un nodo è collegato al DOM o se è “disconnesso”.

 function isDisconnected (node) {
     ritorno! nodo ||  ! node.parentNode ||  node.parentNode.nodeType === 11;
 }

Un nodeType === 11 definisce un DOCUMENT_FRAGMENT_NODE, che è un nodo non connesso all’object del documento.

Per ulteriori informazioni consultare il seguente articolo di John Resig: http://ejohn.org/blog/dom-documentfragments/

 var finalElement=null; 

Sto costruendo oggetti dom in un ciclo, quando il ciclo si trova sull’ultimo ciclo e viene creato lastDomObject :

  finalElement=lastDomObject; 

lasciare il ciclo:

 while (!finalElement) { } //this is the delay... 

L’azione successiva può utilizzare uno qualsiasi degli oggetti dom appena creati. Ha funzionato al primo tentativo.

Un MutationObserver è ciò che dovresti usare per rilevare quando un elemento è stato aggiunto al DOM. MutationObservers ora è ampiamente supportato da tutti i browser moderni (Chrome 26+, Firefox 14+, IE11, Edge, Opera 15+, ecc.).

Ecco un semplice esempio di come puoi usare un MutationObserver per ascoltare quando un elemento viene aggiunto al DOM.

Per brevità, sto usando la syntax jQuery per build il nodo e inserirlo nel DOM.

 var myElement = $("
hello world
")[0]; var observer = new MutationObserver(function(mutations) { if (document.contains(myElement)) { console.log("It's in the DOM!"); observer.disconnect(); } }); observer.observe(document, {attributes: false, childList: true, characterData: false, subtree:true}); $("body").append(myElement); // console.log: It's in the DOM!

Il gestore di eventi observer si attiverà ogni volta che qualsiasi nodo viene aggiunto o rimosso dal document . All’interno del gestore, eseguiamo un controllo di contains per determinare se myElement è ora nel document .

Non è necessario iterare su ogni MutationRecord memorizzato in mutations perché è ansible eseguire il controllo document.contains direttamente su myElement .

Per migliorare le prestazioni, sostituire il document con l’elemento specifico che conterrà myElement nel DOM.

Nessuno degli eventi DOMNodeInserted o DOMNodeInsertedIntoDocument è supportato in IE. Un’altra caratteristica mancante di IE [solo] di DOM è la funzione compareDocumentPosition progettata per fare ciò che suggerisce il nome.

Per quanto riguarda la soluzione suggerita, penso che non ce ne sia una migliore (a meno che non si facciano alcuni trucchetti sporchi come la sovrascrittura delle implementazioni dei membri DOM nativi)

Inoltre, correggendo il titolo della domanda: l’elemento creato dynamicmente fa parte del DOM dal momento in cui viene creato, può essere aggiunto a un frammento di documento o a un altro elemento, ma potrebbe non essere aggiunto a un documento.

Penso anche che il problema che hai descritto non sia il problema che hai, questo sembra piuttosto una delle potenziali soluzioni al tuo problema. Puoi probabilmente spiegare in dettaglio il caso d’uso?