DOM-加载 JavaScript

内容列表

页面的动态交互离不开 Javascript,将 js 脚本引入页面时会阻塞页面加载,在某些时候我们则可以选择异步加载 js 脚本。

引入 js 代码

通常我们会将 JavaScript 代码写在一个单独的文件中,这样做的好处是页面整洁、结构更清晰,页面更小加载速度更快,同时也易维护。实际上在页面中引入 js 代码的方法有多种,我们按需选择即可。

外部脚本文件(允许跨域)

这种方式是最常用的,但要注意的是 <script> 标签内如果再写 js 代码会被直接忽略掉(不执行)。

<script src="./js/main.js"></script>

内联脚本

该方式会生成一个文本节点,如果我们通过节点的 innerHTMLtextContent 属性改变其文本内容(代码),并不会执行新的代码。此种方式也较常用。

<script>
    alert(1);
</script>

事件属性

我们可以在事件属性的值中写入一个方法引用,在事件触发时则会执行该方法。这种方法不推荐使用。

<body>
    <div onclick="show()"></div>
    <script>
        function show() { ... }
        // 作用等价形式为下
        var obj = document.querySelector('div');
        obj.onclick = show;
    </script>
</body>

JavaScript 协议

我们可以在元素的属性值中使用 JavaScript 协议来执行相应代码,但这种方式是不推荐使用的。

<a href="javascript: false">Link</a>

加载外部 js 文件

页面在加载过程中,默认是同步加载 js 的,如果遇到 <script> 标签则会停止页面的一切解析行为,开始(下载)执行相应的 JavaScript 代码,直至执行完毕。这会造成页面阻塞,页面的显示效果可能会因此受到影响。所以说,我们应该尽可能将不需要立即执行的 js 脚本放在 <body> 标签的尾部加载,这时候所有的页面元素已加载完毕,并不会对页面产生过多影响。

延迟加载

有时候我们的 js 文件可能很大,即便是放在 <body> 尾部也加载的很慢,或者要放的更靠前一点,这样页面的显示效果会被严重影响。此时,我们可以选择将一些不是很重要的 js 文件延迟到 <html> 标签关闭前再进行下载、执行。

  • defer

exp:

<!-- 一直延迟到 </html> 前才开始加载 -->
<script defer src="./js/main.js"></script>

我们可以延迟多个脚本的加载,但并不是所有的浏览器最后都按顺序并且在 DOMContentLoaded 事件前加载它们。

异步加载

也许我们并不想将 js 文件延迟到 </html> 标签关闭前才进行加载,此时我们可以通过异步加载来更提前一些。

  • async

exp:

<!-- 开始加载,但不阻塞页面 -->
<script async src="./js/main.js"></script>

异步加载会一开始就进行 js 文件的下载,但不会阻塞当前页面的解析,至于何时加载完我们也不知道,但是一加载完就会执行相应代码,此时也不会阻塞页面的解析。同样地,我们可以异步加载多个文件,但必定不会按顺序进行加载,这是我们要注意的。而且,IE10 才开始支持该属性。

<!-- 测试加载完毕顺序 -->
<script async onload="console.log('1 加载完毕')" src="./js/1.js"></script>
<script async onload="console.log('2 加载完毕')" src="./js/2.js"></script>
<script async onload="console.log('3 加载完毕')" src="./js/3.js"></script>

动态引入,异步加载

我们可以在不使用 async 属性的情况下,动态创建 <script> 标签并插入 DOM 树,此时引入的 js 文件也会进行异步加载。

<script>
    // 动态引入,异步加载,可跨域
    var nScript = document.createElement('script');
    nScript.src = "http://.../js/1.js";
    document.body.appendChild(nScript);
</script>

**该方法可以解决跨域访问资源的问题。**同样的,多个文件动态引入均会进行异步加载,但也不会按顺序加载。

获取所有 script 节点

我们可以通过预先设置的属性获取页面中所有 <script> 标签的节点集合,然后我们可以对其进行遍历打印一些信息。

  • document.scripts

exp:

<script>
    // 遍历并打印出 src
    Array.prototype.slice.call(document.scripts).forEach(function(e){
        console.log(e, e.src);
    })
</script>

我们获取的并不是一个数组,而是一个节点集合,要使用数组的 forEach() 方法我们就先将其转换成一个数组。

结语

由于 <script> 标签的同步加载特性,若我们的代码中有进行 DOM 操作,那么放在 <head> 中将会执行失败,因为此时 <body> 中要操作的元素还未解析出来。

参考

  • 《DOM 启蒙》,Cody Lindley,陈养剑 译

相关

DOM-节点集合

当从文档树中选取成组的节点或者使用预定义的节点集合时,这些节点都是放在 NodeList 或者一个 HTMLCollecton 之中,而不是一个数组(Array)中。

了解更多

Linux-Vim 编辑器

在 Linux 这样的命令行操作系统中,必须有一款功能强大的编辑器支持我们快速完成文本编辑,这就是 Vi 编辑器;通过对其加强和升级,Vim 编辑器比前者更为强大,拥有更多的功能和颜色高亮的特性,是程序员在 Linux 下编码的利器。

了解更多

Web 字体加载对 DOM 位置的影响

由于公司的业务会用到自己设计师定制的 Web 字体,所以一般在组件开发过程中会利用 CSS 引用该字体(@font-face),前段时间发现一个线上问题:在部分 iOS 机型上,DOM 元素的位置发生了错位。

了解更多