打印

BX9014: document.write 方式引入外部 JS 文件导致脚本程序执行顺序不同以及 DOM 树更新延迟问题

作者:钱宝坤 权一

标准参考

关于 document.write 方法请参考标准文档:DOM Level 2 HTML 规范 1.5. Objects related to HTML documents

问题描述

当脚本中存在以 document.write 的方式向页面内写入内容时,各浏览器对于执行各个线程的顺序可能不一致。

造成的影响

此现象造成在各浏览器里,某些需要延后执行的代码被立即执行,导致程序出错。

受影响的浏览器

IE6 IE7 IE8  

问题分析

由于 W3C DOM 标准文档中,没有明确说明使用 document.write 方法外引 JS 文件后,被引用脚本文件内继续重复使用 document.write 引用其他外部脚本文件时的执行顺序问题,因而导致实际环境中各个浏览器具体实现不一。

我们来分析以下代码 A:

<script>
document.write("before write h1.js"+"<br/>");
document.write("<script  src='h" + "1.js'><\/script>");
document.write("after write h1.js" +"<br/>");
</script>
/* h1.js */
document.write("This is h1.js"+"<br/>");

即在一个 SCRIPT 标签内,通过 document.write 的方式向文档输入内容,并在其间引入 JS 文件 A,A 内也包含 document.write,意在向文档输出某些内容。
各浏览器表现如下:

  IE6 IE7 IE8 Firefox Chrome Safari Opera
执行输出顺序 before write h1.js
after write h1.js
This is h1.js
before write h1.js
This is h1.js
after write h1.js

即 IE 是在当前 SCRIPT 标记内所有的 document.write 向文档中输出内容完成后,再处理以 document.write 方式引入的 JS 文件内的 document.write 写入流。

对于其他浏览器,则根据代码执行顺序依次处理 document.write 方式写入的内容。

 

此外,使用 document.write 方式写入可引用的外部 JavaScript 内容后,非 IE Opera 浏览器并不会立即更新 DOM 树。

分析以下代码 B:

<script>
document.write('<script src="a.js"><\/script>');
document.write('<DIV id="d1"></DIV>');
alert(document.getElementById('d1')===null);
</script>
/* a.js */
//可以不包含代码

示例脚本通过 document.write 的方式向文档输入其他的 SCRIPT 标记引入其他 JS 文件以及其他内容。立即写入一个 DIV 标记后通过 getElementById 方法得到该元素的 DOM 引用。
各浏览器表现如下:

IE6 IE7 IE8 Opera Firefox Chrome Safari
false true

对于以上测试代码,IE Opera 表现为在执行到语句 "document.write('<DIV id="d1"></DIV>');" 后立即更新了文档的 DOM 结构(向文档内插入了 ID 为 d1 的 DIV 标记)。

而在其他浏览器里,在当前 SCRIPT 标记的执行流运行完成后,才更新文档的 DOM 结构。

此外,还存在另外问题,分析以下代码 C :

<script>
document.write('<script id="d1" src="a.js"><\/script>');
document.write('<script id="d2"></scirpt>');
alert([document.getElementById('d1'),document.getElementById('d2'));
</script>
/* a.js */
//可以不包含代码

各浏览器表现如下:

IE6 IE7 IE8 Opera Firefox Chrome Safari
[Element, Element] [Element, null]

本例中,Firefox Safari Chrome 浏览器下,id="d2" 的 Script 元素将输出 null。 除非 d1 不是加载外部资源,d2 元素才可能会立即被获取到;或者同上例一般,在当前脚本块执行流完后才可以访问到 d2、这个问题原因不明,Firefox Safari Chrome 这些浏览器可能因为脚本元素 d1 的外部资源加载而导致 d2 元素没有被及时放入 DOM Tree。

解决方案

如果外部引用的 JS 文件内程序,要求在执行顺序上一致,请避免使用 document.write 语句引入的 JS 程序文件中再次使用他来加载外部 JS 文件。

可以参考 动态引入的外部 JS 文件在各浏览器中的执行顺序不一致 文章内解决方案。

参见

知识库

相关问题

测试环境

操作系统版本: Windows 7 Ultimate build 7600
浏览器版本: IE6
IE7
IE8
Firefox 3.6
Chrome 4.0.302.3 dev
Safari 4.0.4
测试页面: document_write_js_file.html
dom_tree_update.html
本文更新时间: 2010-08-06

关键字

documen.wirte SCRIPT 延迟执行 DOM 树更新