如何让JavaScript在页面加载后执行?

1082

我正在执行一个外部脚本,使用在 <head> 中的 <script>

由于脚本是在页面加载之前执行的,我无法访问 <body> 等内容。我希望在文档被“加载”(HTML完全下载并进入内存)后执行一些JavaScript。有没有事件可以挂钩到我的脚本执行时,以在页面加载时触发?


对于“页面加载后立即执行”仍然太早的情况:在所有window.onload脚本完成后运行JavaScript? - Inigo
26个回答

1189
这些解决方案都可以使用:
如评论中所提到的,使用defer
<script src="deferMe.js" defer></script>

或者

<body onload="script();">

或者

document.onload = function ...

或者甚至
window.onload = function ...

注意事项

  • window.onload被认为比document.onload更标准
  • 使用deferwindow.onload是不显眼的

9
<body onload="script();">和 document.onload=function ...之间有什么区别? 这两种方法都可以在页面加载完成后执行JavaScript代码。但是,<body onload="script();">会等到整个页面包括图片和其他资源加载完毕后才触发,而document.onload=function ...只需要等待HTML文档加载完成就可以触发。如果您的JavaScript代码依赖于特定的资源(如图像),则使用<body onload="script();">可能更合适。否则,使用document.onload=function ...可能更有效率。 - Mentoliptus
17
"document.onload=" 是非侵入式的,意思是它不会干扰页面的正常加载。更多信息可参考维基百科上的“Unobtrusive JavaScript”词条。 - marcgg
29
OP没有提到jQuery。我的答案中也提供了一个不显眼的选项。 - marcgg
7
document.onload不起作用。我发现了一个有相同问题的帖子https://dev59.com/llTTa4cB1Zd3GeqPvLoU。看起来document.onload不是一个选项。 - spottedmahn
8
考虑到在DOM完成加载之后运行JavaScript的目标,所有这些选项都要晚得多了。对于这个目标,有两个更好的选择:1)在头部使用defer属性链接JavaScript;2)将任何访问DOM的JavaScript移到页面底部,在</body>之前立即执行。 - ToolmakerSteve
显示剩余9条评论

479
在正确的时刻触发脚本
快速了解如何在希望加载/执行的时刻加载/运行脚本。

使用"defer"关键字
<script src="script.js" defer></script>

使用defer会在domInteractive(document.readyState = "interactive")之后触发,并在触发"DOMContentLoaded"事件之前执行。如果您需要在所有资源(图像、脚本)加载完成后执行脚本,请使用"load"事件或目标一个document.readyState状态。请继续阅读了解有关这些事件/状态的更多信息,以及与脚本获取和执行时间相关的async和defer属性。

这个布尔属性用于告诉浏览器脚本应在文档解析完成之后但在触发DOMContentLoaded之前执行。

带有defer属性的脚本将阻止DOMContentLoaded事件的触发,直到脚本加载并完成评估。

资源:https://developer.mozilla.org/en-US/docs/Web/HTML/Element/script#attributes * 请参考底部的图片以获取更详细的解释。

事件监听器 - 请记住页面加载有多个事件:

"DOMContentLoaded"

当初始的HTML文档已经完全加载和解析完成时,会触发此事件,而无需等待样式表、图像和子框架加载完成。在这个阶段,您可以根据用户设备或带宽速度进行图像和CSS的程序化优化加载。 在DOM加载完成后执行(在图像和CSS之前)。
document.addEventListener("DOMContentLoaded", function(){
    //....
});

注意:同步JavaScript会暂停DOM的解析。 如果您希望在用户请求页面后尽快解析DOM,您可以将JavaScript设置为异步,并且优化样式表的加载。
"load"
一个非常不同的事件,**load**,应该只用于检测*完全加载的页面*。在使用load的地方使用DOMContentLoaded是非常常见的错误,所以请谨慎使用。

在所有内容加载和解析完成后执行:
window.addEventListener("load", function(){
    // ....
});

MDN 资源: https://developer.mozilla.org/zh-CN/docs/Web/Events/DOMContentLoaded https://developer.mozilla.org/zh-CN/docs/Web/Events/load

MDN 所有事件列表:
https://developer.mozilla.org/zh-CN/docs/Web/Events

使用 readyStates 的事件监听器 - 另一种解决方案 (readystatechange):


