iT邦幫忙

9

陪榜來著-document.write怎麼跑?

最近比較沒空寫像樣的文章,不過還是來陪陪榜吧。

前一陣子在回文時,建議不要使用document.write(),但是沒有仔細去看看到底使用document.write會發生什麼事情,總覺得不太好。所以還是花一點時間測試看看,也檢視一下相關文件,看看今日的document.write()到底是怎樣執行的。
document.write()到底怎樣運作,其實跟瀏覽器怎麼做剖析,建立DOM的過程息息相關。本來這部份是各個瀏覽器自己要實作的,不過HTML5出來以後,有制定標準的方法:8.2 Parsing HTML documents,這裡面有一張圖,可以說明這個過程:

簡單地說,在剖析過程中,如果碰到script節點,執行時可能會產生新的文件內容,這時候就會剖析動態產生的文件內容,然後把它加入到DOM中。

例如:

<script>
var str = "<div>ok i'm a text node within div.</div>";
genode(str);
function genode(str) {
	document.write(str);
}
</script>

執行完畢,就會在script node之後加入一個div:

這樣看起來還算順利,具體的說,用document.write產生的nodes,就會直接加在執行他的script node之後,也就是script node的nextSibling。另外,在執行script的時候,在他之前的node都已經加入到DOM,所以也可以透過script來存取:

<div id="panel"></div>
<script>
var str = "我踏月色而來";
genode(str, 'panel');
function genode(str, id) {
	document.getElementById(id).innerHTML = str;
}
</script>

執行結果:

用起來很簡單吧。但是如果要使用document.write產生script,就會碰到奇怪的問題XD...例如:

<script>
var str = "<script type='text/javascript'>function foo(){alert('im changed XD')};</script>";
gencode(str);
function gencode(str) {
	document.write(str);
}
function foo() {
	alert('im a foo.');
}
</script>


<script>
foo();
</script>

執行起來卻會變成:

如果不加上script tag,就不會有script node加到DOM,也不會執行,但是加入script tag後,瀏覽器剖析的結果卻會是這樣(最新版的Chrome, Firefox, IE都一樣),不過其實有簡單的解法,就是在script tag內加上html區塊註解:

<script>
//<!--
var str = "<script type='text/javascript'>function foo(){alert('im changed XD')};</script>";
gencode(str);
function gencode(str) {
	document.write(str);
}
function foo() {
	alert('im a foo.');
}
//-->
</script>

這樣程式就可以跑了,當然還是在另外一個script node。

簡單的結論:

* document.write()無法在script node中嵌套script node
* document.write()所有執行的結果都會插入目前執行的script node之後
* 可以用html comment來避免javascript字串中有</script>被瀏覽器剖析的問題


0
ted99tw
iT邦高手 1 級 ‧ 2012-07-17 14:45:21

筆記筆記

總裁 iT邦好手 1 級 ‧ 2012-07-17 15:34:37 檢舉

泰大,您的5000...逃跑

ted99tw iT邦高手 1 級 ‧ 2012-07-17 15:39:57 檢舉

cdfu提到:
泰大,您的5000...

唱歌再來啊,費大!! 不要說5000,再多都值!!唱歌

0
jyyihch
iT邦新手 5 級 ‧ 2012-07-17 15:32:52

fillano提到:
可以用html comment來避免javascript字串中有</script>被瀏覽器剖析的問題

很實用謝謝

0
fillano
iT邦超人 1 級 ‧ 2012-07-17 15:43:49

找了一下HTML 4.01,18.2.4 Dynamic modification of documents 裡面提到的行為,跟HTML5定義的已經不一樣了。就HTML 4.01的定義,呼叫document.write會用產生的文件內容取代script元素...所以一執行很多東西就不確定會怎樣XD

另外,使用html comment來避免瀏覽器剖析javascript程式中的東西,是HTML4.01就提到的:18.3.2 Hiding script data from user agents

0
tony1223
iT邦新手 2 級 ‧ 2012-07-17 16:41:09

我自己不建議用 document.write 的理由倒不是這個。

document.write 最可怕的是在 page ready 之後去用,他會把整個 body 裡面的東西改寫,
Ready 之前我倒是覺得還好,以前也常用,沒有太大問題。

現在的問題是在現在 ajax 當道的環境裡面,萬一不小心跑到 document.write 就完蛋了。

像這個case 如果有document.write
http://jsfiddle.net/Nk3N9/1/
沒有的時候
http://jsfiddle.net/Nk3N9/2/

千萬不能在畫面已經 render 完成之後去 call document.write ,
除非你真的是想要重新寫一個完整的頁面。

至於 "<script" 的問題,可以用 "<"+"script" 來躲就沒問了。

fillano iT邦超人 1 級 ‧ 2012-07-17 17:53:05 檢舉

感謝補充,document.write是個大殺手阿XD

0
fillano
iT邦超人 1 級 ‧ 2012-07-17 18:01:33

試跑了一下:

&lt;pre class="c" name="code">
&lt;script>
function gencode() {
    document.write("&lt;script>alert('oh no.')&lt;"+"/script>all crashed.");
}
&lt;/script>

&lt;div>I lived here.&lt;/div>
&lt;input type="button" value="test" onclick="gencode()">

執行:

點一下按鈕:

結果就全毀:

這樣就更清楚document.write()的妙用了

0
kos101
iT邦新手 5 級 ‧ 2012-07-19 02:08:41

我是用此方式就可以跑了

&lt;pre class="c" name="code">
&lt;script language="javascript">
var str = '&lt;script type="text/javascript">window.alert("test");&lt;\/script>';
document.write(str);
&lt;/script>
fillano iT邦超人 1 級 ‧ 2012-07-19 09:22:21 檢舉

這個方法不錯,感謝。

我要留言

立即登入留言