IE6的base标签导致页面结构大混乱
这是一个非常隐秘也是非常强大的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 > |
运行代码下,我们发现完全乱套了,原本父节点为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) } } |
搞定!不过,我的globalEval函数用不着这个,直接用execScript一了百了,连script标签也不用插入。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· .NET Core 中如何实现缓存的预热?
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
· AI与.NET技术实操系列:向量存储与相似性搜索在 .NET 中的实现
· 基于Microsoft.Extensions.AI核心库实现RAG应用
· Linux系列:如何用heaptrack跟踪.NET程序的非托管内存泄露
· TypeScript + Deepseek 打造卜卦网站:技术与玄学的结合
· 阿里巴巴 QwQ-32B真的超越了 DeepSeek R-1吗?
· 【译】Visual Studio 中新的强大生产力特性
· 【设计模式】告别冗长if-else语句:使用策略模式优化代码结构
· 10年+ .NET Coder 心语 ── 封装的思维:从隐藏、稳定开始理解其本质意义