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

时间:2012-04-30 08:34:42

标签: javascript element execute exists

我在使用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);
    }
}

2 个答案:

答案 0 :(得分:3)

发布代码后

更新回答

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字符串并为其构建新的节点和元素。
  4. 这是一些工作,也是消除事件处理程序的可能性。如果您要添加新的子元素,请查看使用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
        }
    })();
    

    在那里,我们几乎立即启动了第一次检查,如果我们找不到该元素,我们会在250ms之后再次安排检查。因为我们只安排下一次检查,如果前一个没有找到该元素,我们只需要循环多次,以找到该元素。

    如果该元素没有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并且该元素不再存在,则会在测试时看到错误,并提醒您更新代码。

答案 1 :(得分: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);
}){};