標(biāo)準(zhǔn)參考
Node(節(jié)點)不僅包括元素節(jié)點,也包含文本節(jié)點、注釋節(jié)點、屬性節(jié)點等等,節(jié)點的類型可以使用 nodeType 來區(qū)分。
在 HTML 源代碼中,位于標(biāo)簽之內(nèi)以及標(biāo)簽之間的文本(包括空白字符)將被創(chuàng)建為文本節(jié)點。
關(guān)于 Node 的更多信息,請參考 DOM-1 Core Interface Node 及 DOM-2 Core Interface Node 中的內(nèi)容。
關(guān)于 Text 的更多信息,請參考 DOM-2 Core Interface Text 中的內(nèi)容。
問題描述
IE 在創(chuàng)建 DOM 樹時,會忽略某些空白字符,因此會比其他瀏覽器少創(chuàng)建一些文本節(jié)點。反過來說,同樣的一篇文檔,其他瀏覽器將比 IE 多創(chuàng)建一些文本節(jié)點。
造成的影響
用戶針對 IE 設(shè)計的腳本如果使用節(jié)點對象的 nodeList、firstChild、lastChild、previousSibling 或 nextSibling 方法,可能會因為此問題而無法在其他瀏覽器中達(dá)到相同的目的,如腳本執(zhí)行出錯,或?qū)﹀e誤的目標(biāo)對象進(jìn)行了操作。
受影響的瀏覽器
IE6 IE7 IE8
問題分析
分析以下代碼:
...
<!--測試元素-->
<div id="a"> <div>div</div> <span id="b">span</span> <span>span</span> </div>
<!--腳本輸出-->
<pre>
<script>
//獲取父元素。
var $a=document.getElementById("a");
//測試 childNodes。
var nodeList=$a.childNodes;
var string="";
for(var i=0;i<nodeList.length;i++)string+=nodeList[i].nodeType;
document.writeln("nodeList: "+string);
//測試 firstChild。
document.writeln("firstChild: "+$a.firstChild.nodeType);
//測試 lastChild。
document.writeln("lastChild: "+$a.lastChild.nodeType);
//獲取子元素。
var $b=document.getElementById("b");
//測試 previousSibling。
document.writeln("previousSibling: "+$b.previousSibling.nodeType);
//測試 nextSibling。
document.writeln("nextSibling: "+$b.nextSibling.nodeType);
//顯示 innerHTML。
alert("|"+$a.innerHTML+"|");
</script>
</pre>
...
注意以上代碼,外層 DIV 標(biāo)簽內(nèi)的各標(biāo)簽間有空格符??崭穹粯?biāo)記為紅色。
根據(jù)規(guī)范中的描述,腳本的預(yù)計輸出情況如下:
第一行輸出應(yīng)該是“nodeList: 3131313”,因為該元素內(nèi)的節(jié)點共有 7 個:3 個元素節(jié)點穿插在 4 個文本節(jié)點之間。
第二行輸出應(yīng)該是“firstChild: 3”,第一個節(jié)點是文本節(jié)點。
第三行輸出應(yīng)該是“l(fā)astChild: 3”,最后一個節(jié)點也是文本節(jié)點。
第四行輸出應(yīng)該是“previousSibling: 3”。本次的目標(biāo)元素(SPAN[id=b])的前一個節(jié)點是文本節(jié)點。
第五行輸出應(yīng)該是“nextSibling: 3”,原因同上。
這段代碼在不同的瀏覽器環(huán)境中的表現(xiàn):
IE其他瀏覽器
nodeList:113133131313
firstChild:13
lastChild:33
previousSibling:13
nextSibling:33
最后彈出 DIV[id=a] 元素的 innerHTML 為:
IE
|<DIV>div</DIV><SPAN id=b>span</SPAN> <SPAN>span</SPAN> |
其他瀏覽器:
| <div>div</div> <span id="b">span</span> <span>span</span> |
對原代碼中的“測試元素部分”進(jìn)行改動后(將其中第二個 SPAN 更換為 DIV 元素):
...
<!--測試元素-->
<div id="a"> <div>div</div> <span id="b">span</span> <div>div</div> </div>
...
再次測試,各瀏覽器表現(xiàn)如下:
IE6 IE7 IE8Firefox Chrome Safari Opera
nodeList:11313131313
firstChild:13
lastChild:13
previousSibling:13
nextSibling:33
最后彈出 DIV[id=a] 元素的 innerHTML 為:
IE
|<DIV>div</DIV><SPAN id=b>span</SPAN>
<DIV>div</DIV>|
其他瀏覽器:
| <div>div</div> <span id="b">span</span> <div>div</div> |
可見:IE 在生成 DOM 樹時,忽略了一些空白字符,從而比其他瀏覽器少創(chuàng)建了一些文本節(jié)點。這導(dǎo)致在使用 nodeList、firstChild、lastChild、previousSibling 或 nextSibling 方法時,在 IE 和其他瀏覽器中得到的結(jié)果不一致。
解決方案
1. 沒有必要時盡量去掉各標(biāo)簽之間的空白字符。
因為頁面腳本多是對“元素節(jié)點”進(jìn)行操作,因此只要保證各元素之間沒有文本節(jié)點(即源代碼中的標(biāo)簽之間沒有空白字符——包括空格符、換行符、制表符),就能使上述各屬性在各瀏覽器中的行為一致。如:
<div id="a"><div>div</div><span id="b">span</span><span>span</span></div>
另外,使用腳本創(chuàng)建并順次添加的元素,他們本身就是緊密相聯(lián)的,各元素之間并沒有文本節(jié)點,因此這種情況也不必?fù)?dān)心上述兼容性問題,如:
...
var $a=document.createElement("div");
...
var $b=document.createElement("div");
...
document.body.appendChild($a);
document.body.appendChild($b);
...
$a.nextSibling.className="foo";
...
上述代碼中,'$a.nextSibling' 在所有瀏覽器中都將是 $b。
2. 在獲取節(jié)點時做類型判斷。
無法保證各元素之間沒有文本節(jié)點時,則需要在針對節(jié)點的操作上添加類型判斷,如:
function getPreviousElementSibling ($target) {
var $previousNode = $target.previousSibling;
while ($previousNode && $previousNode.nodeType!=1) {
$previousNode = $previousNode.previousSibling;
}
return $previousNode;
}
另外,在非IE中,還可以使用 Element Traversal Specification 草案中提到的 previousElementSibling 和 nextElementSibling 獲取元素節(jié)點,例如:以 Element.nextElementSibling 取得與元素 Element 的相鄰的下一個元素節(jié)點。
更多信息請查看IT技術(shù)專欄