文档对象模型(DOM,Document Object Model)是针对XML但经过扩展用于HTML的应用程序编程接口。DOM把整个页面映射为一个多层节点结构。HTML或XML页面中每个组成部分都是某种类型的节点,这些节点又包括着不同类型的数据。 通过DOM创建的这个表示文档的树形图,开发人员获得了控制页面内容和结构的主动权。借助DOM提供的API,开发人员可以轻松自如地删除、添加、替换或修改任何节点。
IE中所有的DOM对象都是以COM对象的形式实现的。这意味着IE中的DOM对象与原生JavaScript对象的行为或活动特点并不一致。
节点层次
DOM将任何HTML或XML文档描绘成一个由多层次节点构成的结构。节点分为几种不同的类型,每种类型分别表示文档中不同的信息及(或)标记。每个节点都拥有各自的特点、数据和方法,另外也与其他节点存在某种关系。节点之间的关系构成了层次,而所有页面标记则表现为一个以特点节点为根节点的树形结构。以下面的HTML为例:
<html> <head> <title>Sample Page</title> </head> <body> <p>Hello World!</p> </body> </html>
可以将这个简单的HTML文档表示为一个层次结构。文档节点是每个文档的根节点。在这个例子中,文档节点只有一个子节点,即<html>元素,称之为文档元素。文档元素是文档最外层元素,文档中的其他所有元素都包含在文档元素中,每个文档只能有一个文档元素。 在HTML页面中,文档元素始终都是<html>元素。在XML中,没有预定义的元素,因此任何元素都可能成为文档元素。
每一段标记都可以通过树中的一个节点表示:HTML元素通过元素节点表示,特性(attribute)通过特性节点表示,文档类型通过文档类型节点表示,而注释则通过注释节点表示。总共有12种节点类型,这些类型都继承自一个基类型。
Node类型
DOM1级定义了一个Node接口,该接口由DOM中所有节点类型实现。这个Node接口在JavaScript中是作为Node类型实现的; 除了IE之外,在其他所有浏览器中都可以访问到这个类型。JavaScript中所有节点类型都继承自Node类型,因此所有节点类型都共享着相同的基本属性和方法。
每个节点都有一个nodeType属性,用来表示节点的类型。 节点类型由在Node类型中定义的下列12个数值常量来表示,任何节点类型必居其一:
- Node.ELEMENT_NODE(1)
- Node.ATTRIBUTE_NODE(2)
- Node.TEXT_NODE(3)
- Node.CDATA_SECTION_NODE(4)
- Node.ENTITY_PEFERENCE_NODE(5)
- Node.ENTITY_NODE(6)
- Node.PROCESSING_INSTRUCTION_NODE(7)
- Node.COMMENT_NODE(8)
- Node.DOCUMENT_NODE(9)
- Node.DOCUMENT_TYPE_NODE(10)
- Node.DOCUMENT_FRAGMENT_NODE(11)
- Node.DOCUMENT_NOTATION_NODE(12)
通过比较上面这些常量,可以很容易地确定节点的类型:
if(someNode.nodeType == Node.ELEMENT_NODE){ //IE中无效 alert("Node is an element"); }
不过由于IE没公开Node类型的构造函数,因此上面的代码在IE中会导致错误。为了确保跨浏览器兼容,最好还是将nodeType属性与数字值比较,如下所示:
if(someNode.nodeType == 1){ //IE中无效 alert("Node is an element"); }
要了解节点的具体信息,可以使用nodeName和nodeValue两个属性。这两个属性的值完全取决于节点的类型。对于元素节点,nodeName中保存的始终都是元素的标签名,而nodeValue的值则始终为null
节点关系
在HTML中,可以将<body>元素看成<html>元素的子元素,相应地,也可以将<html>看成是<body>元素的父元素。而<head>元素,则可以看成是<body>元素的同胞元素,因为他们都是同一个父元素<html>的直接子元素。
每个节点都有一个childNodes属性,其中保存着一个NodeList对象。NodeList是一种类数组对象,用于保存一组有序的节点,可以通过位置来访问这些节点。NodeList对象的独特之处在于,它实际上是基于DOM结构动态执行查询的结果,因此DOM结构的变化能够自动反应在NodeList对象中。
可以通过方括号,也可以使用item()方法:
var firstChild = someNode.childNodes[0]; var secondChild = someNode.childNodes.item(1); var count = someNode.childNodes.length;
要注意的是length属性表示的是访问NodeList的那一刻,其中包含的节点的数量。
每个节点都有一个parentNode属性,该属性指向文档树中的父节点。 包含在childNodes列表中的所有节点都具有相同的父节点,因此它们的parentNode属性都指向同一个节点。 此外,包含在childNodes列表中的每个节点相互之间都是同胞节点。 通过使用列表中每个节点的previousSibling和nextSibling属性,可以访问同一列表中的其他节点。列表中的第一个节点的previousSibling属性值为null,而最后一个节点的nextSibling属性的值同样也为null。
父节点与其第一个和最后一个子节点也存在特殊关系。 父节点的firstChild和lastChild分别指向childNodes列表中的一个和最后一个节点。 如果没有子节点,那么firstChild和lastChild的值均为null
另外hasChildNodes()也是一个非常有用的方法,这个方法在节点包含一或多个子节点的情况下返回true;应该说,这是比查询childNodes列表的length属性更简单的方法。
所有节点都有的一个属性是ownerDocument,该属性指向表示整个文档的文档节点。 这种关系表示的是任何节点都属于它所在的文档,任何节点都不能同时存在于两个或更多个文档中。
小结:所有反映节点关系的属性有:childNodes、parentNode、previousSibling、nextSibling、firstChild、lastChild、ownerDocument。通过这些属性我们可以访问所有节点。
操作节点
因为关系指针都是只读的,所以DOM提供了一些操作节点的方法。
appendChild()
用于向childNodes列表的末尾添加一个节点,添加节点后,childNodes的新增节点,父节点及以前的最后一个节点的关系指针都会得到相应的更新。更新完成后,appendChild返回新增的节点。
如果传入到appendChild()的节点已经是文档的一部分了,那结果就是将该节点从原来的位置转移到新位置。
insertBefore()
如果需要把节点放在childNodes列表中某个特定的位置上,而不是末尾,可以使用insertBefore()方法。这个方法接收两个参数:要插入的节点和作为参照的节点,插入节点后,被插入的节点会变成参照节点的前一个同胞节点(previousSibling),同时被方法返回。 如果参照节点是null,则insertBefore()与appendChild()执行相同的操作。
replaceChild()
该方法接收两个参数:要插入的节点和要替换的节点。 要替换的节点将由这个方法返回并从文档树中被移除,同时要插入的节点占据其位置。使用replaceChild()插入一个节点时,该节点所有关系指针都会从被它替换的节点复制过来。尽管从技术上讲被替换的节点仍然还在文档中,但它在文档中已经没有了自己的位置。
removeChild()
该方法接受一个参数,即要移除的节点。被移除的节点将成为方法的返回值。如果只是想移除而非替换节点可以使用removeChild()方法。与使用replaceChild()方法一样,通过removeChild()方法移除的节点仍然为文档所有,只不过在文档中已经没有了自己的位置。
cloneNode()
用于创建调用这个方法的节点的副本。 cloneNode()方法接受一个布尔值参数,表示是否执行深复制。参数为true的情况下,执行深复制,也就是复制节点及其整个子节点树;在参数为false的情况下,执行浅复制,即只复制节点本身。复制后返回的节点副本属于文档所有,但并没有为它指定父节点。因此这个节点副本就成了一个“孤儿”,除非通过appendChild()、insertBefore()、replaceChild()将它添加到文档中。
注:cloneNode()方法不会复制添加到DOM节点中的JavaScript属性,例如事件处理程序等。这个方法只复制特性、子节点,其他一切都不会复制。IE在此存在一个bug,即它会复制事件处理程序,所以建议在复制之前最好先移除事件处理程序。
normalize()
这个方法的唯一作用就是处理文档树中的文本节点。 由于解析器的实现或者DOM操作等原因,可能会出现文本节点不包含文本,或者接连出现两个文本节点的情况。当在某个节点上调用这个方法时,就会在该节点的后代节点中查找上述两种情况。如果找到了空文本节点,则删除它;如果找到相邻的文本节点,则将它们合并成为一个文本节点。
(
使用document.createElement()方法可以创建新元素。这个方法只接受一个参数,即要创建元素的标签名。这个标签名在HTML文档中部区分大小写,而在XML文档中,则是区分大小写的。要把新元素添加到文档树,可以使用appendChild()、insertBefore()或replaceChild()方法。)