我应该在HTML标记的哪里放置<script>标记?


1487

将JavaScript嵌入HTML文档中时,在哪里放置<script>标签和包含的JavaScript 的正确位置?我似乎记得,您不应该将它们放在本<head>节中,但是放在本节的开头<body>也是不好的,因为必须在完全呈现页面(或类似的东西)之前解析JavaScript。这似乎将本节的末尾<body>作为<script>标记的逻辑位置。

所以,在这里把正确的地方<script>标记?

(此问题引用了这个问题,在该问题中建议将JavaScript函数调用从<a>标记移到<script>标记。我专门使用jQuery,但更通用的答案也是合适的。)


如果您还只是在寻找一个简单的解决方案,并且正在使用某些服务器端生成器(如Jekyll),我建议将脚本包括在内。简单得多!
cregox

Answers:


1861

当浏览器加载带有<script>标签的网站时,会发生以下情况:

  1. 提取HTML页面(例如index.html)
  2. 开始解析HTML
  3. 解析器遇到一个<script>引用外部脚本文件的标记。
  4. 浏览器请求脚本文件。同时,解析器将阻止并停止解析页面上的其他HTML。
  5. 一段时间后,脚本将被下载并随后执行。
  6. 解析器将继续解析HTML文档的其余部分。

步骤4会导致不良的用户体验。您的网站基本上会停止加载,直到您下载了所有脚本。如果用户讨厌一件事,它正在等待网站加载。

为什么会发生这种情况?

任何脚本都可以通过document.write()或其他DOM操作插入自己的HTML 。这意味着解析器必须等到脚本被下载并执行后,才能安全地解析文档的其余部分。毕竟,脚本可能已经在文档中插入了自己的HTML。

但是,大多数JavaScript开发人员在文档加载不再处理DOM 。取而代之的是,他们等到文档加载完成后再进行修改。例如:

<!-- index.html -->
<html>
    <head>
        <title>My Page</title>
        <script src="my-script.js"></script>
    </head>
    <body>
        <div id="user-greeting">Welcome back, user</div>
    </body>
</html>

Javascript:

// my-script.js
document.addEventListener("DOMContentLoaded", function() { 
    // this function runs when the DOM is ready, i.e. when the document has been parsed
    document.getElementById("user-greeting").textContent = "Welcome back, Bart";
});

由于您的浏览器不知道my-script.js在下载并执行文档之前不会修改文档,因此解析器将停止解析。

过时的推荐

解决此问题的旧方法是将<script>标签放在的底部<body>,因为这样可以确保解析器直到最后都不会被阻塞。

这种方法有其自身的问题:浏览器在解析整个文档之前无法开始下载脚本。对于具有大型脚本和样式表的大型网站,能够尽快下载脚本对于提高性能非常重要。如果您的网站在2秒钟内未加载,人们将转到另一个网站。

在最佳解决方案中,浏览器将尽快开始下载脚本,同时解析文档的其余部分。

现代方法

如今,浏览器支持脚本的asyncdefer属性。这些属性告诉浏览器在下载脚本时继续解析是安全的。

异步的

<script src="path/to/script1.js" async></script>
<script src="path/to/script2.js" async></script>

具有async属性的脚本是异步执行的。这意味着该脚本在下载后立即执行,同时不会阻塞浏览器。
这意味着可以在脚本1之前下载并执行脚本2。

根据http://caniuse.com/#feat=script-async,所有浏览器中的97.78%支持此功能。

推迟

<script src="path/to/script1.js" defer></script>
<script src="path/to/script2.js" defer></script>

具有defer属性的脚本按顺序执行(即,第一个脚本1,然后是脚本2)。这也不会阻止浏览器。

与异步脚本不同,延迟脚本仅在整个文档加载后才执行。

根据http://caniuse.com/#feat=script-defer的说法,所有浏览器中的97.79%支持此功能。98.06%至少部分支持它。

关于浏览器兼容性的重要说明:在某些情况下IE <= 9可能会无序执行延迟的脚本。如果您需要支持这些浏览器,请先阅读此内容

结论

当前最新的技术是将脚本放入<head>标签中并使用asyncdefer属性。这样一来,您的脚本就可以尽快下载,而不会阻止浏览器。

