[JS红宝书笔记]HTML中的JavaScript

544 阅读4分钟

序言

开启本章前,先回顾一下第一章节:介绍 JavaScript 作为一门用来与网页交互的脚本语言,自身通过 ECMAScript、BOM 和 DOM 三者来实现。我觉得第二章就开始一点点撩开 JavaScript 的“面纱”了,讨论的是将 JavaScript 引入 HTML 所形成的 HTML 规范,主要包括 <script><noscript> 两种元素。本章有一些值得注意的地方以及面试可能遇到的点。话不多说,开始。

MIND

<script> 元素

将 JavaScript 插入 HTML 的主要方法是使用 <script> 元素

嵌入方式

首先看看这个元素是如何将 JavaScript 插入 HTML 的,和 CSS 类似,JavaScript 可通过行内代码和外部文件嵌入到 HTML中。

  • 行内代码
    <script type="text/javascript">
    <!-- some code -->
    </script>
    
    这里的 type 是可选的,代替退出舞台的 language,指代脚本语言的内容类型,一般值都是 text/javascript
  • 外部文件
    <script src="index.js"></script>
    
    这里的 src 同样也是可选的(外部文件必须),用来表示要执行的代码的外部文件。

通常推荐以外部文件嵌入的方式,该方式有以下推荐理由:

  • 可维护性:代码汇集相比于分散,更容易维护,并独立于 HTML 文件。
  • 缓存:浏览器会缓存外部嵌入的 JavaScript 文件,无需重复下载,加载更迅速
  • 适应未来:避免 XHTML 对行内代码的影响(XHTML 退出历史舞台)

标签位置

了解了嵌入方式,再看看这个元素在文件的哪里起作用。<script> 标签的放置也是有很大学问的,现在和过去的做法有很大区别:过去是将元素放在页面的 <head> 标签内,现在通常将其放到 <body> 元素中的页面内容后面。

  • <head> 标签内
    <head>
        <title>async</title>
        <script src="index.js"></script>
    </head>
    
  • <body> 元素中的页面内容后面
    <body>
        <script src="index.js"></script>
    </body>
    

为什么需要更改位置呢?因为以往的方式会导致白屏现象(面试考点)。这一点与浏览器解析规则相关:

  • 页面在浏览器解析到 <body> 的起始标签才开始渲染
  • 浏览器解析到 JavaScript 时会阻碍文档的解析

以往的放置方式中,JavaScript 位于 <body> 的起始标签前,阻碍了文档渲染,期间浏览器窗口会完全空白。正因如此,现代的标签位置很好的解决了这个问题。

执行方式

标签都放好了,怎么执行 JavaScript 呢?一般情况下,JavaScript 加载后会马上执行,可能会阻塞文档的解析。不过可以通过设置,改善或者说解决这种问题,那就是 <script> 的两个属性 deferasync(面试考点)。

defer

defer 表示脚本在执行的时候不会改变页面的结构。

<head>
    <title>defer</title>
    <script defer src="index.js"></script>
</head>

听起来可能有点晦涩,不过我们要理解的就是,外部脚本文件添加这个属性后, JavaScript 会与文档同步解析,但不会马上执行,而是等到整个页面解析完之后再执行。并且一般情况下有两个特点:

  • JavaScript 还是依次执行
  • JavaScript 会在 DOMContentLoaded 事件之前执行

注意,以上特点是一般情况下,会有部分非主流浏览器忽略这个特定,按照通常做法处理 JavaScript,因此,还是将 <script> 放在在页面底部为好。

async

async 告知浏览器不必等脚本下载和执行完毕后再加载页面,同样不必等到该异步脚本下载和执行后再加载其他脚本。

<head>
    <title>async</title>
    <script async src="index.js"></script>
</head>

简单来说, async 也是保证脚本与文档同步加载,但会立即执行,可能阻塞文档的解析。同样两个与 defer 不同的特性:

  • JavaScript 执行不保证次序
  • JavaScript 会在页面 load 事件前执行,但不保证在 DOMContentLoaded 前后

动态加载

<script> 元素不仅能静态写入,同样可以动态生成:使用 DOM API 生成 script 元素同样可以加载脚本。

let script = document.createElement('script');
script.src = 'index.js';
document.head.appendChild(script);

这种方式生成的 <script> 元素是默认添加 async 的,当然可以修改这个设置:

...
script.async = false;
document.head.appendChild(script);

但这种动态添加且默认 async 的方式,会严重影响 JavaScript 的执行顺序,建议使用 <link> 提前显示声明

<link rel="preload" href="index.js">

JSONP 跨域

这一点是基于动态加载的,是一个面试考点,便私自加上了。

<script> 元素在解析资源时,会向 src 属性执行的路径发送一个 GET 请求。这个初始的请求不受浏览器同源策略限制。

根据上面条件,我们可以通过动态生成 script 元素,给 src 源发跨域的 GET 请求,这样的方式称为 JSONP 跨域。其实现方式暂时就不细说了,细节网上比比皆是,如传送门

noscript

其实这个元素很少用到,是用来给不支持 JavaScript 浏览器提供替代内容,现在仅用于禁用 JavaScript 的浏览器。这个元素在浏览器不支持脚本或脚本支持关闭情况下,会显示出 <script> 以外的 HTML 元素,使用如下:

<body>
	<noscript>
    	<span>浏览器不支持脚本或脚本支持关闭</span>
    </noscript>
</body>

总结

第二章主要讲的是 <script> 元素,包括:

  • 嵌入方式:行内代码和外部文件,推荐使用外部文件
  • 标签位置:<head> 中和 </body> 前,推荐第二种
  • 执行方式:defer (推迟) 和 async (同步)
  • 动态加载:动态生成 <script> 元素,可用于 JSONP 跨域 还有一个浏览器不支持或禁止脚本情况下的显示 HTML 内容。

整章的 <script> 元素相关需要掌握,下一章是 JavaScript 语言基础。