閒扯 『 document.write 』

初春的晚上,閒來無事,聊聊 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" 都在。能夠想象,當頁面初次載入,瀏覽器還沒解析完時,就算手動關閉文檔流,也是關不掉的。

相關文章
相關標籤/搜索