好消息是,您的网站仍应在不支持这些属性的2%的浏览器上正确加载,同时加快其他98%的浏览器的速度。


63
我很惊讶没有人引用Google的解释... developers.google.com/speed/docs/insights/BlockingJS
Casey Falk 2014年

6
我不清楚什么涉及DOM,哪些不涉及。你能澄清一下吗?在jquery.js之类的文件上进行异步加载是否安全?
道格

7
@Doug例如document.write在dom上运行。问题不在于如果一个脚本操纵DOM,但是它。只要domready事件触发后发生所有dom操作,就可以。jQuery是一个库,因此不会(或不应该)自行操作dom。
巴特

24
这个答案是误导的。当现代浏览器到达可能影响HTML的同步脚本标记时,它们不会停止解析,它们只是停止渲染/执行,并继续乐观地解析以开始下载其他资源,如果不影响HTML,这些资源随后可能会被请求。
Fabio Beltramini 2014年

40
为什么不使用asyncand defer属性?我的意思是,我查看了许多来自Internet的HTML来源,但在任何地方都看不到asyncdefer属性。...?
约翰cj

239

如上所述,在结束标签前

http://developer.yahoo.com/performance/rules.html#js_bottom

将脚本放在最下面

脚本引起的问题是它们阻止并行下载。HTTP / 1.1规范建议浏览器每个主机名并行下载最多两个组件。如果从多个主机名提供映像,则可以并行进行两个以上的下载。但是,在下载脚本时,即使使用不同的主机名,浏览器也不会启动任何其他下载。


7
同意该概念及其解释。但是,如果用户开始播放页面会发生什么。假设我有一个AJAX下拉菜单,该页面将在用户显示页面后开始加载,但是在加载页面时,用户单击了它!如果“非常不耐烦”的用户提交该表格怎么办?
Hemant Tank 2012年

9
@Hermant Old注释,但是您可以通过技巧来默认禁用字段,然后在完全加载DOM时使用JS启用它们。如今,Facebook似乎正在这样做。
novato 2012年

2
刚刚用chrome测试过,以检查是否仍然相同。它是。您可以在此处查看浏览器页面加载时间的差异。stevesouders.com/cuzillion
密码

46
如果这是最佳实践,为什么堆栈溢出会在<head>中包括所有脚本标记?:-P
菲利普(Philip)

10
在某些情况下,尤其是在ajax繁重的站点中,头部加载实际上可以导致更快的加载时间。请参阅:encosia.com/dont-let-jquerys-document-ready-slow-you-down(请注意,jQuery中不赞成使用“ live()”函数,但本文仍适用于“ on()”或“委托”功能)。可能还需要加载<head>以保证@Hermant指出的正确行为。最后,出于网站解释的原因,modernizr.com / docs建议将其脚本放在<head>中。
内森

76

非阻塞脚本标签可以放置在几乎任何地方:

<script src="script.js" async></script>
<script src="script.js" defer></script>
<script src="script.js" async defer></script>
  • async 脚本将在可用后立即异步执行
  • defer 文档解析完成后执行脚本
  • async defer 如果不支持异步,脚本将退回到延迟行为

此类脚本将在文档准备好后异步执行/执行,这意味着您无法执行以下操作:

<script src="jquery.js" async></script>
<script>jQuery(something);</script>
<!--
  * might throw "jQuery is not defined" error
  * defer will not work either
-->

或这个:

<script src="document.write(something).js" async></script>
<!--
  * might issue "cannot write into document from an asynchronous script" warning
  * defer will not work either
-->

或这个:

<script src="jquery.js" async></script>
<script src="jQuery(something).js" async></script>
<!--
  * might throw "jQuery is not defined" error (no guarantee which script runs first)
  * defer will work in sane browsers
-->

或这个:

<script src="document.getElementById(header).js" async></script>
<div id="header"></div>
<!--
  * might not locate #header (script could fire before parser looks at the next line)
  * defer will work in sane browsers
-->

