[英]How to check if an element has been loaded on a page before running a script?
所以我在我的公司模板中创建我的页面,它只允许我们访问页面的正文。 我们无权访问 head 标记,并且在页面的下部加载了我们无权访问的脚本。 其中一个脚本将元素动态加载到页面上。 我需要在该元素上运行另一个脚本,但是因为该元素直到我的脚本已经运行后才会加载到页面上,所以我无法访问该元素。 有没有办法在运行我的脚本之前检查该元素是否已加载到页面上?
如果我需要更好地解释,请告诉我。
<head>Don't have access</head>
<body>
<!--Needs to manipulate the element created by the companyScript.js-->
<script src="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>
听起来像是MutationObserver
的工作!
MutationObserver
就像一个事件侦听器:您可以将其附加到任何 DOM 元素以侦听更改:
var observer = new MutationObserver(function (mutationRecords) {
console.log("change detected");
});
回调传递了一个MutationRecord
数组,其中包含添加/删除/修改节点的不同列表。
然后,我们将观察者附加到任何节点:
observer.observe(document.body, {childList: true});
// second argument is a config: in this case, only fire the callback when nodes are added or removed
注意: IE 支持并不惊人。 (惊喜,惊喜)仅适用于 IE 11。(不过 Edge 支持它。)
这是关于检测 DOM 更改的另一个 SO 问题: 检测 DOM 中的更改
片段:
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>
重写 2020 代码以使用MutationObserver
而不是轮询。
/**
* 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");
});
此代码将以 10 倍/秒的速度轮询 DOM 并在可选超时之前解决承诺。
/**
* 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");
});
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");
});
我建议不要使用 MutationObserver 。 这种方法有很多缺点:它会减慢页面加载速度,浏览器支持不佳。 在某些情况下,这是解决手头问题的唯一方法。
更好的方法是使用onload DOM 事件。 此事件适用于 iframe 和 img 等标签。 对于其他标签,这是您可以从中获得灵感的伪代码:
<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>
注意:诀窍是在您要查看加载的每个元素之后附加 iframe 或 img 元素。
这是一个用 ClojureScript 编写的函数,它在满足条件时调用某个函数。
(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)))))))
然后你可以做类似的事情
wait_for_condition((id) => document.getElementById(id), () => alert("it happened"))
或者
(wait-for-condition #(js/document.getElementById id)
#(js/alert "it happened")
:frequency 250
:timeout 10)
扩展@i-wrestled-a-bear-once 的解决方案。 使用此 js 代码,当目标组件不再存在于 DOM 中时,我们也可以禁用弹出窗口。
它可用于请求从请求输入文件,并且在请求完成后不想显示文件的情况。
// 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");
很想就我如何使这件事变得更好提出建议。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.