Como esperar até que um elemento exista?

Estou trabalhando em uma extensão no Chrome e estou pensando: qual é a melhor maneira de descobrir quando um elemento é criado? Usando javascript simples, com um intervalo que verifica até que um elemento exista, ou o jQuery tem alguma maneira fácil de fazer isso?

DOMNodeInserted está sendo reprovado, juntamente com os outros events de mutação do DOM, devido a problemas de desempenho – a abordagem recomendada é usar um MutationObserver para observar o DOM. No entanto, ele só é suportado em navegadores mais recentes, portanto, você deve retornar ao DOMNodeInserted quando o MutationObserver não estiver disponível.

 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() 

Eu estava tendo o mesmo problema, então fui em frente e escrevi um plugin para isso.

$(selector).waitUntilExists(function);

Código:

 ;(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)); 

Aqui está uma function JavaScript principal para aguardar a exibição de um elemento.

parameters:

  1. selector : esta function procura o elemento $ {selector}
  2. time : Esta function verifica se este elemento existe a cada $ {time} milissegundos.

     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); } } 

Por exemplo, o selector="#div1" configuração selector="#div1" e o time=5000 procurarão a tag HTML cujo id="div1" cada 5000 milissegundos.

Você pode ouvir events DOMSubtreeModified ou DOMSubtreeModified que são DOMSubtreeModified sempre que um novo elemento é adicionado ao DOM.

Há também o plugin jQuery do LiveQuery que detecta quando um novo elemento é criado:

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

Você pode fazer

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

Observe que isso só funcionará se o elemento estiver presente no DOM quando solicitado pelo servidor. Se o elemento estiver sendo adicionado dinamicamente via JavaScript, ele não funcionará e você poderá precisar examinar as outras respostas.

Eu usei essa abordagem para esperar que um elemento aparecesse para que eu pudesse executar as outras funções depois disso.

Vamos dizer que a function doTheRestOfTheStuff(parameters) só deve ser chamada depois que o elemento com ID the_Element_ID aparecer ou terminar de carregar, nós podemos usar,

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

Como sobre a biblioteca de inserção de inserção ?

insertionQuery usa retornos de chamada CSS Animation anexados ao (s) seletor (es) especificado (s) para executar um retorno de chamada quando um elemento é criado. Esse método permite que retornos de chamada sejam executados sempre que um elemento é criado, não apenas na primeira vez.

Do github:

Modo não-dom-evento para capturar nós aparecendo. E usa seletores.

Não é apenas para suporte de navegador mais amplo, pode ser melhor do que DOMMutationObserver para certas coisas.

Por quê?

  • Porque os events do DOM tornam o navegador mais lento e o Query de inserção não
  • Porque o DOM Mutation Observer tem menos suporte ao navegador que o insertionQuery
  • Porque com inserçãoQuery você pode filtrar as alterações do DOM usando seletores sem sobrecarga de desempenho!

Suporte generalizado!

IE10 + e principalmente qualquer outra coisa (incluindo celular)

Para uma abordagem simples usando o jQuery, descobri que isso funciona bem:

  // 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"); }); }); 

Aqui nós simplesmente verificamos cada 500ms para ver se o elemento está carregado, quando é, podemos usá-lo.

Isso é especialmente útil para adicionar manipuladores de clique a elementos que foram adicionados dinamicamente ao documento.

Aqui está uma function que age como um wrapper fino em torno do MutationObserver. O único requisito é que o navegador suporte MutationObserver; não há dependência no JQuery. Execute o trecho abaixo para ver um exemplo funcional.

 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

Aqui está uma function JavaScript pura que permite esperar por qualquer coisa. Defina o intervalo por mais tempo para ter menos resources da 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); } 

Para chamar isso, por exemplo, em jQuery, use algo como:

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

Um exemplo mais limpo usando o MutationObserver:

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

Aqui está uma solução de retorno de promise em Javascript de baunilha (sem retornos de chamada confuso). Por padrão, ele verifica a cada 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); } } }); }