這是一個非常隱秘也是非常強大的bug,我真不知微軟的IE開發人員是怎麼搞出來的。此bug的觸發條件是,當頁面存在一個自閉合的base節點,它就會把其“下面”所有的元素都拷到它內部。這裡的下面非childNodes,children所能描述,它連body節點都能編入其中。
<!doctype html><html> <head> <meta charset="utf-8"/> <meta content="IE=8" http-equiv="X-UA-Compatible"/> <meta name="keywords" content="IE6 base bug" /> <base href='http://www.cnblogs.com/rubylouvre/' /> <script type="text/javascript" id="test"> //http://blog.sina.com.cn/situdesign window.onload = function(){ var meta = document.createElement("meta"); meta.name = "description"; meta.content = "IE6 base bug by 司徒正美" var head = document.documentElement.firstChild; head.appendChild(meta); alert("meta.parentNode : "+meta.parentNode.tagName); var base = document.getElementsByTagName("base")[0]; alert("base.childNodes.length : "+base.childNodes.length) alert("body.parentNode : "+document.body.parentNode.tagName) var script = document.getElementById("test"); alert("script.parentNode : "+script.parentNode.tagName) } </script> <title>IE 複製節點 bug</title> </head> <body> <h1>請在IE6下運行</h1> <script type='text/javascript' src='http://getfirebug.com/releases/lite/1.2/firebug-lite-compressed.js'></script> </body></html>
<br /><!doctype html><br /><html><br /> <head><br /> <meta charset="utf-8"/><br /> <meta content="IE=8" http-equiv="X-UA-Compatible"/><br /> <meta name="keywords" content="IE6 base bug" /><br /> <base href='http://www.cnblogs.com/rubylouvre/' /><br /> <script type="text/javascript" id="test"></p><p> window.onload = function(){<br /> var meta = document.createElement("meta");<br /> meta.name = "description";<br /> meta.content = "IE6 base bug by 司徒正美"<br /> var head = document.documentElement.firstChild;<br /> head.appendChild(meta);<br /> alert("meta.parentNode : "+meta.parentNode.tagName);<br /> var base = document.getElementsByTagName("base")[0];<br /> alert("base.childNodes.length : "+base.childNodes.length)<br /> alert("body.parentNode : "+document.body.parentNode.tagName)<br /> var script = document.getElementById("test");<br /> alert("script.parentNode : "+script.parentNode.tagName)<br /> }<br /> </script><br /> <title>IE 複製節點 bug</title><br /> </head><br /> <body><br /> <h1>請在IE6下運行</h1><br /> <script type='text/javascript'<br /> src='http://getfirebug.com/releases/lite/1.2/firebug-lite-compressed.js'></script><br /> </body></p><p></html><br />
運行代碼
運行代碼下,我們發現完全亂套了,原本父節點為head的meta元素(動態產生)與父節點為html的body元素(原來就存在),它們的父節點都變成base節點。下面是用firebug lite截到的圖:
jQuery在使用globalEval方法動態解析指令碼時,就遇到這個問題,它取巧地把動態產生的script節點插入到base節點之前。
globalEval: function( data ) { if ( data && rnotwhite.test(data) ) { var head = document.getElementsByTagName("head")[0] || document.documentElement, script = document.createElement("script"); script.type = "text/javascript"; if ( jQuery.support.scriptEval ) { script.appendChild( document.createTextNode( data ) ); } else { script.text = data; } head.insertBefore( script, head.firstChild ); head.removeChild( script ); } },
但還是不妥當,因為始終留著個陷阱給人踩。想了想,base標籤無礙乎只有兩個屬性,用於對頁面上所有的URL進行統一設定,如img.src,form.action,a.href等等。通常只有一個就夠了,如果它只存在於body中,不管,如果存在於head就會為害人間了,我們需要將它強制添加一個閉合標籤。即,將它由
<base href='http://www.cnblogs.com/rubylouvre/' />
轉換為
<base href='http://www.cnblogs.com/rubylouvre/' ></base>
雖然IE6解析base的半閉合形式出錯,但它不會不濟到解析一個全新的節點出錯吧,它理應會把新節點轉換為它不會出錯的形式,換言之,是第二種形式。建立新節點的方法有許多種,但我們很需要原節點的屬性,恰好IE的cloneNode什麼也能複製。之後就是插入節點的問題了,我們使用它的一個私人實現replaceNode,不用知曉其父節點,它也將用於區分IE與非IE。
下面是我的fixbug程式:
//by 司徒正美//此bug在IE7中修複,詳見://http://blogs.msdn.com/ie/archive/2005/08/29/457667.aspx if(document.replaceNode && !window.XMLHttpRequest){ var head = document.getElementsByTagName("head")[0], base = head.getElementsByTagName("base")[0], headFrag = document.createDocumentFragment(), bodyFrag = headFrag.cloneNode(),el; if(base && base.childNodes.length){ for (; el =base.childNodes[0];) { if(el.tagName === "BODY"){ bodyFrag.appendChild(el); break; }else{ headFrag.appendChild(el) } } base.replaceNode(base.cloneNode()) head.appendChild(headFrag); document.documentElement.appendChild(bodyFrag) } }
<br /><!doctype html><br /><html><br /> <head><br /> <meta charset="utf-8"/><br /> <meta content="IE=8" http-equiv="X-UA-Compatible"/><br /> <meta name="keywords" content="IE6 base bug" /><br /> <base href='http://www.cnblogs.com/rubylouvre/' /><br /> <script type="text/javascript" id="test"></p><p> window.onload = function(){<br /> if(document.replaceNode && !window.XMLHttpRequest){<br /> var head = document.getElementsByTagName("head")[0],<br /> base = head.getElementsByTagName("base")[0],<br /> headFrag = document.createDocumentFragment(),<br /> bodyFrag = headFrag.cloneNode(),el;<br /> if(base && base.childNodes.length){<br /> for (; el =base.childNodes[0];) {<br /> if(el.tagName === "BODY"){<br /> bodyFrag.appendChild(el); break;<br /> }else{<br /> headFrag.appendChild(el)<br /> }<br /> }<br /> base.replaceNode(base.cloneNode())<br /> head.appendChild(headFrag);<br /> document.documentElement.appendChild(bodyFrag)<br /> }<br /> }<br /> // =================測試代碼==========<br /> var meta = document.createElement("meta");<br /> meta.name = "description";<br /> meta.content = "IE6 base bug by 司徒正美"<br /> var head = document.documentElement.firstChild;<br /> head.appendChild(meta);<br /> alert("meta.parentNode : "+meta.parentNode.tagName);<br /> var base = document.getElementsByTagName("base")[0];<br /> alert("base.childNodes.length : "+base.childNodes.length)<br /> alert("body.parentNode : "+document.body.parentNode.tagName)<br /> var script = document.getElementById("test");<br /> alert("script.parentNode : "+script.parentNode.tagName)<br /> }<br /> </script><br /> <title>IE 複製節點 bug</title><br /> </head><br /> <body><br /> <h1>請在IE6下運行</h1><br /> <script type='text/javascript'<br /> src='http://getfirebug.com/releases/lite/1.2/firebug-lite-compressed.js'></script><br /> </body></p><p></html><br />
運行代碼
搞定!不過,我的globalEval函數用不著這個,直接用execScript一了百了,連script標籤也不用插入。