Netscape 最初开发 Mozilla 浏览器的时候,明智地决定支持 W3C 标准。因此,Mozilla 和 Netscape Navigator 4.x 以及 Microsoft Internet Explorer 遗留代码不完全向后兼容,比如后面将提到 Mozilla 不支持 <span style="font-family:NSimsun"></span>
。Internet Explorer 4 这些在 W3C 标准的概念出现之前建立的浏览器继承了很多古怪之处。本文中将讨论 Mozilla 的特殊模式,它为 Internet Explorer 和其他遗留浏览器提供了强大的 HTML 向后兼容功能。
我还将讨论 Mozilla 支持的非标准技术,如 XMLHttpRequest 和富文本编辑,因为当时 W3C 还没有对应的标准。其中包括:
级联样式表(CSS):CSS Level 1、CSS Level 2 以及 CSS Level 3 的部分。
文档对象模型(DOM):DOM Level 1、 DOM Level 2 和 DOM Level 3 的部分
数学标记语言:MathML Version 2.0
可扩展标记语言(XML):XML 1.0、Namespaces in XML、Associating Style Sheets with XML Documents 1.0、Fragment Identifier for XML
XSL 转换:XSLT 1.0
XML Path 语言:XPath 1.0
资源描述框架:RDF
简单对象访问协议:SOAP 1.1
ECMA-262 修订版 3(JavaScript 1.5):ECMA
通用的跨浏览器编码技巧
虽然存在 Web 标准,但不同浏览器的行为并不完全相同(实际上同一个浏览器在不同的平台上行为也不相同)。很多浏览器,如 Internet Explorer 依然支持 W3C 之前的、从未在 W3C 符合浏览器中获得广泛支持的 API。
深入讨论 Mozilla 和 Internet Explorer 的区别之前,首先介绍一下使 Web 应用程序具备可扩展性以便日后增加新浏览器支持的一些基本方法。
因为不同的浏览器有时会为同样的功能使用不同的 API,因此经常会在代码中看到很多 <span style="font-family:NSimsun">if() else()</span>
块,来区别对待不同的浏览器。下面的代码块用于 Internet Explorer:
. . . var elm; if (ns4) elm = document.layers["myID"]; else if (ie4) elm = document.all["myID"]; Salin selepas log masuk |
<br/>
上述代码不具备可扩展性,如果需要支持新的浏览器,必须修改 Web 应用程序中所有这样的代码块。
避免为新浏览器重新编码最简单的办法就是抽象功能。不要使用层层嵌套的 <span style="font-family:NSimsun">if() else()</span>
块,把通用的任务抽象成单独的函数可以提高效率。这样不但代码更易于阅读,还便于增加新客户机支持:
var elm = getElmById("myID"); function getElmById(aID){ var element = null; if (isMozilla || isIE5) ?element = document.getElementById(aID) else if (isNetscape4) element = document.layers[aID] else if (isIE4) element = document.all[aID]; return element; } Salin selepas log masuk |
<br/>
上述代码仍然存在浏览器嗅探 或者检测用户使用何种浏览器的问题。浏览器嗅探一般通过用户代理完成,比如:
Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.5) Gecko/20031016 Salin selepas log masuk |
<br/>
虽然使用用户代理来嗅探浏览器提供了所用浏览器的详细信息,但是出现新的浏览器版本时处理用户代理的代码可能出错,因而需要修改代码。
如果浏览器的类型无关紧要(假设禁止不支持的浏览器访问 Web 应用程序),最好通过浏览器本身的能力来嗅探。一般可以通过测试需要的 JavaScript 功能来完成。比如,与其使用:
if (isMozilla || isIE5) Salin selepas log masuk |
<br/>
不如用:
if (document.getElementById) Salin selepas log masuk |
<br/>
这样不用任何修改,在其他支持该方法的浏览器如 Opera 或 Safari 上也能工作。
但是如果准确性很重要,比如要验证浏览器是否满足 Web 应用程序的版本要求或者尝试避免某个 bug,则必须使用用户代理嗅探。
JavaScript 还允许使用内嵌条件语句,有助于提高代码的可读性:
var foo = (condition) ? conditionIsTrue : conditionIsFalse; Salin selepas log masuk |
<br/>
比如,要检索一个元素,可以用如下代码:
function getElement(aID){ return (document.getElementById) ? document.getElementById(aID) : document.all[aID]; } Salin selepas log masuk |
<br/><br/>
<br/> |
<br/>回页首 |
<br/><br/>
Mozilla 和 Internet Explorer 的区别
首先讨论 Mozilla 和 Internet Explorer 在 HTML 行为方式上的区别。
工具提示
遗留浏览器在 HTML 中引入了工具提示,在链接上显示 <span style="font-family:NSimsun">alt</span>
属性作为工具提示的内容。最新的 W3C HTML 规范增加了 <span style="font-family:NSimsun">title</span>
属性,用于包含链接的详细说明。现代浏览器应该使用 <span style="font-family:NSimsun">title</span>
属性显示工具提示,Mozilla 仅支持用该属性显示工具提示而不能用 <span style="font-family:NSimsun">alt</span>
属性。
实体
HTML 标记可以包含多种实体,W3 标准体 专门作了规定。可以通过数字或者字符引用来引用这些实体。比如,可以用 #160 或者等价的字符引用 <span style="font-family:NSimsun"> </span>
来引用空白字符 <span style="font-family:NSimsun"> </span>
。
一些旧式浏览器,如 Internet Explorer,有一些怪异的地方,比如允许用正常文本内容替换实体后面的分号(<span style="font-family:NSimsun">;</span>
):
  Foo    Foo Salin selepas log masuk |
