DOM(文档对象模型)是针对 HTML 和 XML 文档的一个 API(应用程序编程接口)。 DOM 描绘了一个层次化的节点树,允许开发人员添加、移除和修改页面的某一部分。 DOM 脱胎于Netscape 及微软公司创始的 DHTML(动态 HTML),但现在它已经成为表现和操作页面标记的真正的跨
平台、语言中立的方式。
1998 年 10 月 DOM1级规范成为 W3C 的推荐标准,为基本的文档结构及查询提供了接口。
一、节点层次
DOM 可以将任何 HTML 或 XML 文档描绘成一个由多层节点构成的结构。节点分为几种不同的类型,每种类型分别表示文档中不同的信息及(或)标记。每个节点都拥有各自的特点、数据和方法,另外也与其他节点存在某种关系。节点之间的关系构成了层次,而所有页面标记则表现为一个以特定节点为根节点的树形结构。以下面的HTML为例:
1. <html>
2. <head>
3. <title>Sample Page</title>
4. </head>
5. <body>
6. <p>Hello World!</p>
7. </body>
8. </html>
其层次结构可表示为:
文档节点是每个文档的根节点。在这个例子中,文档节点只有一个子节点,即<html>元素,我们称之为文档元素。文档元素是文档的外层元素,文档中的其他所有元素都包含在文档元素中。每个文档只能有一个文档元素。在 HTML 页面中,文档元素始终都是<html>元素。在 XML 中,没有预定义的元素,因此任何元素都可能成为文档元素。
总共有 12 种节点类型,这些类型都继承自一个基类型。
A、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_REFERENCE_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.NOTATION_NODE(12)。
通过比较上面这些常量,可以很容易地确定节点的类型:
1. if (someNode.nodeType == Node.ELEMENT_NODE){ //在 IE 中无效
2. alert("Node is an element.");
3. }
然而,由于 IE 没有公开 Node 类型的构造函数,因此上面的代码在 IE 中会导致错误。为了确保跨浏览器兼容,好还是将 nodeType 属性与数字值进行比较:
1. if (someNode.nodeType == 1){ //适用于所有浏览器
2. alert("Node is an element.");
3. }
并不是所有节点类型都受到 Web 浏览器的支持。开发人员常用的就是元素和文本节点。
1. nodeName 和 nodeValue 属性
要了解节点的具体信息,可以使用 nodeName 和 nodeValue 这两个属性。这两个属性的值完全取决于节点的类型。在使用这两个值以前,好是像下面这样先检测一下节点的类型。
1. if (someNode.nodeType == 1){
2. value = someNode.nodeName; //nodeName 的值是元素的标签名
3. }
nodeName 中保存的始终都是元素的标签名,而 nodeValue 的值则始终为 null;
2. 节点关系
每个节点都有一个 childNodes 属性,其中保存着一个 NodeList 对象。 NodeList 是一种类数组对象,用于保存一组有序的节点,可以通过位置来访问这些节点。请注意,虽然可以通过方括号语法来访问 NodeList 的值,而且这个对象也有 length 属性,但它并不是 Array 的实例。 NodeList 对象的独特之处在于,它实际上是基于 DOM 结构动态执行查询的结果,因此 DOM 结构的变化能够自动反映在 NodeList 对象中。我们常说, NodeList 是有生命、有呼吸的对象,而不是在我们第一次访问它们的某个瞬间拍摄下来的一张快照。
下面的例子展示了如何访问保存在 NodeList 中的节点——可以通过方括号,也可以使用 item()方法。
1. var firstChild = someNode.childNodes[0];
2. var secondChild = someNode.childNodes.item(1);
3. var count = someNode.childNodes.length;
对 arguments 对象使用 Array.prototype.slice()方法可以将其转换为数组。而采用同样的方法,也可以将 NodeList 对象转换为数组。
1. //在 IE8 及之前版本中无效
2. var arrayOfNodes = Array.prototype.slice.call(someNode.childNodes,0);
由于 IE8 及更早版本将 NodeList实现为一个 COM 对象,而我们不能像使用 JScript 对象那样使用这种对象,因此上面的代码会导致错误。要想在 IE 中将 NodeList 转换为数组,必须手动枚举所有成员。下列代码在所有浏览器中都可以运行:
1. function convertToArray(nodes){
2. var array = null;
3. try {
4. array = Array.prototype.slice.call(nodes, 0); //针对非 IE 浏览器
5. } catch (ex) {
6. array = new Array();
7. for (var i=0, len=nodes.length; i < len; i++){
8. array.push(nodes[i]);
9. }
10. }
11. return array;
12. }
每个节点都有一个 parentNode 属性,该属性指向文档树中的父节点。包含在 childNodes 列表中的所有节点都具有相同的父节点,因此它们的 parentNode 属性都指向同一个节点。此外,包含在childNodes 列表中的每个节点相互之间都是同胞节点。通过使用列表中每个节点的 previousSibling和 nextSibling 属性,可以访问同一列表中的其他节点。列表中第一个节点的 previousSibling 属性值为 null,而列表中后一个节点的 nextSibling 属性的值同样也为 null。
1. if (someNode.nextSibling === null){
2. alert("Last node in the parent’s childNodes list.");
3. } else if (someNode.previousSibling === null){
4. alert("First node in the parent’s childNodes list.");
5. }
当然,如果列表中只有一个节点,那么该节点的 nextSibling 和 previousSibling 都为 null。
父节点与其第一个和后一个子节点之间也存在特殊关系。父节点的 firstChild 和 lastChild属性分别指向其 childNodes 列表中的第一个和后一个节点。其中, someNode.firstChild 的值始 终 等 于 someNode.childNodes[0] , 而 someNode.lastChild 的 值 始 终 等 于 someNode.childNodes [someNode.childNodes.length-1]。在只有一个子节点的情况下, firstChild 和lastChild 指向同一个节点。如果没有子节点,那么 firstChild 和 lastChild 的值均为 null。
另外, hasChildNodes()也是一个非常有用的方法,这个方法在节点包含一或多个子节点的情况下返回 true;应该说,这是比查询 childNodes列表的 length 属性更简单的方法。所有节点都有的后一个属性是 ownerDocument,该属性指向表示整个文档的文档节点。这种关系表示的是任何节点都属于它所在的文档,任何节点都不能同时存在于两个或更多个文档中。通过这个属性,我们可以不必在节点层次中通过层层回溯到达顶端,而是可以直接访问文档节点。
热点新闻
前端开发技术库