Netscape 最初开发 Mozilla 浏览器的时候,明智地决定支持 W3C 标准。因此,Mozilla 和 Netscape Navigator 4.x 以及 Microsoft Internet Explorer 遗留代码不完全向后兼容,比如后面将提到 Mozilla 不支持 <layer>
。Internet Explorer 4 这些在 W3C 标准的概念出现之前建立的浏览器继承了很多古怪之处。本文中将讨论 Mozilla 的特殊模式,它为 Internet Explorer 和其他遗留浏览器提供了强大的 HTML 向后兼容功能。
我还将讨论 Mozilla 支持的非标准技术,如 XMLHttpRequest 和富文本编辑,因为当时 W3C 还没有对应的标准。其中包括:
- HTML 4.01 和 XHTML 1.0/1.1
- 级联样式表(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,因此经常会在代码中看到很多 if() else()
块,来区别对待不同的浏览器。下面的代码块用于 Internet Explorer:
. . . var elm; if (ns4) elm = document.layers["myID"]; else if (ie4) elm = document.all["myID"];
上述代码不具备可扩展性,如果需要支持新的浏览器,必须修改 Web 应用程序中所有这样的代码块。
避免为新浏览器重新编码最简单的办法就是抽象功能。不要使用层层嵌套的 if() else()
块,把通用的任务抽象成单独的函数可以提高效率。这样不但代码更易于阅读,还便于增加新客户机支持:
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; }
上述代码仍然存在浏览器嗅探 或者检测用户使用何种浏览器的问题。浏览器嗅探一般通过用户代理完成,比如:
Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.5) Gecko/20031016
虽然使用用户代理来嗅探浏览器提供了所用浏览器的详细信息,但是出现新的浏览器版本时处理用户代理的代码可能出错,因而需要修改代码。
如果浏览器的类型无关紧要(假设禁止不支持的浏览器访问 Web 应用程序),最好通过浏览器本身的能力来嗅探。一般可以通过测试需要的 JavaScript 功能来完成。比如,与其使用:
if (isMozilla || isIE5)
不如用:
if (document.getElementById)
这样不用任何修改,在其他支持该方法的浏览器如 Opera 或 Safari 上也能工作。
但是如果准确性很重要,比如要验证浏览器是否满足 Web 应用程序的版本要求或者尝试避免某个 bug,则必须使用用户代理嗅探。
JavaScript 还允许使用内嵌条件语句,有助于提高代码的可读性:
var foo = (condition) ? conditionIsTrue : conditionIsFalse;
比如,要检索一个元素,可以用如下代码:
function getElement(aID){ return (document.getElementById) ? document.getElementById(aID) : document.all[aID]; }
回页首
Mozilla 和 Internet Explorer 的区别
首先讨论 Mozilla 和 Internet Explorer 在 HTML 行为方式上的区别。
工具提示
遗留浏览器在 HTML 中引入了工具提示,在链接上显示 alt
属性作为工具提示的内容。最新的 W3C HTML 规范增加了 title
属性,用于包含链接的详细说明。现代浏览器应该使用 title
属性显示工具提示,Mozilla 仅支持用该属性显示工具提示而不能用 alt
属性。
实体
HTML 标记可以包含多种实体,W3 标准体 专门作了规定。可以通过数字或者字符引用来引用这些实体。比如,可以用 #160 或者等价的字符引用
来引用空白字符
。
一些旧式浏览器,如 Internet Explorer,有一些怪异的地方,比如允许用正常文本内容替换实体后面的分号(;
):
  Foo    Foo
Mozilla 将把上面的  
呈现为空格,虽然违反了 W3C 规范。如果后面紧跟着更多字符,浏览器就不能解析  
,如:
 12345
