最近更新: 2007-02-06

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.nextSiblingd.nextSibling.firstChild 兩個體之內容,也發現 Opera 無法正確處理自定元素的屬性。

Opera 唯有插入在節點之前的動作正確。

在 Ajax 技法中,上述節點操作動作的使用機會很高。而從本文的測試結果中顯示,如果你使用了自定元素,而 Opera 的使用者反應他們無法使用你的網站時,多半就是碰到本文的狀況了。

樂多舊網址: http://blog.roodo.com/rocksaying/archives/2704403.html