在調用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>')
}, );
</script>
</body>
通過上面的例子可以看出,隻要浏覽器解析到文檔末尾,則浏覽器主線程結束,文檔流關閉。
需要特别指出的,文檔是嚴格按照從下直下的順序執行的,即使某一個js由于網絡原因延遲了較長時間,那麼文檔也會一直等待它的執行,直到逾時或等待的JS執行完成,進而将浏覽器主線程的執行周期拉長。
模拟JS延時的代碼如下(jsp實作):
<%@ page language="java" contentType="text/javascript; charset=UTF-8" pageEncoding="UTF-8"%>
<%
Thread.sleep();
%>
// 業務處理代碼
alert('defered script!');
順帶說一個document.write産生的安全現象,如果在document.write寫入的内容請求了其他的JS檔案,會提示跨站腳本攻擊:
A Parser-blocking, cross site (i.e. different eTLD+) 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>')
}, )
更有意思的是,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的寫入效果跟文檔流所處的狀态嚴格相關,如果文檔流已關閉,則必然會出現清除文檔内容的現狀。