HTML中的JavaScript
前言
将JavaScript引入网页,首先要解决它与网页的主导语言HTML的关系问题。在JavaScript早期,网景公司的工作人员希望在将JavaScript引入 HTML页面的同时,不会导致页面在其他浏览器中渲染出问题。通过反 复试错和讨论,他们最终做出了一些决定,并达成了向网页中引入通用脚本能力的共识。当初他们的很多工作得到了保留,并且最终形成了 HTML规范。
------摘自《JavaScript高级程序设计(第四版)》
HTML中的JavaScript
JavaScript是通过<script>
标签插入HTML的,<script>
元素可以将JavaScript代码嵌入到HTML当中,跟其他标记混合在一起,也可用于引入保存在外部文件中的JavaScript。
行内代码和外部文件
- 行内代码:直接嵌入在HTML中的代码
- 外部文件:通过script元素的src属性进行引入
虽然可以直接在HTML文件中嵌入JavaScript代码,但通常认为最佳实践 是尽可能将JavaScript代码放在外部文件中。不过这个最佳实践并不是明确的强制性规则。推荐使用外部文件的理由如下。 可维护性。JavaScript代码如果分散到很多HTML页面,会导致维护困难。JavaScript代码如果分散到很多HTML页面,会导致维护 困难。而用一个目录保存所有JavaScript文件,则更容易维护,这样 开发者就可以独立于使用它们的HTML页面来编辑代码。 缓存。浏览器会根据特定的设置缓存所有外部链接的JavaScript文件,这意味着如果两个页面都用到同一个文件,则该文件只需下载 一次。这最终意味着页面加载更快。适应未来。
------摘自《JavaScript高级程序设计(第四版)》
script标签相关属性
script标签含有下列8个属性:
- defer:可选。表示脚本可以延迟到文档完全被解析和显示之后再执行。只对外部脚本文件有效。在IE7及更早的版本中,对行内脚本也可以指定这个属性。
- async:可选。表示应该立即开始下载脚本,但不能阻止其他页面动作,比如下载资源或等待其他脚本加载。只对外部脚本文件有效。
- src:可选。表示包含要执行的代码的外部文件。
- type:可选。代替language,表示代码块中脚本语言的内容类型 (也称MIME类型)。按照惯例,这个值始终都 是"text/javascript",尽 管"text/javascript"和"text/ecmascript"都已经废弃了。 JavaScript文件的MIME类型通常是"application/x-javascript", 不过给type属性这个值有可能导致脚本被忽略。在非IE的浏览器中 有效的其他值还 有"application/javascript"和"application/ecmascript"。如果这个值是module,则代码会被当成ES6模块,而且只有这时候代码中 才能出现import和export关键字。
- crossorigin:可选。配置相关请求的CORS(跨源资源共享)设置。默认不使用CORS。crossorigin="anonymous"配置文件请求不 必设置凭据标志。crossorigin="use-credentials"设置凭据标志, 意味着出站请求会包含凭据。
- integrity:可选。允许比对接收到的资源和指定的加密签名以验 证子资源完整性(SRI,Subresource Integrity)。如果接收到的资源 的签名与这个属性指定的签名不匹配,则页面会报错,脚本不会执 行。这个属性可以用于确保内容分发网络(CDN,Content Delivery Network)不会 提供恶意内容。
- charset:可选。使用src属性指定的代码字符集。这个属性很少使 用,因为大多数浏览器不在乎它的值。
- language:废弃。最初用于表示代码块中的脚本语言 (如"JavaScript"、"JavaScript 1.2"或"VBScript")。大多数浏 览器都会忽略这个属性,不应该再使用它。
script加载时机
执行过程: 遇到script标签 暂停渲染DOM 加载执行JavaScript 执行完成之后 <script>
注:行内代码没有加载JavaScript的过程,且不支持defer、async属性
<script defer>
<script async>
defer(推迟执行脚本)
defer属性被称为推迟执行脚本,表示脚本在执行的时候不会改变页面的结构。也就是说,脚本会被延迟到整个页面都解析完毕后再运行。在<script>
元素中设置defer属性,相当于告诉浏览器立即下载,但延迟执行。 HTML5规范要求脚本应该按照它们出现的顺序执行,因此第一个推迟的脚本会在第二个推迟的脚 本之前执行,而且两者都会在DOMContentLoaded事件之前执行不过在实际当中,推迟执行的脚本不一定总会按 顺序执行或者在DOMContentLoaded事件之前执行,因此最好只包含一个这样的脚本。
示例代码:
html
<html>
······
<!--console.log('example*')-->
<script defer src="./example1.js"></script>
<script defer src="./example2.js"></script>
<script src="./example3.js"></script>
<script >
addEventListener("DOMContentLoaded", (event) => {
console.log('last');
});
</script>
······
</html>
输出结果: 执行顺序为example3.js
优先与example1.js
,example1.js
优先于example2.js
。
知识补充
DOMContentLoaded
是浏览器的一个事件,当 HTML 文档完全解析,且所有延迟脚本(<script defer src="...">
和 <script type="module">
)下载执行完毕后(不包括样式表、图片等外部资源),会触发 DOMContentLoaded
事件。这个事件意味着DOM树已经构建完成,开发者可以通过监听这个事件来执行需要依赖DOM结构的JavaScript代码,而不必等待所有资源(如图片、外部脚本、样式表等)加载完毕。
Document:DOMContentLoaded 事件 - Web API 接口参考 | MDN (mozilla.org)
DOMContentLoaded
事件是在 HTML
被完全解析后触发的,而 load
事件是在整个页面及依赖的所有样式表和图片等资源都完成加载后触发。
async(异步执行脚本)
async与defer类似。都只适用于外部脚本,都会告诉浏览器立即开始下载。与defer不同的是,标记为async 的脚本并不保证能按照它们出现的次序执行。
示例代码:
html
<html>
······
<script async src="./example1.js"></script>
<script async src="./example2.js"></script>
<script >
addEventListener("DOMContentLoaded", (event) => {
console.log('last');
});
</script>
······
</html>
输出结果:
注意:
重点在于它们(脚本)之间没有依赖关系。给脚本添加async属性的目的是告诉浏览器,不必等脚本下载和执行完后再加载页面,同样也不必等到该异步脚本下载和执行后再加载其他脚本。正因为如此,异步脚本不应该在加载期间修改DOM。
异步脚本保证会在页面的load事件前执行,但可能会在DOMContentLoaded之前或之后。 使用async也会告诉页面你不会使用document.write,不过好的Web开发实践根本就不推荐使用这个方法。
知识补充:
load
事件在整个页面及所有依赖资源如样式表和图片都已完成加载时触发。它与 DOMContentLoaded
不同,后者只要页面 DOM 加载完成就触发,无需等待依赖资源的加载。
Window:load 事件 - Web API 接口参考 | MDN (mozilla.org)
script标签放在header和body的区别
区别(注意):
在HTML body部分中的JavaScript会在页面加载的时候被执行。 在HTML head部分中的JavaScripts会在被调用的时候才执行,但是在主页和其余部分代码之前预先装载。
script标签应该放在哪里
head内的JavaScript会阻塞页面渲染且无法获取body中的DOM节点。
CSS 应当写在 head 中,以避免页面元素由于样式缺失造成瞬间的白页或者给用户闪烁感。 ---吴钊
所有<script>
元素会依照它们在网页中出现的次序被解释。在不使用defer和async属性的情况下,包含在<script>
元素中的代码必须严格按次序解释。对不推迟执行的脚本,浏览器必须解释完位于<script>
元素中的代码,然后才能继续渲染页面的剩余部分。为此,通常应该把,<script>
元素放到页面末尾,介于主内容之后及</body>
标签之前。可以使用defer属性把脚本推迟到文档
------摘自《JavaScript高级程序设计(第四版)》
noscript标签
针对早期浏览器不支持JavaScript的问题,需要一个页面优雅降级的处理方案。最终,<noscript>
元素出现,被用于给不支持JavaScript的浏览器提供替代内容。虽然如今的浏览器已经100%支持JavaScript,但对于禁用JavaScript的浏览器来说,这个元素仍然有它的用处。<noscript>
元素可以包含任何可以出现在<body>
中的HTML元素,<script>
除外。
在下列两种情况下,浏览器将显示包含在<noscript>
中的内容:
-
浏览器不支持脚本;
-
浏览器对脚本的支持被关闭。
任何一个条件被满足,包含在
<noscript>
中的内容就会被渲染。否则,浏览器不会渲染<noscript>
中的内容。示例:
html<html> ······ <noscript> <p>This page requires a JavaScript-enabled browser.</p> </noscript> ······ </html>
当我们使用浏览器打开禁用JavaScript选项时便会出现
script标签ES modules模块化(补充)
语法:使用 type="module" 开启
xml
<script type="module">
console.log('this is es module')
</script>
每一个设置 type=module 的标签内都会形成一个私有作用域。作用域之间的变量不能直接共享。 默认外部文件加载为defer
如果浏览器不支持 es6 modules
,可以指定一个 nomodule
的 script
来进行 fallback
:
xml
<script type="module" src="main.js"></script>
<script nomodule src="fallback.js"></script>
对于不支持 es modules
的浏览器,第一个脚本不会被执行,第二个脚本会被执行。而对于支持 es modules
的浏览器,第一个脚本被执行,第二个脚本会被忽略。
总结script类型
- 内联脚本
- 外部脚本
外部脚本又分为:
- 普通外部脚本
- defer 外部脚本
- async 外部脚本
- es modules 脚本
结尾
作为一名前端小白,入门学习途径从教学视频和文章中开始,学习过于片面,根基不稳。对于基础三大件不够深入,此系列文章本意为对JavaScript学习进行记录从而加深记忆,如存在错误,望告知纠正。