初春的晚上,閒來無事,聊聊 document.write 方法。javascript
document.write 使用方式很是簡單,把 "字符串化"(很差意思,這多是我本身創造的名詞)的 html 代碼當作參數傳入就 ok 了,我並不打算講它的基本用法,能夠參考如下連接:html
document.write 常常會被用來加載腳本,好比這樣:java
var url = 'http://ads.com/buyme?rand='+Math.random() document.write('<script src="'+url+'"></scr'+'ipt>')
傳統方式:jquery
var script = document.createElement('script') script.src = 'http://ads.com/buyme?rand='+Math.random() // now append the script into HEAD, it will fetched and executed document.documentElement.firstChild.appendChild(script)
對比 dom 插入的傳統方法,的確能少幾行代碼。這樣作還有個好處,它比 dom 插入的方式快,由於它是在一個輸出流中,因此不用修改 dom 結構(It is very fast, because the browser doesn’t have to modify an existing DOM structure)。可是,若是這段腳本沒有執行完,後續渲染都將掛起!ajax
document.write 加載腳本也不是沒有合適的場景,好比說後續的渲染都要依賴這段腳本,那麼這樣寫就徹底沒有問題。好比這段代碼:chrome
<script src="//ajax.googleapis.com/ajax/libs/jquery/1.6.3/jquery.min.js"></script> <script>window.jQuery || document.write('<script src="js/libs/jquery-1.6.3.min.js"><\/script>')</script>
或者:shell
<script>window.JSON || document.write('<script src="json2.js"><\/script>')</script>
很是的優雅。json
還有個應用場景,加載第三方廣告,百度聯盟的廣告就是用該方法輸出的。咱們假設百度聯盟廣告以下(另存爲 cm.js):api
document.write("<img src='ad.jpg /'>");
那麼咱們在頁面任意部分同步加載這段代碼,就能顯現百度廣告,事實上,體驗是很是差的,由於是同步渲染,若是這段代碼沒有執行完,後續是不會執行下去的(UI 掛起)。嘗試着將內含 document.write 的腳本文件異步執行,寫個簡單的 demo。瀏覽器
index.htm 文件:
<body> Hello <script> var s = document.createElement("script"); s.src = "data.js"; document.body.appendChild(s); </script> </body>
data.js 文件:
document.write('World');
頁面只顯示了 Hello 字樣,控制檯打印 notice 以下(詳見 stackoverflow):
按照 notice 的提示將 document.open() 加入 data.js 文件,這時頁面就只有 World 了。我去,異步加載個 js,替換這個頁面,這樣的操做應該幾乎沒有吧!因此,看起來百度的廣告只能同步加載了,若是延遲加載(用個 setTimeout 方法)用到 document.write 的文件,那麼理論上會覆蓋整個頁面吧,這是咱們不但願看到的,也是咱們要謹慎使用該方法的緣由( Why is document.write considered a 「bad practice」?)。
用 document.write 加載腳本文件,甚至還涉及到瀏覽器的兼容性,不一樣的瀏覽器會用不一樣的順序加載,這點不展開了,有興趣的能夠參考以下連接:
最後總結下吧,若是用 document.write 來渲染頁面,能夠適當適時的使用,若是是加載腳本,儘可能別用了,畢竟 stevesouders 建議別用(Don’t docwrite scripts),主要仍是爲了避免影響後續的加載。
附之前寫的草稿:
document.write 是 document 下的一個方法,不少入門書籍中常常見到該方法,實際生產中卻不多用到。
document.write() 接收一個字符串做爲參數,將該字符串寫入文檔流中。一旦文檔流已經關閉(document.close()),那麼 document.write 就會從新利用 document.open() 打開新的文檔流並寫入,此時原來的文檔流會被清空,已渲染好的頁面就會被清除,瀏覽器將從新構建 DOM 並渲染新的頁面。
向文檔流中寫入 HTML 字符串:
<div> <script> document.write("<script src='cm.js'><\/script>"); document.write("<div class='add'></div>") </script> </div>
由於 document.write 方法做用時,文檔流還沒關閉,因此並不用先 document.open()。渲染完後頁面 dom 結構( chrome下 需考慮瀏覽器兼容性):
<div> <script> document.write("<script src='cm.js'><\/script>"); document.write("<div class='add'></div>") </script> <script src="cm.js"></script> <div class="add"></div> </div>
這裏還須要 注意一點,當 document.write 的字符串參數包含 script 標籤時,注意要轉義,或者將 </script>
割開(split),好比 document.write("<script src='cm.js'></" + "script>");
,這是由於一旦遇到 </script>
,會自動與包裹該段代碼的 <script>
進行配對。詳見 這裏。
再看個例子:
<div> <p>hello world</p> </div> <script> setTimeout(function() { document.write('<a href="http://www.cnblogs.com/zichi/">zichi\'s blog</a>'); }, 0); </script>
由於當 setTimeout 的回調執行時,文檔流已經關閉(頁面已經解析完),因此首先自動調用 document.open() 函數打開文檔流,而後將文檔流清空,渲染新的東西,即字符串中的 a 標籤。
既然不加 document.open() 也會自動開啓文檔流,那麼 document.open() 以及 document.close() 是否沒用武之地了呢?思考以下代碼。
代碼一:
<div> <p>hello world</p> </div> <script> setTimeout(function() { document.write('a'); document.write('b'); }, 0); </script>
代碼二:
<div> <p>hello world</p> </div> <script> setTimeout(function() { document.open(); document.write('a'); document.close(); document.open(); document.write('b'); document.close(); }, 0); </script>
前者頁面顯示 "ab",然後者顯示 "b"。能夠想象前者兩個 document.write 在一個文檔流中輸出,然後者手動關閉文檔流,因此至關於重寫了兩次。
繼續看:
<div> <p>hello world</p> </div> <script> document.open(); document.write('a'); document.close(); document.open(); document.write('b'); document.close(); </script>
頁面上 "hello world" 和 "ab" 都在。能夠想象,當頁面初次載入,瀏覽器還沒解析完時,就算手動關閉文檔流,也是關不掉的。