话虽如此,异步脚本具有以下优点:

  • 并行下载资源
    浏览器可以并行下载样式表,图像和其他脚本,而无需等待脚本下载和执行。
  • 源顺序独立性
    您可以将脚本放置在头部或正文中,而无需担心阻塞(如果使用CMS则很有用)。执行顺序仍然很重要。

通过使用支持回调的外部脚本,可以避免执行顺序问题。现在,许多第三方JavaScript API支持非阻塞执行。这是异步加载Google Maps API的示例。


2
今天,这是正确的答案-使用这种方法意味着可以更轻松地使小部件保持独立,而无需花哨的<head>包含逻辑。
Daniel Sokolowski

1
我很困惑,为什么你不能使用asyncdefer包括jQuery的时候,你在你的第二块指定:<script src="jquery.js" async></script>。你能解释为什么吗?我认为我需要在异步标签中添加性能(按照公认的答案),这样即使jQuery仍在加载,我的页面也可以加载。谢谢!
elbowlobstercowstand

3
@elbow 99%的时间后<script src=jquery.js>$(function(){ ... })页面中某些位置的块。异步加载不能保证在浏览器尝试解析这些块时会加载jQuery,因此会引发$未定义错误(如果从缓存中加载jQuery可能不会收到错误)。我回答了有关异步加载jQuery和保持jQuery的问题$(function(){ ... })。我会看看是否可以找到它,或者您可以看一下这个问题:stackoverflow.com/q/14811471/87015
Salman A'8

1
@SalmanA谢谢!是的,我属于那99%。我首先需要jquerylib来加载,然后需要其余的.js脚本。当我声明asyncdeferjquerylib script标签上时,我的.js脚本不起作用。我以为$(function(){ ... })保护了这一点—不用猜测。目前的解决方案:我不添加deferasyncjqueryLIB剧本,但我加async我跟进.js的脚本。注:我做的理由任何这是使谷歌网页速度高兴。再次感谢您的帮助!任何其他建议,欢迎。(或指向您先前答案的链接)。:)
elbowlobstercowstand 2015年

@elbow请参阅stackoverflow.com/a/21013975/87015,它只会给您一个想法,而不能给您完整的解决方案。您可以改为搜索“ jquery异步加载程序库”。
Salman A

38

由Yahoo!提倡的标准建议。卓越的性能团队将<script>标签放在文档正文的末尾,以免它们阻碍页面的呈现。

但是,如以下有关Google Analytics(分析)JavaScript文件的加载时间的答案所述,有些新方法可以提供更好的性能:

Steve Souders(客户端性能专家)提供了一些很棒的幻灯片,这些幻灯片涉及:

  • 并行加载外部JavaScript文件的不同技术
  • 它们对加载时间和页面呈现的影响
  • 浏览器显示什么样的“进行中”指示符(例如状态栏中的“正在加载”,沙漏鼠标光标)。

25

如果使用的是JQuery,则将javascript放在最合适的位置,并用于$(document).ready()确保在执行任何功能之前正确加载了所有内容。

附带说明:我喜欢本<head>节中的所有脚本标签,因为这似乎是最干净的地方。


14
在头...呃?<header>
Dan Lugg

7
请注意,使用$(document).ready()并不意味着您可以将JavaScript 放置在所需的任何位置 –仍必须将其放在<script src=".../jquery.min.js">包含jQuery 的位置之后,以便$存在。
罗里·奥肯

2
将脚本标记放在<head>部分中不是最佳选择-这将延迟页面可见部分的显示,直到脚本被加载。
Cyber​​Monk

不,@ Dan,header元素是HTML文档内容的一部分,并且应该在body元素. The head标签中出现一次或多次,以用于文档的元数据和非内容数据。这是,这些天,deferasync脚本标签的理想场所。header元素应仅包含描述其后文档部分的信息。
ProfK

1
@ ProfK,Dan在4年前发布此问题时指的是原始未编辑的问题。如您所见,问题是在一年后编辑的。
kojow7

18

2019年的现代方法是使用ES6模块类型脚本

<script type="module" src="..."></script>

默认情况下,模块是异步加载和延迟的。也就是说,您可以将它们放置在任何地方,它们将并行加载,并在页面加载完成后执行。

此处描述了脚本和模块之间的区别:

