Come aspettare che esista un elemento?

Sto lavorando su un’estensione in Chrome e mi chiedo: qual è il modo migliore per scoprire quando un elemento viene creato? Usando plain javascript, con un intervallo che controlla fino a quando un elemento esiste, o jQuery ha un modo semplice per farlo?

DOMNodeInserted viene deprecato, insieme agli altri eventi di mutazione DOM, a causa di problemi di prestazioni – l’approccio consigliato è quello di utilizzare un MutationObserver per guardare il DOM. Tuttavia, è supportato solo nei nuovi browser, quindi dovresti tornare a DOMNodeInserted quando MutationObserver non è disponibile.

 var observer = new MutationObserver(function(mutations) { mutations.forEach(function(mutation) { if (!mutation.addedNodes) return for (var i = 0; i < mutation.addedNodes.length; i++) { // do things to your newly added nodes here var node = mutation.addedNodes[i] } }) }) observer.observe(document.body, { childList: true , subtree: true , attributes: false , characterData: false }) // stop watching using: observer.disconnect() 

Stavo avendo lo stesso problema, quindi sono andato avanti e ho scritto un plugin per questo.

$(selector).waitUntilExists(function);

Codice:

 ;(function ($, window) { var intervals = {}; var removeListener = function(selector) { if (intervals[selector]) { window.clearInterval(intervals[selector]); intervals[selector] = null; } }; var found = 'waitUntilExists.found'; /** * @function * @property {object} jQuery plugin which runs handler function once specified * element is inserted into the DOM * @param {function|string} handler * A function to execute at the time when the element is inserted or * string "remove" to remove the listener from the given selector * @param {bool} shouldRunHandlerOnce * Optional: if true, handler is unbound after its first invocation * @example jQuery(selector).waitUntilExists(function); */ $.fn.waitUntilExists = function(handler, shouldRunHandlerOnce, isChild) { var selector = this.selector; var $this = $(selector); var $elements = $this.not(function() { return $(this).data(found); }); if (handler === 'remove') { // Hijack and remove interval immediately if the code requests removeListener(selector); } else { // Run the handler on all found elements and mark as found $elements.each(handler).data(found, true); if (shouldRunHandlerOnce && $this.length) { // Element was found, implying the handler already ran for all // matched elements removeListener(selector); } else if (!isChild) { // If this is a recurring search or if the target has not yet been // found, create an interval to continue searching for the target intervals[selector] = window.setInterval(function () { $this.waitUntilExists(handler, shouldRunHandlerOnce, true); }, 500); } } return $this; }; }(jQuery, window)); 

Ecco una funzione JavaScript di base per attendere la visualizzazione di un elemento.

parametri:

  1. selector : questa funzione cerca l’elemento $ {selector}
  2. time : questa funzione controlla se questo elemento esiste ogni $ {tempo} millisecondi.

     function waitForElementToDisplay(selector, time) { if(document.querySelector(selector)!=null) { alert("The element is displayed, you can put your code instead of this alert.") return; } else { setTimeout(function() { waitForElementToDisplay(selector, time); }, time); } } 

Ad esempio, selector="#div1" e time=5000 si cercherà il tag HTML il cui id="div1" ogni 5000 millisecondi.

Puoi ascoltare DOMSubtreeModified eventi DOMNodeInserted o DOMSubtreeModified che si DOMSubtreeModified ogni volta che un nuovo elemento viene aggiunto al DOM.

Esiste anche il plugin jQuery di LiveQuery che rileva quando viene creato un nuovo elemento:

 $("#future_element").livequery(function(){ //element created }); 

Tu puoi fare

 $('#yourelement').ready(function() { }); 

Si noti che questo funzionerà solo se l’elemento è presente nel DOM quando viene richiesto dal server. Se l’elemento viene aggiunto dynamicmente tramite JavaScript, non funzionerà e potrebbe essere necessario esaminare le altre risposte.

Ho usato questo approccio per attendere la comparsa di un elemento in modo da poter eseguire successivamente le altre funzioni.

Diciamo che la funzione doTheRestOfTheStuff(parameters) dovrebbe essere chiamata solo dopo che l’elemento con ID the_Element_ID appare o ha terminato il caricamento, possiamo usare,

 var existCondition = setInterval(function() { if ($('#the_Element_ID').length) { console.log("Exists!"); clearInterval(existCondition); doTheRestOfTheStuff(parameters); } }, 100); // check every 100ms 

Che ne dici della libreria insertionQuery ?

insertionQuery utilizza i callback di animazione CSS collegati al selettore o ai selettori specificati per eseguire una richiamata quando viene creato un elemento. Questo metodo consente di eseguire callback ogni volta che viene creato un elemento, non solo la prima volta.

Da github:

Modo non-dom-event per catturare i nodes che appaiono. E usa i selettori.

Non è solo per un supporto browser più ampio, può essere migliore di DOMMutationObserver per certe cose.

Perché?

  • Poiché gli eventi DOM rallentano il browser e insertionQuery no
  • Perché DOM Mutation Observer ha meno supporto del browser di insertionQuery
  • Perché con inserttionQuery puoi filtrare le modifiche DOM usando selettori senza sovraccarico di prestazioni!

Supporto diffuso!

IE10 + e principalmente qualsiasi altra cosa (incluso il cellulare)

Per un approccio semplice con jQuery ho trovato che questo funziona bene:

  // Wait for element to exist. function elementLoaded(el, cb) { if ($(el).length) { // Element is now loaded. cb($(el)); } else { // Repeat every 500ms. setTimeout(function() { elementLoaded(el, cb) }, 500); } }; elementLoaded('.element-selector', function(el) { // Element is ready to use. el.click(function() { alert("You just clicked a dynamically inserted element"); }); }); 

Qui controlliamo semplicemente ogni 500ms per vedere se l’elemento è caricato, quando lo è, possiamo usarlo.

Ciò è particolarmente utile per aggiungere gestori di clic a elementi che sono stati aggiunti dynamicmente al documento.

Ecco una funzione che funge da involucro sottile attorno a MutationObserver. L’unico requisito è che il browser supporti MutationObserver; non c’è dipendenza da JQuery. Esegui lo snippet qui sotto per vedere un esempio funzionante.

 function waitForMutation(parentNode, isMatchFunc, handlerFunc, observeSubtree, disconnectAfterMatch) { var defaultIfUndefined = function(val, defaultVal) { return (typeof val === "undefined") ? defaultVal : val; }; observeSubtree = defaultIfUndefined(observeSubtree, false); disconnectAfterMatch = defaultIfUndefined(disconnectAfterMatch, false); var observer = new MutationObserver(function(mutations) { mutations.forEach(function(mutation) { if (mutation.addedNodes) { for (var i = 0; i < mutation.addedNodes.length; i++) { var node = mutation.addedNodes[i]; if (isMatchFunc(node)) { handlerFunc(node); if (disconnectAfterMatch) observer.disconnect(); }; } } }); }); observer.observe(parentNode, { childList: true, attributes: false, characterData: false, subtree: observeSubtree }); } // Example waitForMutation( // parentNode: Root node to observe. If the mutation you're looking for // might not occur directly below parentNode, pass 'true' to the // observeSubtree parameter. document.getElementById("outerContent"), // isMatchFunc: Function to identify a match. If it returns true, // handlerFunc will run. // MutationObserver only fires once per mutation, not once for every node // inside the mutation. If the element we're looking for is a child of // the newly-added element, we need to use something like // node.querySelector() to find it. function(node) { return node.querySelector(".foo") !== null; }, // handlerFunc: Handler. function(node) { var elem = document.createElement("div"); elem.appendChild(document.createTextNode("Added node (" + node.innerText + ")")); document.getElementById("log").appendChild(elem); }, // observeSubtree true, // disconnectAfterMatch: If this is true the hanlerFunc will only run on // the first time that isMatchFunc returns true. If it's false, the handler // will continue to fire on matches. false); // Set up UI. Using JQuery here for convenience. $outerContent = $("#outerContent"); $innerContent = $("#innerContent"); $("#addOuter").on("click", function() { var newNode = $("
Outer

"); $outerContent.append(newNode); }); $("#addInner").on("click", function() { var newNode = $("

Inner

"); $innerContent.append(newNode); });

 .content { padding: 1em; border: solid 1px black; overflow-y: auto; } #innerContent { height: 100px; } #outerContent { height: 200px; } #log { font-family: Courier; font-size: 10pt; } 
  

Create some mutations

Log

Ecco una funzione Javascript pura che ti consente di aspettare qualsiasi cosa. Impostare l’intervallo più lungo per assorbire meno risorse della CPU.

 /** * @brief Wait for something to be ready before triggering a timeout * @param {callback} isready Function which returns true when the thing we're waiting for has happened * @param {callback} success Function to call when the thing is ready * @param {callback} error Function to call if we time out before the event becomes ready * @param {int} count Number of times to retry the timeout (default 300 or 6s) * @param {int} interval Number of milliseconds to wait between attempts (default 20ms) */ function waitUntil(isready, success, error, count, interval){ if (count === undefined) { count = 300; } if (interval === undefined) { interval = 20; } if (isready()) { success(); return; } // The call back isn't ready. We need to wait for it setTimeout(function(){ if (!count) { // We have run out of retries if (error !== undefined) { error(); } } else { // Try again waitUntil(isready, success, error, count -1, interval); } }, interval); } 

Per chiamare questo, per esempio in jQuery, usa qualcosa come:

 waitUntil(function(){ return $('#myelement').length > 0; }, function(){ alert("myelement now exists"); }, function(){ alert("I'm bored. I give up."); }); 

Un esempio più pulito usando MutationObserver:

 new MutationObserver( mutation => { if (!mutation.addedNodes) return mutation.addedNodes.forEach( node => { // do stuff with node }) }) 

Ecco una soluzione di ritorno promise in vaniglia Javascript (senza callback disordinati). Di default controlla ogni 200ms.

 function waitFor(selector) { return new Promise(function (res, rej) { waitForElementToDisplay(selector, 200); function waitForElementToDisplay(selector, time) { if (document.querySelector(selector) != null) { res(document.querySelector(selector)); } else { setTimeout(function () { waitForElementToDisplay(selector, time); }, time); } } }); }