閑扯 『 document.write 』


初春的晚上,閑來無事,聊聊 document.write 方法。

document.write 使用方式非常簡單,把 "字符串化"(不好意思,這可能是我自己創造的名詞)的 html 代碼當做參數傳入就 ok 了,我並不打算講它的基本用法,可以參考以下鏈接:

document.write 經常會被用來加載腳本,比如這樣:

var url = 'http://ads.com/buyme?rand='+Math.random()
document.write('<script src="'+url+'"></scr'+'ipt>')

傳統方式:

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)。但是,如果這段腳本沒有執行完,后續渲染都將掛起!

document.write 加載腳本也不是沒有合適的場景,比如說后續的渲染都要依賴這段腳本,那么這樣寫就完全沒有問題。比如這段代碼:

<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>

或者:

<script>window.JSON || document.write('<script src="json2.js"><\/script>')</script>

非常的優雅。

還有個應用場景,加載第三方廣告,百度聯盟的廣告就是用該方法輸出的。我們假設百度聯盟廣告如下(另存為 cm.js):

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" 都在。可以想象,當頁面初次載入,瀏覽器還沒解析完時,就算手動關閉文檔流,也是關不掉的。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM