1. 概述
1.1 DOM
DOM 是 JavaScript 操作网页的接口,全称为“文档对象模型”(Document Object Model)。它的作用是将网页转为一个 JavaScript 对象,从而可以用脚本进行各种操作(比如增删内容)。
浏览器会根据 DOM 模型,将结构化文档(比如 HTML 和 XML)解析成一系列的节点,再由这些节点组成一个树状结构(DOM Tree)。
严格地说,DOM 不是 JavaScript 语法的一部分,但JavaScript 是最常用于 DOM 操作的语言,离开了 DOM,JavaScript 就无法控制网页。
1.2 节点(node)
DOM 的最小组成单位叫做节点(node)。文档的树形结构(DOM 树),就是由各种不同类型的节点组成。
节点的类型有七种,都继承了原生的节点对象 Node。
-
Document:整个文档树的顶层节点 -
DocumentType:doctype标签(比如<!DOCTYPE html>) -
Element:网页的各种HTML标签(比如<body>、<a>等) -
Attribute:网页元素的属性(比如class="right") -
Text:标签之间或标签包含的文本 -
Comment:注释 -
DocumentFragment:文档的片段
1.3 节点树
文档的第一层只有一个节点,就是 HTML 网页的第一个标签 <html>,它构成了树结构的根节点(root node),其他 HTML 标签节点都是它的下级节点。
除了根节点,其他节点都有三种层级关系。
- 父节点关系(parentNode):直接的那个上级节点
- 子节点关系(childNodes):直接的下级节点
- 同级节点关系(sibling):拥有同一个父节点的节点
2. Node 接口
所有 DOM 节点对象都继承了 Node 接口,拥有一些共同的属性和方法。
2.1 nodeType 属性
nodeType 属性返回一个整数值,表示节点的类型。
不同节点的 nodeType 属性值和对应的常量如下:
- 元素节点(element):对应常量
Node.ELEMENT_NODE,1 - 属性节点(attr):常量
Node.ATTRIBUTE_NODE,2 - 文本节点(text):常量
Node.TEXT_NODE,3 - 注释节点(Comment):常量
Node.COMMENT_NODE,8 - 文档节点(document):常量
Node.DOCUMENT_NODE,9 - 文档类型节点(DocumentType):常量
Node.DOCUMENT_TYPE_NODE,10 - 文档片断节点(DocumentFragment):常量
Node.DOCUMENT_FRAGMENT_NODE,11
确定节点类型时,使用 nodeType 属性是常用方法。
2.2 nextSibling 属性
Node.nextSibling 属性返回紧跟在当前节点后面的第一个同级节点。如果当前节点后面没有同级节点,则返回 null。
// HTML 代码如下
// <div id="d1">hello</div><div id="d2">world</div>
var d1 = document.getElementById('d1');
var d2 = document.getElementById('d2');
d1.nextSibling === d2 // true
注意,该属性还包括文本节点和注释节点(<! -- comment -->)。因此如果当前节点后面有空格,该属性会返回一个文本节点,内容为空格。
2.3 previousSibling 属性
previousSibling 属性返回当前节点前面的、距离最近的一个同级节点。如果当前节点前面没有同级节点,则返回 null 。
// HTML 代码如下
// <div id="d1">hello</div><div id="d2">world</div>
var d1 = document.getElementById('d1');
var d2 = document.getElementById('d2');
d2.previousSibling === d1 // true
注意,该属性还包括文本节点和注释节点。因此如果当前节点前面有空格,该属性会返回一个文本节点,内容为空格。
2.4 父节点 parentNode 和 parentElement 属性
- parentNode 属性
parentNode属性返回当前节点的父节点。对于一个节点来说,它的父节点只可能是三种类型:元素节点(element)、文档节点(document)和文档片段节点(documentfragment)。
文档节点(document)和文档片段节点(documentfragment)的父节点都是null。另外,对于那些生成后还没插入 DOM 树的节点,父节点也是null。 - parentElement 属性
parentElement属性返回当前节点的父元素节点。如果当前节点没有父节点,或者父节点类型不是元素节点,则返回null。
注意: Element:网页的各种HTML标签(比如 <body>、<a> 等)
2.5 childNodes 属性
childNodes 属性返回一个类似数组的对象(NodeList 集合),成员包括当前节点的所有子节点(包括文本节点和注释节点)。
var children = document.querySelector('ul').childNodes;
上面代码中,children 就是 ul 元素的所有子节点。
- 另:关于 ParentNode.children
如果当前节点是父节点,就会继承ParentNode接口。(只有元素节点(element)、文档节点(document)和文档片段节点(documentFragment)拥有子节点)
children属性返回一个HTMLCollection实例,成员是当前节点的所有元素子节点。
注意,children属性只包括元素子节点,不包括其他类型的子节点(比如文本子节点)
3. NodeList 接口
节点都是单个对象,有时需要一种数据结构,能够容纳多个节点。DOM 提供两种节点集合,用于容纳多个节点:NodeList 和 HTMLCollection。
主要区别是,NodeList 可以包含各种类型的节点,HTMLCollection 只能包含 HTML 元素节点。
3.1 NodeList 实例
通过以下方法可以得到 NodeList 实例。
Node.childNodes-
document.querySelectorAll()等节点搜索方法
document.body.childNodes instanceof NodeList // true
var children = document.body.childNodes;
Array.isArray(children) // false
children.length // 34
children.forEach(console.log)
上面代码中,NodeList实例 children 不是数组,但是具有 length 属性和 forEach 方法。
3.2 静态/动态集合
NodeList 实例可能是动态集合,也可能是静态集合。
所谓动态集合就是一个活的集合,DOM 删除或新增一个相关节点,都会立刻反映在 NodeList实例。
目前,只有 Node.childNodes 返回的是一个动态集合,其他的 NodeList 都是静态集合。
var children = document.body.childNodes;
children.length // 18
document.body.appendChild(document.createElement('p'));
children.length // 19
4. HTMLCollection 接口
4.1 概述
HTMLCollection 是一个节点对象的集合,只能包含元素节点(element),不能包含其他类型的节点。
它的返回值是一个类似数组的对象,但是与 NodeList 接口不同,HTMLCollection没有forEach 方法,只能使用 for 循环遍历。
HTMLCollection 实例都是动态集合,节点的变化会实时反映在集合中。
返回 HTMLCollection 实例的,主要是一些 Document对象的集合属性,比如 document.links、document.forms、document.images等。
document.links instanceof HTMLCollection // true
4.2 id 或 name 属性引用
如果元素节点有 id 或 name 属性,那么 HTMLCollection 实例上面,可以使用 id 属性或 name 属性引用该节点元素。如果没有对应的节点,则返回 null。
// HTML 代码如下
// <img id="pic" src="http://example.com/foo.jpg">
var pic = document.getElementById('pic');
document.images.pic === pic // true
上面代码中,document.images 是一个HTMLCollection实例,可以通过<img>元素的 id 属性值,从 HTMLCollection 实例上取到这个元素。
5. ParentNode 接口
节点对象除了继承 Node 接口以外,还会继承其他接口。ParentNode 接口表示当前节点是一个父节点,提供一些处理子节点的方法。ChildNode 接口表示当前节点是一个子节点,提供一些相关方法。
(以下重复文字可看做为:重要的事情多说一遍)
5.1 父节点与 ParentNode 接口
如果当前节点是父节点,就会继承 ParentNode 接口。
由于只有元素节点(element)、文档节点(document)和文档片段节点(documentFragment)拥有子节点,因此只有这三类节点会继承 ParentNode 接口。
5.2 ParentNode.children(只读)
children 属性返回一个 HTMLCollection 实例,成员是当前节点的所有元素子节点。
注意,children 属性只包括元素子节点,不包括其他类型的子节点(比如文本子节点)。如果没有元素类型的子节点,返回值 HTMLCollection 实例的 length 属性为 0。
另外,HTMLCollection 是动态集合,会实时反映 DOM 的任何变化。
6. ChildNode 接口
6.1 概述
ChildNode 接口表示当前节点是一个子节点,提供一些相关方法。
如果一个节点有父节点,那么该节点就继承了 ChildNode 接口。
6.2 方法
- ChildNode.remove()
remove方法用于从父节点移除当前节点。
el.remove()
上面代码在 DOM 里面移除了 el 节点。
- ChildNode.before()
before方法用于在当前节点的前面,插入一个或多个同级节点。两者拥有相同的父节点。
该方法不仅可以插入元素节点,还可以插入文本节点。
var p = document.createElement('p');
var p1 = document.createElement('p');
// 插入元素节点
el.before(p);
// 插入文本节点
el.before('Hello');
// 插入多个元素节点
el.before(p, p1);
// 插入元素节点和文本节点
el.before(p, 'Hello');
-
ChildNode.after()
after方法用于在当前节点的后面,插入一个或多个同级节点,两者拥有相同的父节点。用法与before方法完全相同。 -
ChildNode.replaceWith()
replaceWith方法使用参数节点,替换当前节点。参数可以是元素节点,也可以是文本节点。
var span = document.createElement('span');
el.replaceWith(span);
上面代码中,el 节点将被 span 节点替换。
参考连接
- 阮一峰, JavaScript 教程









网友评论