【问题标题】:How to check if an element has been loaded on a page before running a script?如何在运行脚本之前检查页面上是否已加载元素?
【发布时间】:2016-01-18 20:47:18
【问题描述】:

所以我在我的公司模板中创建我的页面,它只允许我们访问页面的正文。我们无权访问 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>

【问题讨论】:

  • 编写一个脚本以将您的脚本附加到页面底部,以便在之后加载
  • If ( $('.element').length )
  • 使用添加元素的代码来运行会影响该元素的代码。需要更好地理解问题以获得更高质量的答案和更少的猜测
  • @johnny5 不处理是否存在动态添加的元素

标签: javascript jquery html


【解决方案1】:

听起来像是MutationObserver 的工作!

MutationObserver 就像一个事件监听器:您可以将它附加到任何 DOM 元素以监听变化:

var observer = new MutationObserver(function (mutationRecords) {
    console.log("change detected");
});

回调传递一个MutationRecords 数组,其中包含不同的添加/删除/修改节点列表。

然后,我们将观察者附加到任何节点:

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 support is not amazing.(惊喜,惊喜)仅适用于 IE 11。(不过,Edge 支持它。)

这是另一个关于检测 DOM 更改的 SO 问题:Detect changes in the 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>

【讨论】:

    【解决方案2】:

    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,如果在可选超时之前找到它,则解决一个 Promise。

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

    【讨论】:

    • 是的,我看到我删除了它
    • 天才! ;-) 如此简单......但有效!唯一有效地在 Angular 上运行的代码:在 ngAfterViewInit() 上调用函数,就是这样!谢谢!
    • 我喜欢你的 2020 代码,你能解释一下 waitForElement 函数中的 if 语句吗?
    • 这是一个非常好的代码,它需要一些解释,但是这段代码所做的是检测元素是否被加载然后做一些事情,这段代码没有做的是做一些事情 WHILE the正在加载元素
    • @Mathew - 这就是承诺的作用,它们让你在等待其他事情时做一些事情。 if 语句只是检查元素是否存在。
    【解决方案3】:

    我建议不要使用 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 元素。

    【讨论】:

    • 如果您遵循这种方法,可以通过多种方式解决这个特定问题,例如您可以观察脚本标签的加载,然后使用 javascript 动态加载另一个脚本。
    【解决方案4】:

    这是一个用 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)
    

    【讨论】:

      【解决方案5】:

      扩展@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");
      

      希望能得到有关如何改进的建议。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2017-01-13
        • 2011-03-26
        • 1970-01-01
        • 2021-05-26
        • 2017-12-30
        • 2018-08-16
        相关资源
        最近更新 更多