繁体   English   中英

如何在运行脚本之前检查元素是否已加载到页面上?

[英]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>

2022 版本,带有 MutationObserver

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

2020 版本,有承诺

此代码将以 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.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM