DOM bug in Opera when using an unknown HTML element
前幾天我在寫用 JavaScript 自動轉換 Tag 註記的工具時,發現 Opera 瀏覽器在操作 Unknown HTML 元素 (即自定元素) 的 DOM 方法中有 bug ,不能正確取代或插入其他節點在 Unknown HTML 元素之節點後。如果用取代方法 (replaceChild
) 甚至會切斷 DOM 結構。
我設置了一個最簡單的測試環境,使用三種瀏覽器: MSIE 6, Firefox 1.5, Opera 9.1 (build 8679) 測試操作取代及插入動作。 MSIE 和 Firefox 都如預期般正確操作,唯 Opera 有錯誤。
案例一、使用一般 HTML 元素
首先,我想先確認使用一般 HTML 元素時, Opera 會不會有問題。因此,我在 HTML 文件中放了四個節點元素, 3 個 paragraph element (<p>) , 1 個 break element (<br>) 。
<html>
<body>
<p>First</p>
<p>Second</p>
<br/>
<p>Third</p>
</body>
</html>
我以 break element 為目標,分別嘗試三個節點操作動作:一、節點取代動作;二、在節點之後插新節點;三、將新節點插入在節點之前。
節點取代動作
建立一個 image element (<img>) ,以其取代 break element 節點。 將下面的 JavaScript 程式碼複製到 HTML 文件中,請放在 <body> 區塊內。
<script type="text/javascript">
window.onload = function() {
var d = document.getElementsByTagName('br')[0];
var s = document.createElement('img');
s.src = 'http://www.google.com/intl/zh-TW_ALL/images/logo.gif';
window.alert('before add img node');
d.parentNode.replaceChild(s, d);
window.alert('after add img node');
}
</script>
在節點之後插新節點
建立一個 image element (<img>) ,將其插入在 break element 節點之下。 將下面的 JavaScript 程式碼複製到 HTML 文件中,請放在 <body> 區塊內。
<script type="text/javascript">
window.onload = function() {
var d = document.getElementsByTagName('br')[0];
var s = document.createElement('img');
s.src = 'http://www.google.com/intl/zh-TW_ALL/images/logo.gif';
window.alert('before add img node');
d.parentNode.insertBefore(s, d.nextSibling);
window.alert('after add img node');
}
</script>
將新節點插入在節點之前
建立一個 image element (<img>) ,將其插入在 break element 節點之前。 將下面的 JavaScript 程式碼複製到 HTML 文件中,請放在 <body> 區塊內。
<script type="text/javascript">
window.onload = function() {
var d = document.getElementsByTagName('br')[0];
var s = document.createElement('img');
s.src = 'http://www.google.com/intl/zh-TW_ALL/images/logo.gif';
window.alert('before add img node');
d.parentNode.insertBefore(s, d);
window.alert('after add img node');
}
</script>
三種瀏覽器皆正確完成以上操作動作。
案例二、使用自定元素
確定一般 HTML 元素不會有問題後,接著要測試自定元素的情形,這是本文的主題。承前面的例子,只是我把 break element (<br>) 換成了自定元素 (<tags>) ,其他動作皆相同。
<html>
<body>
<p>First</p>
<p>Second</p>
<tags/>
<p>Third</p>
</body>
</html>
即使面對一個自定元素 (Unknown element) , MSIE 和 Firefox 仍然正確地完成操作動作, Opera 卻出現錯誤。
<script type="text/javascript">
window.onload = function() {
var d = document.getElementsByTagName('br')[0];
var s = document.createElement('img');
s.src = 'http://www.google.com/intl/zh-TW_ALL/images/logo.gif';
window.alert('before add img node');
d.parentNode.replaceChild(s, d);
/*
Opera will cut DOM tree after img node.
*/
window.alert('after add img node');
}
</script>
Opera 在完成取代動作後,卻砍斷了 DOM 結構,原來自定元素之節點後的樹枝也被移除了。
<script type="text/javascript">
window.onload = function() {
var d = document.getElementsByTagName('br')[0];
var s = document.createElement('img');
s.src = 'http://www.google.com/intl/zh-TW_ALL/images/logo.gif';
window.alert('before add img node');
window.alert(d.nextSibling);
/*
d.nextSibling should be a HTML paragraphElement, '<p>Third</p>'
However, Opera says that d.nextSibling is null.
*/
//window.alert(d.nextSibling.firstChild);
/*
Although Opera says d.nextSibling is null,
Opera will still be blocked
when I try to get the property of d.nextSibling.
*/
d.parentNode.insertBefore(s, d.nextSibling);
/*
Opera will cut add img node behind '<p>third</p>'.
*/
window.alert('after add img node');
}
</script>
插入在節點之後的動作,卻是把新節點插入在最後面位置,而不是自定元素後的節點 (<p>Thrid</p>) 之前。透過觀察 d.nextSibling 和 d.nextSibling.firstChild 兩個體之內容,也發現 Opera 無法正確處理自定元素的屬性。
Opera 唯有插入在節點之前的動作正確。
在 Ajax 技法中,上述節點操作動作的使用機會很高。而從本文的測試結果中顯示,如果你使用了自定元素,而 Opera 的使用者反應他們無法使用你的網站時,多半就是碰到本文的狀況了。