https://stackoverflow.com/a/53821485/731548

与脚本相比,模块的执行描述如下:

https://developers.google.com/web/fundamentals/primers/modules#defer

支持如下所示:

https://caniuse.com/#feat=es6-module


添加到知识库的好信息
Sagar

1
请注意,如果您仅在没有服务器的情况下尝试在本地文件系统上进行操作,则此操作将无效。至少在Chrome上,您尝试从HTML加载js时会遇到跨域错误,即使它们的起源相同,即文件系统。
hippietrail

11

XHTML不会验证脚本是否位于head元素之外的任何其他位置。 事实证明它可以无处不在。

您可以使用jQuery之类的方法推迟执行,因此放置位置无关紧要(解析期间对性能的影响很小)。


1
XHTML将使用正文中的脚本标签(严格的和过渡的)进行验证。但是,样式标签可能仅位于头部。
I.devries

11
<script src="myjs.js"></script>
</body>

脚本标记应始终在主体关闭之前或HTML文件的底部之前使用。

那么您可以在加载js文件之前先查看页面的内容。

如果需要,请检查此内容:http : //stevesouders.com/hpws/rule-js-bottom.php


2
这实际上回答了这个问题。我想知道几乎所有发布的示例都没有给出“页面结尾”的正确视觉上下文
肯·英格拉姆

1
这个答案很容易引起误解,而且很可能是错误的。GoogleMDN中的文章 表明,同步JS(此处就是这种情况)始终会阻止DOM的构建和解析,这将导致延迟的首次渲染。因此,直到将JS文件提取并完成执行,无论您将JS文件放在HTML文档中的同步位置如何,都无法看到页面的内容
Lingaraju EV

它还引用了2009年提出的观点,不再相关。
toxaq

7

常规的(并且被广泛接受的)答案是“在最底层”,因为这样,在任何内容开始执行之前,整个DOM都将被加载。

出于各种原因,有一些持不同政见者从可用的实践开始,故意以页面onload事件开始执行。


6

放置<script>标签</body>最佳位置是在关闭标签之前,因此下载并执行它不会阻止浏览器解析文档中的html,

此外,从外部加载js文件也有其自身的优势,例如它将被浏览器缓存并可以加快页面加载时间,它可以将HTML和JavaScript代码分开,有助于更好地管理代码库

但是现代浏览器还支持其他一些最佳方式,例如asyncdefer加载外部javascript文件。

异步和延迟

通常,HTML页面的执行逐行开始。当遇到外部JavaScript元素时,HTML解析将停止,直到下载JavaScript并准备执行为止。可以使用deferasync属性更改此常规页面执行。

Defer

使用defer属性时,JavaScript与HTML解析并行下载,但是只有在完整的HTML解析完成后才能执行。

<script src="/local-js-path/myScript.js" defer></script>

Async

当使用async属性时,JavaScript会在遇到脚本后立即下载,下载后,它将与HTML解析一起异步(并行)执行。

<script src="/local-js-path/myScript.js" async></script>

何时使用哪些属性

  • 如果您的脚本独立于其他脚本并且是模块化的,请使用 async
  • 如果您要使用加载script1和script2 async,则
    它们在下载
    并可用后将与HTML解析并行运行。
  • 如果您的脚本依赖于另一个脚本defer,请同时使用这两个脚本:
  • 当使用依次加载script1和script2时defer,保证script1首先执行,
  • 然后script2将在完全执行script1之后执行。
  • 如果script2依赖于script1,则必须执行此操作。
  • 如果您的脚本足够小,并且受其他类型的脚本的依赖,async请使用没有属性的async脚本并将其放在所有脚本之上。

参考:Knowledgehills.com


3

取决于,如果要加载的脚本对于页面的样式设置/使用页面中的操作(例如单击按钮)是必要的,则最好将其放在顶部。如果您的样式是100%CSS,并且按钮操作具有所有后备选项,则可以将其放置在底部。

或最好的事情(如果这不是问题)是您可以创建一个模式加载框,将您的JavaScript放在页面底部,并在加载脚本的最后一行时使其消失。这样,您可以避免用户在加载脚本之前在页面中使用操作。并且避免样式不当。


