Skip to content

Commit 4857f45

Browse files
committed
docs(dom): add mutationobserver
1 parent 17ac921 commit 4857f45

File tree

3 files changed

+274
-3
lines changed

3 files changed

+274
-3
lines changed

chapters.yml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,7 @@
5353
- dom/attributes.md: 属性的操作
5454
- dom/text.md: Text 节点和 DocumentFragment 节点
5555
- dom/css.md: CSS 操作
56+
- dom/mutationobserver.md: Mutation Observer API
5657
- dom/globaleventhandlers.md: GlobalEventHandlers 接
5758
- bom/: 浏览器模型
5859
- bom/cookie.md: Cookie

docs/dom/css.md

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
# CSS 操作
22

3-
CSS 与 JavaScript 是两个有着明确分工的领域,前者负责页面的视觉效果,后者负责与用户的行为互动。但是,它们毕竟同属网页开发的前端,因此不可避免有着交叉和互相配合。本节介绍如果通过 JavaScript 操作 CSS。
3+
CSS 与 JavaScript 是两个有着明确分工的领域,前者负责页面的视觉效果,后者负责与用户的行为互动。但是,它们毕竟同属网页开发的前端,因此不可避免有着交叉和互相配合。本节介绍如何通过 JavaScript 操作 CSS。
44

55
## HTML 元素的 style 属性
66

@@ -560,7 +560,9 @@ crl.length // 2
560560

561561
## CSSRule 接口
562562

563-
一条 CSS 规则包括两个部分:CSS选择器和样式声明。下面就是一条典型的 CSS 规则。
563+
### 概述
564+
565+
一条 CSS 规则包括两个部分:CSS 选择器和样式声明。下面就是一条典型的 CSS 规则。
564566

565567
```css
566568
.myClass {
@@ -585,7 +587,7 @@ var rule = ruleList[0];
585587
rule instanceof CSSRule // true
586588
```
587589

588-
CSSRule 实例有以下属性。
590+
### CSSRule 实例的属性
589591

590592
**(1)CSSRule.cssText**
591593

docs/dom/mutationobserver.md

