Estoy haciendo una biblioteca de juguetes en la que el usuario puede modificar el DOM con AJAX, básicamente alrededor de:
fetch("/newpage").then(r => r.text()).then(html => { var tmp = document.createElement('html'); tmp.innerHTML = html; document.querySelector("#container").outerHTML = tmp.querySelector("#container").outerHTML; // insert in the DOM El usuario de la biblioteca puede decidir tener bloques <script> en el HTML insertado <div> , así que después de la inserción hago lo siguiente:
for (const scr of document.querySelectorAll("#container script")) eval(scr.innerHTML);como se explica en Ejecutar <script> inyectado por innerHTML después de la llamada AJAX .
Esto funciona bien. Pero luego, si se realiza otra llamada AJAX y #container se reemplaza nuevamente por otra cosa, y se elimina el <script> , debemos poder detener todos los scripts en ejecución que se originaron desde el <script> dentro #container . ¿Como hacer esto?
Ejemplo:
después de la llamada AJAX #1, #container contiene un <script> con código que se ejecuta periódicamente ( setInterval , etc.). Ahora se está ejecutando gracias a eval (ver arriba)
después de la llamada AJAX #2, #container se reemplaza por otra cosa, y este <script> ya no está aquí . Pero la tarea setInterval todavía se está ejecutando... ¿Cómo eliminar esto automáticamente cuando <script> desaparece después de la llamada AJAX #2?
Ideas: ¿podríamos asociar el valor de retorno de eval(..) a una variable, para que podamos realizar un seguimiento de esto y eliminar su ejecución más tarde?
O al insertar <script> con element.outerHTML = ... , ¿deberíamos agregar un identificador (id o clase) al <script> , para que luego podamos tomar medidas sobre el código que lanzó este bloque de script?
Nota: tampoco ¿Es posible detener la ejecución de JavaScript? ni ¿Cómo terminar el script en JavaScript? ni ejecutar <script> inyectado por innerHTML después de la llamada AJAX para resolver esta pregunta.
Puede envolver el setTimeout antes de eval() el script. Cuando elimine el script, puede cancelar el registro de las tareas.
function mockInternal() { const setTimeout = window.setTimeout; const setInterval = window.setInterval; // window.setImmediate // window.addEventListener const setTimeoutHandlers = []; const setIntervalHandlers = []; window.setTimeout = function mock_setTimeout(callback, delay) { const h = setTimeout(callback, delay); setTimeoutHandlers.push(h); return h; } window.setInterval = function mock_setInterval(callback, delay) { const h = setInterval(callback, delay); setIntervalHandlers.push(h); return h; } return function clearMockInternal() { window.setTimeout = setTimeout; window.setInterval = setInterval; setTimeoutHandlers.forEach(h => clearTimeout(h)); setIntervalHandlers.forEach(h => clearInterval(h)); } } const clearMockInternal = mockInternal(); eval(`setInterval(() => console.log(new Date()), 1000);`); setTimeout(() => { clearMockInternal(); console.log("should not log Date from now"); }, 6000);Esta respuesta solo responde a su única pregunta (por intervalo), pero solo quiero compartirla. Puede devolver valores de su evaluación y usarlos para más adelante. Por ejemplo:
índice.html
<html> <head> </head> <body> <button type="button" onclick="run1()">Run first method</button> <button type="button" onclick="run2()">Run second method</button> <div id="container"> </div> <script> var pro1, pro2; function run1() { if (pro2 != undefined && pro2 != null) { clearInterval(pro2); } fetch("page1.html").then(r => r.text()).then(html => { var tmp = document.createElement('html'); tmp.innerHTML = html; document.querySelector("#container").outerHTML = tmp.querySelector("#container").outerHTML; for (const scr of document.querySelectorAll("#container script")) { pro1 = eval(scr.innerHTML); } }); } function run2() { if (pro1 != undefined && pro1 != null) { clearInterval(pro1); } fetch("page2.html").then(r => r.text()).then(html => { var tmp = document.createElement('html'); tmp.innerHTML = html; document.querySelector("#container").outerHTML = tmp.querySelector("#container").outerHTML; for (const scr of document.querySelectorAll("#container script")) { var data = eval(scr.innerHTML); pro2 = data; } }); } </script> </body> </html>pagina1.html
<html> <head> </head> <body> <div id="container"> <script> test(); function test() { var i = 0; const interval = setInterval(function () { console.log(i); i++; }, 1000); return interval; }; </script> </div> </body> </html>pagina2.html
<html> <head> </head> <body> <div id="container"> <script> test(); function test() { var i = 9999; const interval = setInterval(function () { console.log(i); i--; }, 1000); return interval; }; </script> </div> </body> </html>Cada vez que haga clic en un botón, se detendrá otro intervalo
Captura de pantalla para la muestra:
Espero que esto ayude.