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

Javascript 只在元素完全加载时执行一次的函数,javascript,element,execute,exists,Javascript,Element,Execute,Exists,让JavaScript函数只在DOM元素完全加载时执行一次有点困难。我尝试了无数次组合setIntervals、seTimeouts、FOR循环、IF语句、WHILE循环等,但都一无所获 我确实成功地让它工作了一次,但它是偶然的,因为我只能通过将函数延迟2秒(这不好,因为无法确切地知道加载需要多长时间)和快速地反复启动相同的函数来让它工作,这也不好 我只需要一些东西来不断地扫描页面,以判断某个元素是否存在并包含内容(innerHTML!=undefined,或者其他什么),加载一块代码后立即执行

让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);
    }
}
完成的代码

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
没有
innerHTML
属性。如果您想要第二个匹配元素,请使用
[1]
获取它,然后测试是否返回了某些内容(或者如果您愿意,可以使用
.length>1
首先检查,但是
节点列表
记录为对于不存在的索引返回
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+=“标记”通常不是向元素追加内容的好方法。以下是浏览器在执行此操作时所做的操作:

  • 旋转元素和它拥有的任何子元素,构建一个HTML字符串以提供给JavaScript层
  • 从元素中删除所有内容,其副产品是删除子元素上的任何事件处理程序
  • 解析HTML字符串并为其构建新节点和元素
  • 所以需要做一点工作,而且可能会清除事件处理程序。如果要添加新的子元素,请查看使用and。如果要添加更多文本内容,请查看并
    appendChild


    原始答案

    如果您为元素指定了一个
    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
    ,那么您可能会以某种方式找到它(
    querySelector
    querySelectorAll
    ,等等),这样您就会知道是否找到了它,并能够安排下一次检查

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

    (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
    返回一个元素,而不是一个元素
    NodeList
    没有
    innerHTML
    属性。如果您想要第二个匹配元素,请使用
    [1]
    获取它,然后测试是否返回了某些内容(或者如果您愿意,可以使用
    .length>1
    首先检查,但是
    节点列表
    记录为对于不存在的索引返回
    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+=“标记”通常不是向元素追加内容的好方法。以下是浏览器在执行此操作时所做的操作:

  • 旋转元素和它拥有的任何子元素,构建一个HTML字符串以提供给JavaScript层
  • 从元素中删除所有内容,其副产品是删除子元素上的任何事件处理程序
  • 解析HTML字符串并为其构建新节点和元素
  • 所以需要做一点工作,而且可能会清除事件处理程序。如果要添加新的子元素,请查看使用and。如果要添加更多文本内容,请查看并
    appendChild


    原始答案

    如果您为元素指定了一个
    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
    ,那么您可能会以某种方式找到它(
    querySelector
    querySelectorAll
    ,等等),这样您就会知道是否找到了它,并能够安排下一次检查

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

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

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

    如果你向我们展示了整个问题,我们可能会想出更优雅的方法,但根据你告诉我们的,