简体   繁体   English

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

[英]How to check if an element has been loaded on a page before running a script?

So I am creating my page in my companies template, which only allow us access to the body of the page.所以我在我的公司模板中创建我的页面,它只允许我们访问页面的正文。 We don't have access to the head tag and there are scripts loaded on a lower part of the page that we don't have access to.我们无权访问 head 标记,并且在页面的下部加载了我们无权访问的脚本。 One of these scripts dynamically loads an element onto the page.其中一个脚本将元素动态加载到页面上。 I need to run another script on that element, but because the element isn't loaded on the page until after my script has already ran I can't access that element.我需要在该元素上运行另一个脚本,但是因为该元素直到我的脚本已经运行后才会加载到页面上,所以我无法访问该元素。 Is there a way I can check to see if that element has already been loaded on the page before running my script?有没有办法在运行我的脚本之前检查该元素是否已加载到页面上?

Let me know if I need to explain better.如果我需要更好地解释,请告诉我。

<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>

Sounds like a job for MutationObserver !听起来像是MutationObserver的工作!

A MutationObserver is like an event listener: you can attach it to any DOM element to listen for changes: MutationObserver就像一个事件侦听器:您可以将其附加到任何 DOM 元素以侦听更改:

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

The callback is passed an array of MutationRecord s, which hold different lists of added/deleted/modified nodes.回调传递了一个MutationRecord数组,其中包含添加/删除/修改节点的不同列表。

Then, we attach the observer to any node:然后,我们将观察者附加到任何节点:

observer.observe(document.body, {childList: true});
// second argument is a config: in this case, only fire the callback when nodes are added or removed

Note: IE support is not amazing.注意: IE 支持并不惊人。 (surprise, surprise) Only works on IE 11. (Edge supports it, though.) (惊喜,惊喜)仅适用于 IE 11。(不过 Edge 支持它。)

Here's another SO question about detecting changes to the DOM: Detect changes in the DOM这是关于检测 DOM 更改的另一个 SO 问题: 检测 DOM 中的更改

Snippet:片段:

 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 Version, with MutationObserver 2022 版本,带有 MutationObserver

Rewrote the 2020 code to use a MutationObserver instead of polling.重写 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 version, with promises 2020 版本,有承诺

This code will poll the DOM 10x/second and resolve a promise if it's found before the optional timeout.此代码将以 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");
});

Original answer原答案

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

I would recommend against using MutationObserver .我建议不要使用 MutationObserver This approach has many drawbacks: it slows page loading, has poor browser support.这种方法有很多缺点:它会减慢页面加载速度,浏览器支持不佳。 There are situations when this is the only approach to solve the problem at hand.在某些情况下,这是解决手头问题的唯一方法。

A better approach is to use the onload DOM event .更好的方法是使用onload DOM 事件 This event works on tags such as iframe and img.此事件适用于 iframe 和 img 等标签。 For other tags, this is the pseudo code you can take inspiration from:对于其他标签,这是您可以从中获得灵感的伪代码:

 <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>

NOTE: The trick is to attach an iframe or img element after every element you want to watch for their loading.注意:诀窍是在您要查看加载的每个元素之后附加 iframe 或 img 元素。

Here's a function written in ClojureScript which invokes some function whenever a condition is met.这是一个用 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)))))))

Then you could do something like然后你可以做类似的事情

wait_for_condition((id) => document.getElementById(id), () => alert("it happened"))

or或者

(wait-for-condition #(js/document.getElementById id)
                    #(js/alert "it happened")
                    :frequency 250
                    :timeout 10)

Expanding on @i-wrestled-a-bear-once's solution.扩展@i-wrestled-a-bear-once 的解决方案。 With this js code, we can disable the popup as well when the target component is not present in the DOM anymore.使用此 js 代码,当目标组件不再存在于 DOM 中时,我们也可以禁用弹出窗口。

It can be used for the cases when you're asking for a file input from for a request, and don't want to display the file after he's done with the request.它可用于请求从请求输入文件,并且在请求完成后不想显示文件的情况。

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

Would love suggestions on how I can make this better.很想就我如何使这件事变得更好提出建议。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

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