再看一看各种节点提供的属性和方法。对于那些熟悉面向对象(OO)编程的人来说,它们说明了 DOM 的一个重要特点:DOM 并非完全面向对象的 API。首先,很多情况下要直接使用对象的属性而不是调用节点对象的方法。比方说,没有 getNodeName()
方法,而要直接使用 nodeName
属性。因此节点对象(以及其他 DOM 对象)通过属性而不是函数公开了大量数据。
其次,如果习惯于使用重载对象和面向对象的 API,特别是 Java 和 C++ 这样的语言,就会发现 DOM 中的对象和方法命名有点奇怪。DOM 必须能用于 C、Java 和 JavaScript(这只是其中的几种语言),因此 API 设计作了一些折衷。比如,NamedNodeMap
方法有两种不同的形式:
getNamedItem(String name)
getNamedItemNS(Node node)
对于 OO 程序员来说这看起来非常奇怪。两个方法目的相同,只不过一个使用 String 参数而另一个使用 Node
参数。多数 OO API 中对这两种版本都会使用相同的方法名。运行代码的虚拟机将根据传递给方法的对象类型决定运行哪个方法。
问题在于 JavaScript 不支持这种称为方法重载 的技术。换句话说,JavaScript 要求每个方法或函数使用不同的名称。因此,如果有了一个名为 getNamedItem()
的接受字符串参数的方法,就不能再有另一个方法或函数也命名为 getNamedItem()
,即使这个方法的参数类型不同(或者完全不同的一组参数)。如果这样做,JavaScript 将报告错误,代码不会按照预期的方式执行。
从根本上说,DOM 有意识地避开了方法重载和其他 OO 编程技术。这是为了保证该 API 能够用于多种语言,包括那些不支持 OO 编程技术的语言。后果不过是要求您多记住一些方法名而已。好处是可以在任何语言中学习 DOM,比如 Java,并清楚同样的方法名和编码结构也能用于具有 DOM 实现的其他语言,如 JavaScript。
如果深入研究 API 设计或者仅仅非常关注 API 设计,您可能会问:“为何节点类型的属性不能适用于所有节点?” 这是一个很好的问题,问题的答案与政治及决策关系更密切,而非技术原因。简单地说,答案就是,“谁知道!但有点令人恼火,不是吗?”
属性 nodeName
意味着允许每种类型的节点都有一个名字,但是很多情况下名字要么未定义,要么是对于程序员没有意义的内部名(比如在 Java 中,很多情况下文本节点的 nodeName
被报告为 “#text”)。从根本上说,必须假设您得自己来处理错误。直接访问 myNode.nodeName
然后使用该值是危险的,很多情况下这个值为空。因此与通常的编程一样,程序员要谨慎从事。
现在已经介绍了 DOM 节点的一些特性和属性(以及一些奇特的地方),下面开始讲述您将用到的一些特殊节点类型。多数 Web 应用程序中只用到四种节点类型:
a
或 img
。href
(a
元素)或 src
(img
元素)。p
、a
或 h2
这些元素中的文字。处理 HTML 时,95% 的时间是跟这些节点类型打交道。因此本文的其余部分将详细讨论这些节点。(将来讨论 XML 的时候将介绍其他一些节点类型。)
基本上所有基于 DOM 的代码中都要用到的第一个节点类型是文档节点。文档节点 实际上并不是 HTML(或 XML)页面中的一个元素而是页面本身。因此在 HTML Web 页面中,文档节点就是整个 DOM 树。在 JavaScript 中,可以使用关键字 document
访问文档节点:
|
JavaScript 中的 document
关键字返回当前网页的 DOM 树。从这里可以开始处理树中的所有节点。
也可使用 document
对象创建新节点,如下所示:
createElement(elementName)
使用给定的名称创建一个元素。createTextNode(text)
使用提供的文本创建一个新的文本节点。createAttribute(attributeName)
用提供的名称创建一个新属性。这里的关键在于这些方法创建节点,但是并没有将其附加或者插入到特定的文档中。因此,必须使用前面所述的方法如 insertBefore()
或 appendChild()
来完成这一步。因此,可使用下面的代码创建新元素并将其添加到文档中:
|
一旦使用 document
元素获得对 Web 页面 DOM 树的访问,就可以直接使用元素、属性和文本了。
虽然会大量使用元素节点,但很多需要对元素执行的操作都是所有节点共有的方法和属性,而不是元素特有的方法和属性。元素只有两组专有的方法:
getAttribute(name)
返回名为 name
的属性值。removeAttribute(name)
删除名为 name
的属性。setAttribute(name, value)
创建一个名为 name
的属性并将其值设为 value
。getAttributeNode(name)
返回名为 name
的属性节点(属性节点在 下一节 介绍)。removeAttributeNode(node)
删除与指定节点匹配的属性节点。getElementsByTagName_r(elementName)
返回具有指定名称的元素节点列表。这些方法意义都很清楚,但还是来看几个例子吧。
处理元素很简单,比如可用 document
对象和上述方法创建一个新的 img
元素:
|
现在看起来应该非常简单了。实际上,只要理解了节点的概念并知道有哪些方法可用,就会发现在 Web 页面和 JavaScript 代码中处理 DOM 非常简单。在上述代码中,JavaScript 创建了一个新的 img
元素,设置了一些属性然后添加到 HTML 页面的 body 元素中。
发现嵌套的元素很容易。比如,下面的代码用于发现和删除 清单 3 所示 HTML 页面中的所有 img
元素:
|
也可以使用 getElementsByTagName_r()
完成类似的功能:
|
DOM 将属性表示成节点,可以通过元素的 attributes
来访问元素的属性,如下所示:
|
|
需要指出的是,attributes
属性实际上是对节点类型而非局限于元素类型来说的。有点古怪,不影响您编写代码,但是仍然有必要知道这一点。
虽然也能使用属性节点,但通常使用元素类的方法处理属性更简单。其中包括:
getAttribute(name)
返回名为 name
的属性值。removeAttribute(name)
删除名为 name
的属性。setAttribute(name, value)
创建一个名为 name
的属性并将其值设为 value
。这三个方法不需要直接处理属性节点。但允许使用简单的字符串属性设置和删除属性及其值。
需要考虑的最后一种节点是文本节点(至少在处理 HTML DOM 树的时候如此)。基本上通常用于处理文本节点的所有属性都属于节点对象。实际上,一般使用 nodeValue
属性来访问文本节点的文本,如下所示:
|
少数其他几种方法是专门用于文本节点的。这些方法用于增加或分解节点中的数据:
appendData(text)
将提供的文本追加到文本节点的已有内容之后。insertData(position, text)
允许在文本节点的中间插入数据。在指定的位置插入提供的文本。replaceData(position, length, text)
从指定位置开始删除指定长度的字符,用提供的文本代替删除的文本。到目前为止看到的多数代码都假设已经知道处理的节点是什么类型,但情况并非总是如此。比方说,如果在 DOM 树中导航并处理一般的节点类型,可能就不知道您遇到了元素还是文本。也许获得了 p
元素的所有孩子,但是不能确定处理的是文本、b
元素还是 img
元素。这种情况下,在进一步的处理之前需要确定是什么类型的节点。
所幸的是很容易就能做到。DOM 节点类型定义了一些常量,比如:
Node.ELEMENT_NODE
是表示元素节点类型的常量。Node.ATTRIBUTE_NODE
是表示属性节点类型的常量。Node.TEXT_NODE
是表示文本节点类型的常量。Node.DOCUMENT_NODE
是表示文档节点类型的常量。还有其他一些节点类型,但是对于 HTML 除了这四种以外很少用到。我有意没有给出这些常量的值,虽然 DOM 规范中定义了这些值,永远不要直接使用那些值,因为这正是常量的目的!
可使用 nodeType
属性比较节点和上述常量 —— 该属性定义在 DOM node 类型上因此可用于所有节点,如下所示:
|
这个例子非常简单,但说明了一个大问题:得到节点的类型非常 简单。更有挑战性的是知道节点的类型之后确定能做什么,只要掌握了节点、文本、属性和元素类型提供了什么属性和方法,就可以自己进行 DOM 编程了。
好了,快结束了。
nodeType
属性似乎是使用节点的一个入场券 —— 允许确定要处理的节点类型然后编写处理该节点的代码。问题在于上述 Node
常量定义不能正确地用于 Internet Explorer。因此如果在代码中使用 Node.ELEMENT_NODE
、Node.TEXT_NODE
或其他任何常量,Internet Explorer 都将返回如 图 4 所示的错误。
任何时候在 JavaScript 中使用 Node
常量,Internet Explorer 都会报错。因为多数人仍然在使用 Internet Explorer,应该避免在代码中使用 Node.ELEMENT_NODE
或 Node.TEXT_NODE
这样的构造。尽管据说即将发布的新版本 Internet Explorer 7.0 将解决这个问题,但是在 Internet Explorer 6.x 退出舞台之前仍然要很多年。因此应避免使用 Node
,要想让您的 DOM 代码(和 Ajax 应用程序)能用于所有主要浏览器,这一点很重要。
|
在本系列的上几期文章中您已经学习了很多。现在,您不 应该再坐等下一篇文章期待我介绍各种聪明的 DOM 树用法。现在的家庭作业是看看如何使用 DOM 创造出富有想像力的效果或者漂亮的界面。利用近几期文章中所学的知识开始实验和练习。看看能否建立感觉更与桌面应用程序接近的网站,对象能够响应用户的动作在屏幕上移动。
最好在屏幕上为每个对象画一个边界,这样就能看到 DOM 树中的对象在何处,然后再移动对象。创建节点并将其添加到已有的孩子列表中,删除没有嵌套节点的空节点,改变节点的 CSS 样式,看看孩子节点是否会继承这些修改。可能性是无限的,每当尝试一些新东西时,就学到了一些新的知识。尽情地修改您的网页吧!
在 DOM 三部曲的最后一期文章中,我将 介绍如何把一些非常棒的有趣的 DOM 应用结合到编程中。我将不再是从概念上说教和解释 API,而会提供一些代码。在此之前先发挥您自己的聪明才智,看看能做些什么
联系客服