Lines changed: 268 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,268 @@
1+
# Mutation Observer API
2+
3+
## 概述
4+
5+
Mutation Observer API 用来监视 DOM 变动。DOM 的任何变动,比如节点的增减、属性的变动、文本内容的变动,这个 API 都可以得到通知。
6+
7+
概念上,它很接近事件,可以理解为 DOM 发生变动就会触发 Mutation Observer 事件。但是,它与事件有一个本质不同:事件是同步触发,也就是说,DOM 的变动立刻会触发相应的事件;Mutation Observer 则是异步触发,DOM 的变动并不会马上触发,而是要等到当前所有 DOM 操作都结束才触发。
8+
9+
这样设计是为了应付 DOM 变动频繁的特点。举例来说,如果文档中连续插入1000个`<p>`元素,就会连续触发1000个插入事件,执行每个事件的回调函数,这很可能造成浏览器的卡顿;而 Mutation Observer 完全不同,只在1000个段落都插入结束后才会触发,而且只触发一次。
10+
11+
Mutation Observer 有以下特点。
12+
13+
- 它等待所有脚本任务完成后,才会运行(即异步触发方式)。
14+
- 它把 DOM 变动记录封装成一个数组进行处理,而不是一条条个别处理 DOM 变动。
15+
- 它既可以观察 DOM 的所有类型变动,也可以指定只观察某一类变动。
16+
17+
## MutationObserver 构造函数
18+
19+
使用时,首先使用`MutationObserver`构造函数,新建一个观察器实例,同时指定这个实例的回调函数。
20+
21+
```javascript
22+
var observer = new MutationObserver(callback);
23+
```
24+
25+
上面代码中的回调函数,会在每次 DOM 变动后调用。该回调函数接受两个参数,第一个是变动数组,第二个是观察器实例,下面是一个例子。
26+
27+
```javascript
28+
var observer = new MutationObserver(function (mutations, observer) {
29+
mutations.forEach(function(mutation) {
30+
console.log(mutation);
31+
});
32+
});
33+
```
34+
35+
## MutationObserver 的实例方法
36+
37+
### observe()
38+
39+
`observe`方法用来启动监听,它接受两个参数。
40+
41+
- 第一个参数:所要观察的 DOM 节点
42+
- 第二个参数:一个配置对象,指定所要观察的特定变动
43+
44+
```javascript
45+
var article = document.querySelector('article');
46+
47+
var options = {
48+
'childList': true,
49+
'attributes':true
50+
} ;
51+
52+
observer.observe(article, options);
53+
```
54+
55+
上面代码中,`observe`方法接受两个参数,第一个是所要观察的DOM元素是`article`,第二个是所要观察的变动类型(子节点变动和属性变动)。
56+
57+
观察器所能观察的 DOM 变动类型(即上面代码的`options`对象),有以下几种。
58+
59+
- **childList**:子节点的变动。
60+
- **attributes**:属性的变动。
61+
- **characterData**:节点内容或节点文本的变动。
62+
- **subtree**:所有后代节点的变动。
63+
64+
想要观察哪一种变动类型,就在`option`对象中指定它的值为`true`。需要注意的是,如果设置观察`subtree`的变动,必须同时指定`childList``attributes``characterData`中的一种或多种。
65+
66+
除了变动类型,`options`对象还可以设定以下属性:
67+
68+
- `attributeOldValue`:布尔值,表示观察`attributes`变动时,是否需要记录变动前的属性值。
69+
- `characterDataOldValue`:布尔值,表示观察`characterData`变动时,是否需要记录变动前的值。
70+
- `attributeFilter`:数组,表示需要观察的特定属性(比如`['class','src']`)。
71+
72+
```javascript
73+
// 开始监听文档根节点(即<html>标签)的变动
74+
mutationObserver.observe(document.documentElement, {
75+
attributes: true,
76+
characterData: true,
77+
childList: true,
78+
subtree: true,
79+
attributeOldValue: true,
80+
characterDataOldValue: true
81+
});
82+
```
83+
84+
对一个节点添加观察器,就像使用`addEventListener`方法一样,多次添加同一个观察器是无效的,回调函数依然只会触发一次。但是,如果指定不同的`options`对象,就会被当作两个不同的观察器。
85+
86+
下面的例子是观察新增的子节点。
87+
88+
```javascript
89+
var insertedNodes = [];
90+
var observer = new MutationObserver(function(mutations) {
91+
mutations.forEach(function(mutation) {
92+
for (var i = 0; i < mutation.addedNodes.length; i++)
93+
insertedNodes.push(mutation.addedNodes[i]);
94+
})
95+
});
96+
observer.observe(document, { childList: true });
97+
console.log(insertedNodes);
98+
```
99+
100+
### disconnect(),takeRecords()
101+
102+
`disconnect`方法用来停止观察。调用该方法后,DOM 再发生变动,也不会触发观察器。
103+
104+
```javascript
105+
observer.disconnect();
106+
```
107+
108+
`takeRecords`方法用来清除变动记录,即不再处理未处理的变动。该方法返回变动记录的数组。
109+
110+
```javascript
111+
observer.takeRecords();
112+
```
113+
114+
下面是一个例子。
115+
116+
```javascript
117+
// 保存所有没有被观察器处理的变动
118+
var changes = mutationObserver.takeRecords();
119+
120+
// 停止观察
121+
mutationObserver.disconnect();
122+
```
123+
124+
## MutationRecord 对象
125+
126+
DOM 每次发生变化,就会生成一条变动记录(MutationRecord 实例)。该实例包含了与变动相关的所有信息。Mutation Observer 处理的就是一个个`MutationRecord`实例所组成的数组。
127+
128+
`MutationRecord`对象包含了DOM的相关信息,有如下属性:
129+
130+
- `type`:观察的变动类型(`attribute``characterData`或者`childList`)。
131+
- `target`:发生变动的DOM节点。
132+
- `addedNodes`:新增的DOM节点。
133+
- `removedNodes`:删除的DOM节点。
134+
- `previousSibling`:前一个同级节点,如果没有则返回`null`
135+
- `nextSibling`:下一个同级节点,如果没有则返回`null`
136+
- `attributeName`:发生变动的属性。如果设置了`attributeFilter`,则只返回预先指定的属性。
137+
- `oldValue`:变动前的值。这个属性只对`attribute``characterData`变动有效,如果发生`childList`变动,则返回`null`
138+
139+
## 应用示例
140+
141+
### 子元素的变动
142+
143+
下面的例子说明如何读取变动记录。
144+
145+
```javascript
146+
var callback = function (records){
147+
records.map(function(record){
148+
console.log('Mutation type: ' + record.type);
149+
console.log('Mutation target: ' + record.target);
150+
});
151+
};
152+
153+
var mo = new MutationObserver(callback);
154+
155+
var option = {
156+
'childList': true,
157+
'subtree': true
158+
};
159+
160+
mo.observe(document.body, option);
161+
```
162+
163+
上面代码的观察器,观察`<body>`的所有下级节点(`childList`表示观察子节点,`subtree`表示观察后代节点)的变动。回调函数会在控制台显示所有变动的类型和目标节点。
164+
165+
### 属性的变动
166+
167+
下面的例子说明如何追踪属性的变动。
168+
169+
```javascript
170+
var callback = function (records) {
171+
records.map(function (record) {
172+
console.log('Previous attribute value: ' + record.oldValue);
173+
});
174+
};
175+
176+
var mo = new MutationObserver(callback);
177+
178+
var element = document.getElementById('#my_element');
179+
180+
var options = {
181+
'attributes': true,
182+
'attributeOldValue': true
183+
}
184+
185+
mo.observe(element, options);
186+
```
187+
188+
上面代码先设定追踪属性变动(`'attributes': true`),然后设定记录变动前的值。实际发生变动时,会将变动前的值显示在控制台。
189+
190+
### 取代 DOMContentLoaded 事件
191+
192+
网页加载的时候,DOM 节点的生成会产生变动记录,因此只要观察 DOM 的变动,就能在第一时间触发相关事件,因此也就没有必要使用`DOMContentLoaded`事件。
193+
194+
```javascript
195+
var observer = new MutationObserver(callback);
196+
observer.observe(document.documentElement, {
197+
childList: true,
198+
subtree: true
199+
});
200+
```
201+
202+
上面代码中,监听`document.documentElement`(即HTML节点)的子节点的变动,`subtree`属性指定监听还包括后代节点。因此,任意一个网页元素一旦生成,就能立刻被监听到。
203+
204+
下面的代码,使用`MutationObserver`对象封装一个监听 DOM 生成的函数。
205+
206+
```javascript
207+
(function(win){
208+
'use strict';
209+
210+
var listeners = [];
211+
var doc = win.document;
212+
var MutationObserver = win.MutationObserver || win.WebKitMutationObserver;
213+
var observer;
214+
215+
function ready(selector, fn){
216+
// 储存选择器和回调函数
217+
listeners.push({
218+
selector: selector,
219+
fn: fn
220+
});
221+
if(!observer){
222+
// 监听document变化
223+
observer = new MutationObserver(check);
224+
observer.observe(doc.documentElement, {
225+
childList: true,
226+
subtree: true
227+
});
228+
}
229+
// 检查该节点是否已经在DOM中
230+
check();
231+
}
232+
233+
function check(){
234+
// 检查是否匹配已储存的节点
235+
for(var i = 0; i < listeners.length; i++){
236+
var listener = listeners[i];
237+
// 检查指定节点是否有匹配
238+
var elements = doc.querySelectorAll(listener.selector);
239+
for(var j = 0; j < elements.length; j++){
240+
var element = elements[j];
241+
// 确保回调函数只会对该元素调用一次
242+
if(!element.ready){
243+
element.ready = true;
244+
// 对该节点调用回调函数
245+
listener.fn.call(element, element);
246+
}
247+
}
248+
}
249+
}
250+
251+
// 对外暴露ready
252+
win.ready = ready;
253+
254+
})(this);
255+
256+
ready('.foo', function(element){
257+
// ...
258+
});
259+
```
260+
261+
## 参考链接
262+
263+
- Paul Kinlan, [Detect DOM changes with Mutation Observers](https://developers.google.com/web/updates/2012/02/Detect-DOM-changes-with-Mutation-Observers)
264+
- Tiffany Brown, [Getting to know mutation observers](http://dev.opera.com/articles/view/mutation-observers-tutorial/)
265+
- Michal Budzynski, [JavaScript: The less known parts. DOM Mutations](http://michalbe.blogspot.com/2013/04/javascript-less-known-parts-dom.html)
266+
- Jeff Griffiths, [DOM MutationObserver – reacting to DOM changes without killing browser performance](https://hacks.mozilla.org/2012/05/dom-mutationobserver-reacting-to-dom-changes-without-killing-browser-performance/)
267+
- Addy Osmani, [Detect, Undo And Redo DOM Changes With Mutation Observers](http://addyosmani.com/blog/mutation-observers/)
268+
- Ryan Morr, [Using Mutation Observers to Watch for Element Availability](http://ryanmorr.com/using-mutation-observers-to-watch-for-element-availability/)

0 commit comments

Comments
 (0)