这样的代码在 Mozilla 中无效,因为违反了 W3 标准。为了避免浏览器的差异,应坚持使用正确的形式(
)。
回页首
DOM 差异
文档对象模型(DOM)是包含文档元素的树状结构。可以通过 JavaScript API 来操纵它,对此 W3C 已有标准。但是在 W3C 标准化之前,Netscape 4 和 Internet Explorer 4 以类似的方式实现了这种 API。Mozilla 仅实现了 W3C 标准不支持的那些遗留 API。
访问元素
未按照跨浏览器的方式检索元素的引用,应使用 document.getElementById(aID)
,该方法可用于 Internet Explorer 5.5+、Mozilla,是 DOM Level 1 规范的一部分。
Mozilla 不支持通过 document.elementName
甚至按照元素名来访问元素,而 Internet Explorer 则支持这种方法(也称为全局名称空间污染)。Mozilla 也不支持 Netscape 4 的 document.layers
方法和 Internet Explorer 的 document.all
方法。除了 document.getElementById
可以检索元素之外,还可用 document.layers
和 document.all
获得具有特定标签名称的全部文档元素列表,比如所有的 <div>
元素。
W3C DOM Level 1 使用 getElementsByTagName()
方法获得所有相同标签名的元素的引用。该方法在 JavaScript 中返回一个数组,可用于 document
元素,也可用于其他节点只检索对应的子树。要获得 DOM 树中所有元素的列表,可使用 getElementsByTagName(*)
。
表 1 中列出了 DOM Level 1 方法,大部分用于把元素移动到特定位置或切换其可视性(菜单、动画)。Netscape 4 使用 <layer>
标签(Mozilla 不支持)作为可以任意定位的 HTML 元素。在 Mozilla 中,可使用 <div>
标签定位元素,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,比如 children
属性。
表 2. 用于遍历 DOM 的方法 属性/方法 说明 childNodes 返回元素所有子节点的数组。 firstChild 返回元素的第一个子节点。 getAttribute( aAttributeName ) 返回指定属性的值。 hasAttribute( aAttributeName ) 返回一个 Boolean 值表明当前节点是否包含指定名称的属性。 hasChildNodes() 返回一个布尔指表明当前节点是否有子节点。 lastChild 返回元素的最后一个子节点。 nextSibling 返回紧接于当前节点之后的节点。 nodeName 用字符串返回当前节点的名称。 nodeType 返回当前节点的类型。
值 说明 1 元素节点 2 属性节点 3 文本节点 4 CDATA 选择节点 5 实体引用节点 6 实体节点 7 处理指令节点 8 注释节点 9 文档节点 10 文档类型节点 11 文档片断节点 12 符号节点
nodeValue 返回当前节点的值。对于包含文本的节点,如文本和注释节点返回其字符串值。对于属性节点返回属性值。其他节点返回null
。
ownerDocument
返回包含当前节点的 document
对象。
parentNode
返回当前节点的父节点。
previousSibling
返回当前节点之前的相邻节点。
removeAttribute( aName )
从当前节点中删除指定的属性。
setAttribute( aName, aValue )
设置指定属性的值。
Internet Explorer 有一种非标准的特殊行为,这些 API 很多跳过(比如)新行字符生成的空白文本节点。Mozilla 则不跳过,因此有时候需要区分这些节点。每个节点都有一个 nodeType
属性指定了节点类型。比如,元素节点类型是 1,文本节点是 3,而注释节点是 8。仅处理元素节点最好的办法是遍历所有子节点,然后处理那些 nodeType 为 1 的节点:
HTML: <div id="foo"> <span>Test</span> c </div> JavaScript: var myDiv = document.getElementById("foo"); var myChildren = myXMLDoc.childNodes; for (var i = 0; i < myChildren.length; i++) { if (myChildren[i].nodeType == 1){ // element node } }
生成和操纵内容
Mozilla 支持向 DOM 动态增加内容的遗留方法,如 document.write
、document.open
和 document.close
。Mozilla 也支持 Internet Explorer 的 InnerHTML
方法,该方法基本上可以在任何节点上使用。但是不支持 OuterHTML
(围绕着元素添加标记,标准中也没有等价的方法)和 innerText
(设置节点的文本值,在 Mozilla 中可使用 textContent
)。
Internet Explorer 有一些非标准的、Mozilla 不支持的内容操作方法,包括检索值、插入文本以及邻近某个节点插入元素,比如 getAdjacentElement
和 insertAdjacentHTML
。表 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 引入了文档片断,这是一种轻型文档包含一般文档接口的一个子集。比如没有 getElementById
但是有 appendChild
。很容易向已有文档添加文档片断。
Mozilla 使用 document.createDocumentFragment()
创建文档片断,该方法返回一个空的文档片断。
但是,Internet Explorer 的文档片断实现没有遵循 W3C 标准,仅仅返回一般的文档。
回页首
JavaScript 差异
Mozilla 和 Internet Explorer 的多数差异都和 JavaScript 有关。但问题通常源自浏览器向 JavaScript 公开的 API,比如 DOM 钩子。两种浏览器在核心 JavaScript 上区别不大,遇到的问题通常和时间有关。
JavaScript date 差异
Date
惟一的区别是 getYear
方法。根据 ECMAScript 规范(这是 JavaScript 所遵循的规范),该方法没有解决千年问题,在 2004 年运行 new Date().getYear()
将返回“104”。根据 ECMAScript 规范,getYear
返回的年份减去 1900 最初是为了在 1998 年返回“98”。ECMAScript Version 3 废止了 getYear
,用 getFullYear()
代替。Internet Explorer 修改了 getYear()
使其和 getFullYear()
类似,消除了千年问题,而 Mozilla 坚持采用标准的行为方式。
JavaScript 执行差异
不同的浏览器执行 JavaScript 的方式是不同的。比如,下列代码假设 script
块执行的时候 div
节点已经存在于 DOM 中:
... <div id="foo">Loading...</div> <script> document.getElementById("foo").innerHTML = "Done."; </script>
但是并不能保证这一点。为了保证所有的元素已经存在,应该对 <body>
元素使用 onload
事件处理程序:
<body onload="doFinish()"> <div id="foo">Loading...</div> <script> function doFinish() { var element = document.getElementById("foo"); element.innerHTML = "Done."; } </script> ...
这类与时间有关的问题也和硬件有关,速度慢的系统可能会发现速度快的系统隐藏起来的 bug。一个具体的例子是 window.open
,它打开新的窗口:
<script> function doOpenWindow(){ var myWindow = window.open("about:blank"); myWindow.location.href = "http://www.ibm.com"; } </script>
这段代码的问题在于 window.open
是异步的,在窗口打开之前没有阻塞 JavaScript 的执行。因此,window.open
后面的行有可能在新窗口打开之前执行。可以在新窗口的 onload
处理程序中解决这个问题,然后回调打开它的窗口(使用 window.opener
)。
JavaScript 生成 HTML 的差别
JavaScript 可以通过 document.write
即时用字符串生成 HTML。主要的问题在于,如果内嵌在 HTML 文档(因此也在一个 <script>
标签中)的 JavaScript 生成的 HTML 又包含 <script>
标签怎么办。如果文档采用 严格呈现模式,就会把字符串中的 </script>
解释成外层 <script>
的结束标签。下面的代码很好地说明了这一点:
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> ... <script> document.write("<script>alert("Hello")</script>") </script>
由于该页面采用严格模式,Mozilla 解析器就会看到第一个 <script>
并解析它直到发现第一个结束标签,即第一个 </script>
。这是因为解析器不知道 JavaScript(或者其他任何语言)何时采用严格模式。在特殊模式下,解析器在解析的过程中分析 JavaScript(因而降低了速度)。Internet Explorer 总是采用特殊模式,因此不真正支持 XHTML。为了在 Mozilla 中使用严格模式,需要将字符串分解成两部分:
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> ... <script> document.write("<script>alert("Hello")</" + "script>") </script>
调试 JavaScript
Mozilla 提供了多种方法调试为 Internet Explorer 创建的应用程序中的 JavaScript 相关问题。第一个工具是内置的 JavaScript 控制台,如图 1 所示,它记录了错误和警告信息。在 Mozilla 中选择 Tools -> Web Development -> JavaScript Console,或者在 Firefox(来自 Mozilla 的独立浏览器产品)中选择 Tools -> JavaScript Console 就能打开它。
图 1. JavaScript 控制台
JavaScript 控制台可以显示完整的日志列表,也可以分别显示错误、警告和消息。图 1 中的错误消息表明,aol.com 第 95 行访问的变量 is_ns70 不存在。单击该链接可以打开 Mozilla 内部的查看源代码窗口,突出显示出错的一行。
控制台还允许对 JavaScript 求值。要计算输入的 JavaScript 语法,在输入字段中输入 1+1
然后按 Evaluate,结果如图 2 所示。
图 2. JavaScript 控制台求值
Mozilla 的 JavaScript 引擎内建了对调试的支持,从而为 JavaScript 开发人员提供了强大的工具。图 3 所示的 Venkman 是一种强大的跨平台 JavaScript 调试器,它与 Mozilla 集成在一起。它通常和 Mozilla 发行包捆绑在一起,可以通过选择 Tools -> Web Development -> JavaScript Debugger 打开它。Firefox 没有捆绑这个调试器,但是可以从 http://www.mozilla.org/projects/venkman/ 下载安装。还可以在开发页面上找到相关教程,开发页面的 URL 为 http://www.hacksrus.com/~ginda/venkman/。
图 3. Mozilla 的 JavaScript 调试器
JavaScript 调试器可以调试在 Mozilla 浏览器窗口中运行的 JavaScript。它支持断点管理、查看调用栈和变量/对象检查这样的标准调试特性。所有特性都可通过用户界面或者调试器的交互控制台来访问。通过控制台,可以在和调试的 JavaScript 代码同一作用域内执行任何 JavaScript。
12下一页阅读全文免责声明:本站文章均来自网站采集或用户投稿,网站不提供任何软件下载或自行开发的软件! 如有用户或公司发现本站内容信息存在侵权行为,请邮件告知! 858582#qq.com