您还可以跟踪 document.readystatechange 的状态以触发脚本执行。
// Place in header (do not use async or defer)
document.addEventListener('readystatechange', event => {
  switch (document.readyState) {
    case "loading":
      console.log("document.readyState: ", document.readyState,
       `- The document is still loading.`
       );
      break;
    case "interactive":
      console.log("document.readyState: ", document.readyState, 
        `- The document has finished loading DOM. `,
        `- "DOMContentLoaded" event`
        );
      break;
    case "complete":
      console.log("document.readyState: ", document.readyState, 
        `- The page DOM with Sub-resources are now fully loaded. `,
        `- "load" event`
        );
      break;
  }
});

MDN 资源:https://developer.mozilla.org/zh-CN/docs/Web/API/Document/readyState

脚本应该放在哪里(使用 async/defer 与否)?

这也非常重要,要知道脚本应该放在哪里以及它在 HTML 中的位置,以及像 defer 和 async 这样的参数将会影响脚本的获取、执行和 HTML 的阻塞。

在下面的图像中,黄色标签“Ready”表示HTML DOM加载结束的时刻。然后它触发:document.readyState = "interactive" >>> 延迟脚本 >>> DOMContentLoaded事件(按顺序进行)。

enter image description here

enter image description here

enter image description here

enter image description here

如果您的脚本使用了asyncdefer,请阅读以下内容:https://flaviocopes.com/javascript-async-defer/

如果以上所有内容还不够清晰...

如果您需要在其他脚本运行之后(包括那些计划在最后运行的脚本,例如计划在"load"事件上运行的脚本)运行您的脚本,该怎么办?请参阅如何在所有window.onload脚本完成后运行JavaScript? 如果您需要确保您的脚本在其他脚本之后运行,而不管它何时运行,该怎么办?上述问题的答案也涵盖了这一点。

