usuario2052567
Así que estoy creando mi página en la plantilla de mi empresa, que solo nos permite acceder al cuerpo de la página. No tenemos acceso a la etiqueta de encabezado y hay scripts cargados en una parte inferior de la página a la que no tenemos acceso. Uno de estos scripts carga dinámicamente un elemento en la página. Necesito ejecutar otra secuencia de comandos en ese elemento, pero debido a que el elemento no se carga en la página hasta que mi secuencia de comandos ya se ha ejecutado, no puedo acceder a ese elemento. ¿Hay alguna manera de verificar si ese elemento ya se ha cargado en la página antes de ejecutar mi secuencia de comandos?
Avísame si necesito explicarme mejor.
<head>Don't have access</head>
<body>
<!--Needs to manipulate the element created by the companyScript.js-->
<script src="https://stackoverflow.com/questions/34863788/myScript.js"></script>
<!--Script that runs after mine, which I don't have access too-->
<script src="companyScript.js">
/*Dynamically adds <div class="element"></div> to page*/
</script>
</body>
Una vez luché con un oso.
Versión 2022, con MutationObserver
Reescribió el código 2020 para usar un MutationObserver
en lugar de votar.
/**
* Wait for an element before resolving a promise
* @param {String} querySelector - Selector of element to wait for
* @param {Integer} timeout - Milliseconds to wait before timing out, or 0 for no timeout
*/
function waitForElement(querySelector, timeout){
return new Promise((resolve, reject)=>{
var timer = false;
if(document.querySelectorAll(querySelector).length) return resolve();
const observer = new MutationObserver(()=>{
if(document.querySelectorAll(querySelector).length){
observer.disconnect();
if(timer !== false) clearTimeout(timer);
return resolve();
}
});
observer.observe(document.body, {
childList: true,
subtree: true
});
if(timeout) timer = setTimeout(()=>{
observer.disconnect();
reject();
}, timeout);
});
}
waitForElement("#idOfElementToWaitFor", 3000).then(function(){
alert("element is loaded.. do stuff");
}).catch(()=>{
alert("element did not load in 3 seconds");
});
Versión 2020, con promesas
Este código sondeará el DOM 10 veces por segundo y resolverá una promesa si se encuentra antes del tiempo de espera opcional.
/**
* Wait for an element before resolving a promise
* @param {String} querySelector - Selector of element to wait for
* @param {Integer} timeout - Milliseconds to wait before timing out, or 0 for no timeout
*/
function waitForElement(querySelector, timeout=0){
const startTime = new Date().getTime();
return new Promise((resolve, reject)=>{
const timer = setInterval(()=>{
const now = new Date().getTime();
if(document.querySelector(querySelector)){
clearInterval(timer);
resolve();
}else if(timeout && now - startTime >= timeout){
clearInterval(timer);
reject();
}
}, 100);
});
}
waitForElement("#idOfElementToWaitFor", 3000).then(function(){
alert("element is loaded.. do stuff");
}).catch(()=>{
alert("element did not load in 3 seconds");
});
respuesta original
function waitForElement(id, callback){
var poops = setInterval(function(){
if(document.getElementById(id)){
clearInterval(poops);
callback();
}
}, 100);
}
waitForElement("idOfElementToWaitFor", function(){
alert("element is loaded.. do stuff");
});
-
Genial! 😉 ¡TAN simple… pero efectivo! La única pieza de código funciona efectivamente en Angular: llamar a la función en
ngAfterViewInit()
¡y eso es! ¡Gracias!– Pedro Ferreira
29 oct 2019 a las 21:49
-
Me gustó su código 2020, ¿podría explicarme las declaraciones if dentro de la función waitForElement?
– Mateo
23 de agosto de 2020 a las 12:42
-
Ese es un código muy bueno, necesita alguna explicación, pero lo que hace este código es detectar si el elemento está cargado y luego hacer cosas, lo que este código no está haciendo es hacer cosas MIENTRAS se carga el elemento
– Mateo
23 de agosto de 2020 a las 12:44
-
@Mathew: eso es lo que hacen las promesas, te permiten hacer cosas mientras esperas otra cosa. los
if
declaración simplemente comprueba si el elemento existe todavía.– Una vez luché con un oso.
24 de agosto de 2020 a las 14:41
-
@Una vez luché con un oso. estoy tratando de desactivar
observer.disconnect()
en MutationObserver para mantenerlo siempre en funcionamiento, pero no estoy pensando en hacerlo. ¿Cualquier pista?– LuisC
28 sep a las 22:44
Hacha
Suena como un trabajo para MutationObserver
!
A MutationObserver
es como un detector de eventos: puede adjuntarlo a cualquier elemento DOM para escuchar los cambios:
var observer = new MutationObserver(function (mutationRecords) {
console.log("change detected");
});
La devolución de llamada se pasa una serie de MutationRecord
s, que contienen diferentes listas de nodos agregados/eliminados/modificados.
Luego, adjuntamos el observador a cualquier nodo:
observer.observe(document.body, {childList: true});
// second argument is a config: in this case, only fire the callback when nodes are added or removed
Nota: El soporte de IE no es sorprendente. (sorpresa, sorpresa) Solo funciona en IE 11. (Sin embargo, Edge lo admite).
Aquí hay otra pregunta SO sobre la detección de cambios en el DOM: Detectar cambios en el DOM
Retazo:
document.querySelector("button").addEventListener("click", function () {
document.body.appendChild(document.createElement("span"));
});
var observer = new MutationObserver(function (m) {
if (m[0].addedNodes[0].nodeName === "SPAN")
document.querySelector("div").innerHTML += "Change Detected<br>";
});
observer.observe(document.body, {childList: true});
<button>Click</button>
<div></div>
Recomendaría no usar MutationObserver. Este enfoque tiene muchos inconvenientes: ralentiza la carga de la página, tiene poca compatibilidad con el navegador. Hay situaciones en las que este es el único enfoque para resolver el problema en cuestión.
Un mejor enfoque es utilizar el cargar evento DOM. Este evento funciona en etiquetas como iframe e img. Para otras etiquetas, este es el pseudocódigo en el que puede inspirarse:
<body>
<script>
function divLoaded(event){
alert(event.target.previousSibling.previousSibling.id);
}
</script>
<div id="element_to_watch">This is an example div to watch it's loading</div>
<iframe style="display: none; width: 0; height: 0" onload="divLoaded(event)"/>
</body>
NOTA: El truco consiste en adjuntar un elemento iframe o img después de cada elemento que desee ver si se carga.
-
si sigue este enfoque, esta pregunta específica se puede resolver de muchas maneras, como puede observar la carga de la etiqueta del script y luego cargar dinámicamente otro script usando javascript.
– Shyam Swaroop
27 de febrero de 2020 a las 19:49
Aquí hay una función escrita en ClojureScript que invoca alguna función cada vez que se cumple una condición.
(require '[cljs.core.async :as a :refer [<!]]
'[cljs-await.core :refer [await]])
(require-macros '[cljs.core.async.macros :refer [go]])
(defn wait-for-condition
[pred-fn resolve-fn & {:keys [reject-fn timeout frequency]
:or {timeout 30 frequency 100}}]
(let [timeout-ms (atom (* timeout 1000))]
;; `def` instead of `let` so it can recurse
(def check-pred-satisfied
(fn []
(swap! timeout-ms #(- % frequency))
(if (pred-fn)
(resolve-fn)
(if (pos? @timeout-ms)
(js/setTimeout check-pred-satisfied frequency)
(when reject-fn
(reject-fn))))))
(go (<! (await (js/Promise. #(js/setTimeout check-pred-satisfied frequency)))))))
Entonces podrías hacer algo como
wait_for_condition((id) => document.getElementById(id), () => alert("it happened"))
o
(wait-for-condition #(js/document.getElementById id)
#(js/alert "it happened")
:frequency 250
:timeout 10)
Abhinandan Mittal
Ampliando la solución de @i-wrestled-a-bear-once. Con este código js, también podemos deshabilitar la ventana emergente cuando el componente de destino ya no está presente en el DOM.
Se puede usar para los casos en los que solicita la entrada de un archivo para una solicitud y no desea mostrar el archivo después de que haya terminado con la solicitud.
// Call this to run a method when a element removed from DOM
function waitForElementToUnLoad(querySelector) {
const startTime = new Date().getTime();
return new Promise((resolve, reject)=>{
const timer = setInterval(()=>{
const now = new Date().getTime();
if(!document.querySelector(querySelector)) {
clearInterval(timer);
resolve();
}
}, 1000);
});
}
// Call this to run a method when a element added to DOM, set timeout if required.
// Checks every second.
function waitForElementToLoad(querySelector, timeout=0) {
const startTime = new Date().getTime();
return new Promise((resolve, reject)=>{
const timer = setInterval(()=>{
const now = new Date().getTime();
if(document.querySelector(querySelector)){
clearInterval(timer);
resolve();
}else if(timeout && now - startTime >= timeout){
clearInterval(timer);
reject();
}
}, 1000);
});
}
// Removing onclose popup if no file is uploaded.
function executeUnloadPromise(id) {
waitForElementToUnLoad(id).then(function(){
window.onbeforeunload = null;
executeLoadPromise("#uploaded-file");
});
}
// Adding onclose popup if a file is uploaded.
function executeLoadPromise(id) {
waitForElementToLoad(id).then(function(){
window.onbeforeunload = function(event) {
return "Are you sure you want to leave?";
}
executeUnloadPromise("#uploaded-file");
});
}
executeLoadPromise("#uploaded-file");
Me encantaría sugerencias sobre cómo puedo hacer esto mejor.
escriba un script para agregar su script en la parte inferior de la página para que se cargue después
– Juan 5
18/01/2016 a las 20:49
If ( $('.element').length )
– adeneo
18/01/2016 a las 20:53
Use el código que agrega el elemento para ejecutar su código que afectaría a ese elemento. Necesita una mejor comprensión del problema para obtener respuestas de mayor calidad y menos adivinanzas
– charlietfl
18/01/2016 a las 20:56
@ johnny5 que no se ocupa de si existe o no un elemento agregado dinámicamente
– charlietfl
18/01/2016 a las 21:00