DOM(文档对象模型)是针对 HTML 和 XML 文档的一个API(应用程序编程接口)。DOM 描绘了一个层次化的节点树,允许开发人员添加、移除和修改页面的某一部分。
层次节点
DOM可以将任何 HTML 或 XML 文档描绘成一个由多层节点构成的结构。节点分成几种不同的类型,每种类型分别表示文档中不同的信息及(或)标记。每个节点都拥有各自的特点、属性及方法,另外也与其他节点存在某种关系。节点之间的关系构成了层次,而所有页面标记则表现为一个以特定节点为根节点的树形结构。以下面的 HTML 为例:
1 2 3 4 5 6 7 8 |
<html> <head> <title> Sample Page </title> </head> <body> <p> Hello World! </p> </body> </html> |
可以将这个简单的 HTML 文档表示为一个层次结构:
1 2 3 4 5 6 7 8 9 |
Document Element html Element head Element title Text Sample Page Element body Element p Text Hello World! |
文档节点是每个文档的根节点,在这个例子中,文档节点只有一个子节点,即 <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_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)
值得注意的是,并不是所有节点类型都受到 Web 浏览器的支持。 开发人员最常用的就是元素和文本节点。而且,上面列出的变量,在 IE 中并没有什么卵用。
1 2 3 4 5 6 |
<div> </div> <script> var a = document.querySelector('div'); console.log(a.nodeType === 1); // 兼容所有浏览器 console.log(a.nodeType === Node.ELEMENT_NODE); // 不兼容 IE </script> |
操作节点
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 |
// 访问子节点 var firstNode = someNode.childNodes[0]; var secondNode = someNode.childNodes.item(1); var count = someNode.childNodes.length; // 能动态变化, childNode并不是快照 var firstNode = someNode.firstChild; var lastNode = someNode.lastChild; // 访问兄弟节点 var nextNode = someNode.nextSibling; var preNode = someNode.previousSibling; // 判断有没有子节点 var hasChild = someNode.hasChildNodes(); var hasChild = someNode.childNodes.length > 0; // 访问节点的文档节点 var documentNode = someNode.ownerDocument; /* 操作节点 */ // appendChild var returnNode = someNode.appendChild(newNode); console.log(returnNode === newNode) // true console.log(someNode.lastChild === newNode) // true // 如果传入到 appendChild() 中的节点已经是文档的一部分了,那就将该节点从原来的位置移到新的位置 var returnNode = someNode.appendChild(someNode.firstChild); console.log(returnNode === someNode.firstChild); // false console.log(returnNode === someNode.lastChild); // true // insertBefore(a, b) 将a插入b前 a和b互为Sibling // 插入后成为最后一个子节点 var returnNode = someNode.insertBefore(newNode, null); console.log(newNode === someNode.lastChild); // true // 插入后成为第一个子节点 var returnNode = someNode.insertBefore(newNode, someNode.firstChild); console.log(returnNode === someNode.firstChild); // true // removeChild 移除节点 var formerFirstChild = someNode.removeChild(someNode.firstChild); // replaceChild 替换 // 替换第一个子节点 var returnNode = someNode.replaceChild(newNode, someNode.firstChild); |
前面介绍的都是操作节点的子节点,也就是说,要使用这些方法必须先获得父节点。最后介绍的 cloneNode()
方法适合所有节点。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
<ul> <li> apple </li> <li> orange </li> <li> pear </li> </ul> <script> var myList = document.querySelector('ul'); // 深度复制 var deepList = myList.cloneNode(true); console.log(deepList.childNodes.length); // 7 // 浅复制 var shallowList = myList.cloneNode(false); console.log(shallowList.childNodes.length); // 0 </script> |