<br/>
Mozilla 将把上面的 <span style="font-family:NSimsun"> </span>
呈现为空格,虽然违反了 W3C 规范。如果后面紧跟着更多字符,浏览器就不能解析 <span style="font-family:NSimsun"> </span>
,如:
 12345 Salin selepas log masuk |
<br/>
这样的代码在 Mozilla 中无效,因为违反了 W3 标准。为了避免浏览器的差异,应坚持使用正确的形式(<span style="font-family:NSimsun"> </span>
)。
<br/>
<br/> |
<br/>回页首 |
<br/><br/>
DOM 差异
文档对象模型(DOM)是包含文档元素的树状结构。可以通过 JavaScript API 来操纵它,对此 W3C 已有标准。但是在 W3C 标准化之前,Netscape 4 和 Internet Explorer 4 以类似的方式实现了这种 API。Mozilla 仅实现了 W3C 标准不支持的那些遗留 API。
访问元素
未按照跨浏览器的方式检索元素的引用,应使用 <span style="font-family:NSimsun">document.getElementById(aID)</span>
,该方法可用于 Internet Explorer 5.5+、Mozilla,是 DOM Level 1 规范的一部分。
Mozilla 不支持通过 <span style="font-family:NSimsun">document.elementName</span>
甚至按照元素名来访问元素,而 Internet Explorer 则支持这种方法(也称为全局名称空间污染)。Mozilla 也不支持 Netscape 4 的 <span style="font-family:NSimsun">document.layers</span>
方法和 Internet Explorer 的 <span style="font-family:NSimsun">document.all</span>
方法。除了 <span style="font-family:NSimsun">document.getElementById</span>
可以检索元素之外,还可用 <span style="font-family:NSimsun">document.layers</span>
和 <span style="font-family:NSimsun">document.all</span>
获得具有特定标签名称的全部文档元素列表,比如所有的 <span style="font-family:NSimsun"></span>
<br/>
元素。
W3C DOM Level 1 使用 <span style="font-family:NSimsun">getElementsByTagName()</span>
方法获得所有相同标签名的元素的引用。该方法在 JavaScript 中返回一个数组,可用于 <span style="font-family:NSimsun">document</span>
元素,也可用于其他节点只检索对应的子树。要获得 DOM 树中所有元素的列表,可使用 <span style="font-family:NSimsun">getElementsByTagName(*)</span>
。
表 1 中列出了 DOM Level 1 方法,大部分用于把元素移动到特定位置或切换其可视性(菜单、动画)。Netscape 4 使用 <span style="font-family:NSimsun"></span>
标签(Mozilla 不支持)作为可以任意定位的 HTML 元素。在 Mozilla 中,可使用 <span style="font-family:NSimsun"></span>
<br/>
标签定位元素,Internet Explorer 也用它,HTML 规范中也包含它。
表 1. 用于访问元素的方法
方法 | 说明 |
document.getElementById( aId ) | 返回具有指定 ID 的元素的引用。 |
document.getElementsByTagName( aTagName ) | 返回文档中具有指定名称的元素数组。 |
遍历 DOM
Mozilla 通过 JavaScript 支持遍历 DOM 树的 W3C DOM API(如表 2 所示)。文档中每个节点都可使用这些 API 方法,可以在任何方向上遍历树。Internet Explorer 也支持这些 API,还支持原来用于遍历 DOM 树的 API,比如 <span style="font-family:NSimsun">children</span>
属性。
表 2. 用于遍历 DOM 的方法
属性/方法 | 说明 |
childNodes | 返回元素所有子节点的数组。 |
firstChild | 返回元素的第一个子节点。 |
getAttribute( aAttributeName ) | 返回指定属性的值。 |
hasAttribute( aAttributeName ) | 返回一个 Boolean 值表明当前节点是否包含指定名称的属性。 |
hasChildNodes() | 返回一个布尔指表明当前节点是否有子节点。 |
lastChild | 返回元素的最后一个子节点。 |
nextSibling | 返回紧接于当前节点之后的节点。 |
nodeName | 用字符串返回当前节点的名称。 |
nodeType | 返回当前节点的类型。 值说明1元素节点2属性节点3文本节点4CDATA 选择节点5实体引用节点6实体节点7处理指令节点8注释节点9文档节点10文档类型节点11文档片断节点12符号节点 |
nodeValue | 返回当前节点的值。对于包含文本的节点,如文本和注释节点返回其字符串值。对于属性节点返回属性值。其他节点返回 <span style="font-family:NSimsun">null</span> 。 |
ownerDocument | 返回包含当前节点的 <span style="font-family:NSimsun">document</span> 对象。 |
parentNode | 返回当前节点的父节点。 |
previousSibling | 返回当前节点之前的相邻节点。 |
removeAttribute( aName ) | 从当前节点中删除指定的属性。 |
setAttribute( aName, aValue ) | 设置指定属性的值。 |
Internet Explorer 有一种非标准的特殊行为,这些 API 很多跳过(比如)新行字符生成的空白文本节点。Mozilla 则不跳过,因此有时候需要区分这些节点。每个节点都有一个 <span style="font-family:NSimsun">nodeType</span>
属性指定了节点类型。比如,元素节点类型是 1,文本节点是 3,而注释节点是 8。仅处理元素节点最好的办法是遍历所有子节点,然后处理那些 nodeType 为 1 的节点:
HTML:Test cJavaScript: var myDiv = document.getElementById("foo"); var myChildren = myXMLDoc.childNodes; for (var i = 0; i < myChildren.length; i++) { if (myChildren[i].nodeType == 1){ // element node } } Salin selepas log masuk |
<br/>
生成和操纵内容
Mozilla 支持向 DOM 动态增加内容的遗留方法,如 <span style="font-family:NSimsun">document.write</span>
、<span style="font-family:NSimsun">document.open</span>
和 <span style="font-family:NSimsun">document.close</span>
。Mozilla 也支持 Internet Explorer 的 <span style="font-family:NSimsun">InnerHTML</span>
方法,该方法基本上可以在任何节点上使用。但是不支持 <span style="font-family:NSimsun">OuterHTML</span>
(围绕着元素添加标记,标准中也没有等价的方法)和 <span style="font-family:NSimsun">innerText</span>
(设置节点的文本值,在 Mozilla 中可使用 <span style="font-family:NSimsun">textContent</span>
)。
Internet Explorer 有一些非标准的、Mozilla 不支持的内容操作方法,包括检索值、插入文本以及邻近某个节点插入元素,比如 <span style="font-family:NSimsun">getAdjacentElement</span>
和 <span style="font-family:NSimsun">insertAdjacentHTML</span>
。表 3 说明了 W3C 标准和 Mozilla 操纵内容的方法,这些方法适用于任何 DOM 节点。
表 3. Mozilla 用于操纵内容的方法
方法 | 说明 |
appendChild( aNode ) | 创建新的子节点。返回新建子节点的引用。 |
cloneNode( aDeep ) | 创建调用节点的副本并返回。如果 aDeep 为 true,则复制该节点的整个子树。 |
createElement( aTagName ) | 创建并返回一个 aTagName 指定类型的无父 DOM 节点。 |
createTextNode( aTextValue ) | 创建并返回一个新的无父 DOM 文本节点,值由 aTextValue 指定。 |
insertBefore( aNewNode, aChildNode ) | 在 aChildNode 前插入 aNewNode,前者必须是当前节点的子节点。 |
removeChild( aChildNode ) | 删除 aChildNode 并返回对它的引用。 |
replaceChild( aNewNode, aChildNode ) | 用 aNewNode 替换 aChildNode 并返回被删除节点的引用。 |
文档片断
出于性能方面的原因,可以在内存中创建文档而不是处理已有文档的 DOM。DOM Level 1 Core 引入了文档片断,这是一种轻型文档包含一般文档接口的一个子集。比如没有 <span style="font-family:NSimsun">getElementById</span>
但是有 <span style="font-family:NSimsun">appendChild</span>
。很容易向已有文档添加文档片断。
Mozilla 使用 <span style="font-family:NSimsun">document.createDocumentFragment()</span>
创建文档片断,该方法返回一个空的文档片断。
但是,Internet Explorer 的文档片断实现没有遵循 W3C 标准,仅仅返回一般的文档。
<br/>
<br/> |
<br/>回页首 |
<br/><br/>
JavaScript 差异
Mozilla 和 Internet Explorer 的多数差异都和 JavaScript 有关。但问题通常源自浏览器向 JavaScript 公开的 API,比如 DOM 钩子。两种浏览器在核心 JavaScript 上区别不大,遇到的问题通常和时间有关。
JavaScript date 差异
<span style="font-family:NSimsun">Date</span>
惟一的区别是 <span style="font-family:NSimsun">getYear</span>
方法。根据 ECMAScript 规范(这是 JavaScript 所遵循的规范),该方法没有解决千年问题,在 2004 年运行 <span style="font-family:NSimsun">new Date().getYear()</span>
将返回“104”。根据 ECMAScript 规范,<span style="font-family:NSimsun">getYear</span>
返回的年份减去 1900 最初是为了在 1998 年返回“98”。ECMAScript Version 3 废止了 <span style="font-family:NSimsun">getYear</span>
,用 <span style="font-family:NSimsun">getFullYear()</span>
代替。Internet Explorer 修改了 <span style="font-family:NSimsun">getYear()</span>
使其和 <span style="font-family:NSimsun">getFullYear()</span>
类似,消除了千年问题,而 Mozilla 坚持采用标准的行为方式。
JavaScript 执行差异
不同的浏览器执行 JavaScript 的方式是不同的。比如,下列代码假设 <span style="font-family:NSimsun">script</span>
块执行的时候 <span style="font-family:NSimsun">p</span>
节点已经存在于 DOM 中:
...Loading... Salin selepas log masuk |