DOM(文档对象模型)是针对 HTML 和 XML 文档的一个API(应用程序编程接口)。DOM 描绘了一个层次化的节点树,允许开发人员添加、移除和修改页面的某一部分。
层次节点
DOM可以将任何 HTML 或 XML 文档描绘成一个由多层节点构成的结构。节点分成几种不同的类型,每种类型分别表示文档中不同的信息及(或)标记。每个节点都拥有各自的特点、属性及方法,另外也与其他节点存在某种关系。节点之间的关系构成了层次,而所有页面标记则表现为一个以特定节点为根节点的树形结构。以下面的 HTML 为例:
<html>
<head>
<title> Sample Page </title>
</head>
<body>
<p> Hello World! </p>
</body>
</html>
可以将这个简单的 HTML 文档表示为一个层次结构:
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 中并没有什么卵用。
<div> </div>
<script>
var a = document.querySelector('div');
console.log(a.nodeType === 1); // 兼容所有浏览器
console.log(a.nodeType === Node.ELEMENT_NODE); // 不兼容 IE
</script>
操作节点
// 访问子节点
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()
方法适合所有节点。
<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>