0

我很难让 JavaScript 函数执行一次且仅在 DOM 元素完全加载时执行。我尝试了无数的setIntervals、seTimeouts、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);
    }
}
4

2 回答 2

3

发布代码后更新答案:

getElementsByClassName返回 a 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 字符串并为其构建新的节点和元素。

所以需要做一些工作,还有可能消除事件处理程序。如果您要添加新的子元素,请查看使用createElementand appendChild。如果您要添加更多文本内容,请查看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 并且该元素不再存在,您会在测试时看到错误并被提醒更新您的代码。

于 2012-04-30T08:38:49.077 回答
1

编辑:既然您已经展示了您的代码,那么它存在一些问题。

例如,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);
}){};
于 2012-04-30T08:42:27.187 回答