在调用document.write时,会将内容写入文档流,所以会自动调用document.open方法打开文档流,如果文档流已经关闭,则会清除所有内容。
那什么时候代表文档流已经关闭呢?即所有的内容都已经读入到浏览器中(含所有的内联CSS、HTML标签),即页面的主线程没有执行完成,也就是我们所看到的源代码里面的所有内容。
以如下的代码为例:
<body>
<h1>Hello, World!</h1>
<script type="text/javascript">
document.write('<h2>Hello, Yiifaa!</h2>')
</script>
</body>
因为此时页面的主线程没有执行完成,所以会将“
Hello, Yiifaa!
”添加到文档末尾,但如果利用setTimeout暂时放弃主线程的执行,切换到任务队列中,那么随着文档流的写入结束,此时需要重新打开文档流,那么文档的内容则会全部覆盖,如下:<body>
<h1>Hello, World!</h1>
<script type="text/javascript">
// 暂时放弃主线程的执行
setTimeout(function() {
document.write('<h2>Hello, Yiifaa!</h2>')
}, 0);
</script>
</body>
通过上面的例子可以看出,只要浏览器解析到文档末尾,则浏览器主线程结束,文档流关闭。
需要特别指出的,文档是严格按照从下直下的顺序执行的,即使某一个js由于网络原因延迟了较长时间,那么文档也会一直等待它的执行,直到超时或等待的JS执行完成,从而将浏览器主线程的执行周期拉长。
模拟JS延时的代码如下(jsp实现):
<%@ page language="java" contentType="text/javascript; charset=UTF-8" pageEncoding="UTF-8"%>
<%
Thread.sleep(10000);
%>
// 业务处理代码
alert('defered script!');
顺带说一个document.write产生的安全现象,如果在document.write写入的内容请求了其他的JS文件,会提示跨站脚本攻击:
A Parser-blocking, cross site (i.e. different eTLD+1) script,
http://localhost/mirana-birt/defered.jsp, is invoked via document.write.
The network request for this script MAY be blocked by the browser in this or a future page load due to poor network connectivity. If blocked in this page load, it will be confirmed in a subsequent console message.
示例代码如下:
document.write('<script src="http://localhost/mirana-birt/defered.jsp"><\/script>')
setTimeout(function() {
document.write('<h2>Hello, Yiifaa!</h2>')
}, 0)
更有意思的是,IE浏览器与Chrome表现出截然不同的表现,IE会直接抹掉所有内容,并会取消所有未完成的请求。
另,在文档流的写入过程,由于JS单线程的限制,document.close()并不能真正关闭流,只能起到一个代码标记的作用,如下面的代码,并不会清除文档内容,说明文档流并没有关闭:
<body>
<h1>Hello, World!</h1>
<script type="text/javascript">
document.close()
document.write('Hello, Yiifaa!')
</script>
</body>
结论
document.write的写入效果跟文档流所处的状态严格相关,如果文档流已关闭,则必然会出现清除文档内容的现状。