仅在元素完全加载时执行一次的函数
Posted
技术标签:
【中文标题】仅在元素完全加载时执行一次的函数【英文标题】:Function that executes once and only when an element is fully loaded 【发布时间】:2012-04-30 08:34:42 【问题描述】:让 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);
【问题讨论】:
对不起。我正在辩论包括代码,但它经历了很多不同的版本,我不知道它是否会有所帮助或阻碍。无论如何我现在都会包含它。 【参考方案1】:在您发布代码后更新答案:
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";
通常不是将内容附加到元素的好方法。当您这样做时,浏览器会执行以下操作:
-
遍历元素及其具有的任何后代元素,构建一个 HTML 字符串以提供给 JavaScript 层。
从元素中删除所有内容,这会带来删除后代元素上的任何事件处理程序的副产品。
解析 HTML 字符串并为其构建新节点和元素。
所以需要做一些工作,并且还有可能清除事件处理程序。如果要添加新的子元素,请查看使用 createElement
和 appendChild
。如果您要添加更多文本内容,请查看createTextNode
和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 并且该元素不再存在,您会在测试时看到错误并提醒您更新代码。
【讨论】:
太完美了,非常感谢。我不知道您在代码中使用的一些方法。很高兴知道。 :) 另外,我认为需要进一步延迟,因为页面在填充内容之前定义了元素 - 除非延迟,否则无法注入 HTML,否则会出现控制台错误。 @BenHooper:很好,很高兴有帮助。顺便说一句,我添加了一条关于使用.innerHTML += "content"
的注释,这可能(或可能没有)有用。
啊,是的。这为其他链接停止工作的原因提供了一些线索。我现在看看链接。谢谢。 :)【参考方案2】:
编辑:既然你已经展示了你的代码,它有一些问题。
例如,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);
);
【讨论】:
我现在将阅读您的回答,谢谢。 :) 顺便说一句,我现在已经包含了代码。 @BenHooper - 您使用getElementsByClassName()
的方式存在一些基本问题,因为它返回一个数组,而不是单个元素。我已经在我对这个问题的回答的开头发表了评论。
感谢您的回答和时间,但我已将答案交给了 T.J.克劳德,他修改了我现有的代码,以便我能理解我在做什么。 :)以上是关于仅在元素完全加载时执行一次的函数的主要内容,如果未能解决你的问题,请参考以下文章