1
@Peter,我很确定这是独立的,最好放在JavaScript文件的底部,这样它会最后执行。 - arodebaugh
4
这是最完整的答案。onload 是之后发生的,但一开始人们并没有意识到这一点。 - tfont
4
关于 "异步 JavaScript",需要澄清的是,对于不立即运行的脚本,有两种选择。其中,defer 通常优于 async,因为 defer 不会打断 HTML 解析或呈现,并且在运行时,DOM 已准备好。使用 defer 和 async 高效加载 JavaScript - ToolmakerSteve
2
+1 我尝试使用 window.onload = ...,以为我的脚本会等待所有内容完全下载后再运行,但似乎 window.onload 实际上的行为类似于 document.addEventListener("DOMContentLoaded", ...,而 window.addEventListener("load", ... 确实会等待所有内容完全下载。我本来认为 window.onload 应该等同于 window.addEventListener("load", ... 而不是 document.addEventListener("DOMContentLoaded", ... ?? 我在 Chrome 和 FF 中得到了相同的结果。 - wkille
@DevWL,我认为你的意思是“defer”...因为“async”仍然可以在所有HTML加载之前执行。 - RayLoveless
@RayLoveless 是的,我是指延迟加载。因此,JavaScript 会暂停 DOM 的渲染,最好的方法是将其放置在页面底部或使用 defer 属性加载 JavaScript 脚本(放置在头部)。谢谢。 - DevWL

212

一种相对便携、非框架的方式,让你的脚本在加载时设置一个函数运行:

if(window.attachEvent) {
    window.attachEvent('onload', yourFunctionName);
} else {
    if(window.onload) {
        var curronload = window.onload;
        var newonload = function(evt) {
            curronload(evt);
            yourFunctionName(evt);
        };
        window.onload = newonload;
    } else {
        window.onload = yourFunctionName;
    }
}

13
对于几乎与我6个月前要想出的内容完全相同的发帖点赞。如果其他框架和代码添加了onload事件,而您又想添加onload事件而不清除其他onload事件,则可能需要编写此类代码。我将我的实现包含为一个函数,它需要var newonload = function(evt) { curronload(evt); newOnload(evt); },因为由于某些原因,我使用的框架需要传递一个事件到onload事件。 - Grant Wagner
6
我在测试中发现,使用attachEvent()附加处理程序时,代码的执行顺序是不确定的。如果处理程序的执行顺序很重要,您可能需要省略window.attachEvent分支。 - Grant Wagner
那么这将把这个函数作为一个“附加”函数添加到OnLoad中运行,对吗?(而不是替换现有的onload事件) - Clay Nichols
@ClayNichols:正确。 - chaos
2
好的解决方案,但现在已经过时:https://developer.mozilla.org/zh-CN/docs/Web/API/EventTarget/attachEvent - Renan
提醒:不要修改不属于你的对象,特别是全局变量...请参考Renan的链接。 - Ray Foss

137
您可以在标签中添加一个"onload"属性。
...<body onload="myFunction()">...

或者如果你使用jQuery,你可以这样做

$(document).ready(function(){ /*code here*/ }) 

or 

$(window).load(function(){ /*code here*/ })

我希望这回答了您的问题。

请注意,$(window).load会在页面上的文档渲染后执行。


81
如果脚本在文档的中加载,则可以在

1
我喜欢这个解决方案,因为它不需要任何JavaScript代码,只需要一个HTML属性 <3 - sarkiroka
这在根本上是错误的。延迟加载并不能阻止页面onload后执行。无论是defer还是async脚本都会在页面加载前被加载,从而提高“domInteraction”时间。 - Akansh
2
@Akansh,原 PO 只是想要一种确保 DOM 加载完毕后再执行其脚本的方法。defer 就是这样做的,我在我的回答中分享了文档片段,上面明确写着这一点。 - Daniel Price
看起来你可以使用 async false 让内联脚本在页面加载后加载? "要实现类似的动态插入脚本效果,可以改用 async=false。- 设置这个布尔属性是为了告诉浏览器,脚本应该在文档解析完成后但在触发 DOMContentLoaded 事件之前执行。" - 1.21 gigawatts

35
这是一个基于页面加载后延迟加载JS的脚本。
<script type="text/javascript">
  function downloadJSAtOnload() {
      var element = document.createElement("script");
      element.src = "deferredfunctions.js";
      document.body.appendChild(element);
  }

  if (window.addEventListener)
      window.addEventListener("load", downloadJSAtOnload, false);
  else if (window.attachEvent)
      window.attachEvent("onload", downloadJSAtOnload);
  else window.onload = downloadJSAtOnload;
</script>

我应该把它放在哪里?

将代码粘贴到 HTML 文件的底部,紧靠着 </body> 标签之前。

它是做什么用的?

此代码表示等待整个文档加载完毕后,再加载外部文件 deferredfunctions.js

以下是上述代码的示例- 推迟JavaScript渲染

我根据推迟加载 JavaScript页面速度谷歌概念编写了本文,并参考了这篇文章延迟加载JavaScript


我认为这个解决方案,结合chaos用户的答案(https://dev59.com/NnRA5IYBdhLWcg3w1BqW#807997),将是你能做的最好的事情。 - Lucas Vazquez

23

JavaScript

document.addEventListener('readystatechange', event => { 

    // When HTML/DOM elements are ready:
    if (event.target.readyState === "interactive") {   //does same as:  ..addEventListener("DOMContentLoaded"..
        alert("hi 1");
    }

    // When window loaded ( external resources are loaded too- `css`,`src`, etc...) 
    if (event.target.readyState === "complete") {
        alert("hi 2");
    }
});

同样适用于jQuery:

$(document).ready(function() {   //same as: $(function() { 
     alert("hi 1");
});

$(window).load(function() {
     alert("hi 2");
});





注意:- 不要使用下面的标记(因为它会覆盖其他相同类型的声明):

document.onreadystatechange = ...

23

查看如何钩取 document.onload 或在 jQuery 中使用 $(document).load(...)


这个事件可靠吗?(跨浏览器... IE6+ FF2+ 等) - Robin Rodricks
1
是的,这是跨浏览器的,标准 DOM。 - Daniel A. White
20
更标准的是window.onload而不是document.onload。据我所知。 - Peter Bailey
1
onload 是在所有资源(如字体和图片)加载并正确地视觉更新之前还是之后被调用的? - Ωmega

14

我发现在更复杂的页面上,有时候并不是所有的元素都在window.onload被触发的时候加载完成。如果是这种情况,在你的函数之前添加setTimeout来延迟一会儿执行。虽然这不太优雅,但它是一个简单的技巧,表现良好。

window.onload = function(){ doSomethingCool(); };

变成...

window.onload = function(){ setTimeout( function(){ doSomethingCool(); }, 1000); };

11
<script type="text/javascript">
  function downloadJSAtOnload() {
   var element = document.createElement("script");
   element.src = "defer.js";
   document.body.appendChild(element);
  }
  if (window.addEventListener)
   window.addEventListener("load", downloadJSAtOnload, false);
  else if (window.attachEvent)
   window.attachEvent("onload", downloadJSAtOnload);
  else window.onload = downloadJSAtOnload;
</script>

http://www.feedthebot.com/pagespeed/defer-loading-javascript.html


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接