仅在元素完全加载时执行一次的函数

Function that executes once and only when an element is fully loaded

本文关键字:一次 函数 执行 元素 加载      更新时间:2023-09-26

我很难让JavaScript函数在DOM元素完全加载时执行一次。我尝试了无数次setInterval s、seTimeout s、FOR循环、IF语句、WHILE循环等的组合,但一无所获。

我确实设法让它工作了一次,但它是命中注定的,因为我只能通过将功能延迟 2 秒来让它工作(这不好,因为不知道加载需要多长时间(并快速重新触发相同的功能一遍又一遍,这也不好。

我只需要一些东西来不断扫描页面以判断元素是否存在并具有内容(innerHTML != undefined或其他内容(,在加载后立即执行代码块(并且仅一次(,然后停止扫描页面。

有没有人找到一种方法来做到这一点?另外,我需要JavaScript,而不是jQuery。

谢谢。
原始代码

function injectLink_Bridge(){
    setTimeout(function(){
        injectLink();
    }, 2000);
}
function injectLink(){
    var headerElement = document.getElementsByClassName("flex-module-header");
    if (headerElement.innerHTML == undefined) {
        console.log("Element doesn't exist. Restarting function");
        setTimeout(function(){
            injectLink_Bridge(); //I can't remember if the bridge is necessary or not
        }, 2000); 
    } else {
        console.log("Element exists. Injecting");
        setTimeout(function(){
            headerElement[1].innerHTML += "code" //Inject code into the second instance of the class-array
        }, 2000);
    }
}

完成的代码

function injectLink(){
    var headerElement = document.getElementsByClassName("flex-module-header")[1]; //Get the second instance
    if(headerElement && headerElement.innerHTML != ""){
        console.log("Element exists and has content. Injecting code...");
        headerElement.innerHTML += "code"; //Currently revising, due to T.J. Crowder's side-note
    } else {
        console.log("Element doesn't exist or has no content. Refiring function...");
        setTimeout(injectLink, 250);
    }
}

发布代码后更新了答案

getElementsByClassName返回一个NodeList,而不是一个元素; NodeList没有innerHTML属性。如果你想要第二个匹配元素,请使用[1]来获取它,然后测试你是否得到了一些东西(或者如果你愿意,你可以使用 .length > 1 先检查,但NodeList被记录为返回不存在的索引的返回null(。此外,在将现有函数传递给 setTimeout 时,您不需要围绕现有函数的函数包装器。所以:

function injectLink(){
    var headerElement = document.getElementsByClassName("flex-module-header")[1];
    if (!headerElement) {
        console.log("Element doesn't exist. Restarting function");
        setTimeout(injectLink, 2000); 
    } else {
        console.log("Element exists. Injecting");
        // Do you really want a further two second delay?
        setTimeout(function(){
            headerElement.innerHTML += "code"; //Inject code into the second instance of the class-array
        }, 2000);
    }
}

旁注:使用 .innerHTML += "markup"; 通常不是将内容附加到元素的好方法。以下是浏览器执行此操作时的操作:

  1. 旋转元素和它构建的 HTML 字符串以提供给 JavaScript 层的任何后代元素。
  2. 从元素中删除所有内容,该元素具有删除后代元素上的任何事件处理程序的副产品。
  3. 分析 HTML 字符串并为其生成新的节点和元素。

所以需要做一些工作,还有清除事件处理程序的可能性。如果要添加新的子元素,请查看使用 createElementappendChild 。如果要添加更多文本内容,请查看 createTextNodeappendChild


原答案

如果你给你的元素一个id(尽管无论你如何找到元素,相同的概念都有效(,你可以这样做:

(function() {
    setTimeout(check, 0);
    function check() {
        var elm = document.getElementById('theId');
        if (!elm) {
            setTimeout(check, 250); // Check again in a quarter second or so
            return;
        }
        // Use the element
    }
})();

在那里,我们几乎立即启动第一次检查,如果我们没有找到元素,我们会在 250 毫秒后再次安排检查。因为我们只在上一个检查没有找到元素时才安排下一次检查,所以我们只根据需要循环多次来查找元素。

如果元素没有id,大概你以某种方式找到了它(querySelectorquerySelectorAll等(,因此会知道你是否找到了它,并能够安排下一次检查。

可能值得对其进行限制,例如:

(function() {
    var start = new Date();
    setTimeout(check, 0);
    function check() {
        var elm = document.getElementById('theId');
        if (!elm) {
            if (new Date() - start > 5000) { // More than five seconds
                throw "Couldn't find the element";
            }
            setTimeout(check, 250); // Check again in a quarter second or so
            return;
        }
        // Use the element
    }
})();

。因此,如果您更改了 HTML 并且该元素不再存在,则在测试时会看到错误,并提醒您更新代码。

编辑:现在你已经展示了你的代码,它有一些问题。

例如,getElementsByClassName()返回一个数组,因此您无法执行此操作:

var headerElement = document.getElementsByClassName("flex-module-header");
if (headerElement.innerHTML == undefined) 

您必须这样做:

var headerElement = document.getElementsByClassName("flex-module-header");
if (!headerElement || headerElement.length == 0 || headerElement[0].innerHTML == undefined) 

问题中提供代码之前的原始答案:

如果你向我们展示了整个问题,我们可能会想出一些更优雅的东西,但根据你告诉我们的内容,我们现在只能建议钝力方法:

(function() {
    var item, cnt = 0; 
    var pollTimer = setInterval(function() {
        ++cnt;
        if (!item) {
            // cache the item if found so future checks will be faster
            item = document.getElementById("testObject");
        }
        if (item && item.innerHTML != "") {
            // the item has innerHTML now, you can process it
            // with code here
            clearInterval(pollTimer);
        } else {
            if (cnt > 100) {
               // if not found after 100 checks (20 seconds)
               // then stop looking so we don't keep going forever
               clearInterval(pollTimer);
            }
        }
    }, 200);
}){};