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