3

最后包括脚本的地方主要用于首先显示网站内容/样式的地方。

包含在头中的脚本会尽早加载脚本,并且可以在加载整个网站之前使用。

如果最后输入了脚本,则只有在加载了整个样式和设计之后才能进行验证,这对于快速响应的网站是不赞赏的。


2

取决于脚本及其用法,最好的办法(就页面加载和渲染时间而言)可能不是本身使用常规的<script> -tag,而是异步地动态触发脚本的加载。

有一些不同的技术,但是最直接的方法是在触发window.onload事件时使用document.createElement(“ script”)。然后,当页面本身已呈现时,将首先加载脚本,因此不会影响用户必须等待页面出现的时间。

自然地,这要求脚本本身不需要呈现页面。

有关更多信息,请参阅Steve Souders(YSlow的创建者,但现在是Google)的帖子Coupling async scripts


2
  • 如果您仍然非常关心IE <10中的支持和性能,最好始终使脚本标记成为HTML正文的最后一个标记。这样,您可以确定DOM的其余部分已加载,并且不会阻塞和渲染。

  • 如果您对IE <10不再太在意,则可能需要将脚本放在文档的开头,并defer用来确保脚本仅在DOM被加载后才能运行(<script type="text/javascript" src="path/to/script1.js" defer></script>)。如果您仍然希望代码在IE <10中工作,请不要忘记将代码window.onload均匀地包裹起来!


在接受的答案中,这被称为“过时的建议”。如果仍然如此,您可能应该提供一些参考来支持它。
dakab '16

1

脚本会阻止DOM加载,直到它被加载并执行。

如果将脚本放在<body>所有DOM 的末尾,则有机会加载和呈现(页面将“显示”得更快)。<script>将有权访问所有这些DOM元素。

另一方面,将其放置在<body>开始或之后的位置将执行脚本(那里仍然没有DOM元素)。

您将包括jQuery,这意味着您可以将其放置在任意位置并使用.ready()


1

我认为这取决于网页的执行情况。如果要显示的页面在未先加载JavaScript的情况下无法正确显示,则应首先包含JavaScript文件。但是,如果您无需先下载JavaScript文件就可以显示/渲染网页,则应将JavaScript代码放在页面底部。因为它将模拟快速的页面加载,并且从用户的角度来看,似乎该页面的加载速度更快。


1

您可以将大多数<script>引用放在的末尾<body>
但是,如果页面上有使用外部脚本的活动组件,
则它们的依赖项(js文件)应该在此之前(最好在head标签中)。



-1

在HTML文档的末尾

因此,在执行时不会影响将HTML文档加载到浏览器中。


-1

编写JavaScript代码的最佳位置是在文档的末尾,该</body>标签位于标签之后或标签之前,以首先加载文档然后执行js代码。

<script> ... your code here ... </script>
</body>

如果您在JQuery主文档中输入以下内容,则该文档将在文档加载后执行:

<script>
$(document).ready(function(){
   //your code here...
});
</script>

它引发SyntaxError
Raz

您的答案没有错,但迫切需要进行更新。
保罗·卡尔顿

-2

对我而言,将脚本包含在HTML之后更有意义。因为大多数时候我需要在执行脚本之前加载Dom。我可以将其放在head标签中,但是我不喜欢所有文档加载侦听器的开销。我希望我的代码简短,甜美并且易于阅读。

当您在head标签之外添加脚本时,我听说野生动物园的旧版本有些古怪,但我说谁在乎。我不知道有人在用那种废话。

好的问题。


-6

您可以将脚本放在需要的地方,一个脚本并不比另一种更好。

情况如下:

页面是线性加载的,“自上而下”,因此,如果将脚本放在头部,可以确保它在所有内容之前都开始加载,现在,如果将其放入与代码混合的正文中,则可能导致页面加载难看。

确定良好实践并不取决于在哪里。

为了支持您,我将提及以下内容:

您可以放置​​:

并且页面将线性加载

页面与其他内容异步加载

该页面的内容将在加载完成之前和之后加载

好的做法是,什么时候实施?

我希望我能有所帮助,任何事情都可以回答我这个问题。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.