什么是观察者?
在深入了解现代浏览器支持的观察者系列之前,让我们先了解一般的观察者是什么?
观察者是一个观察或注意到某事的程序。观察者有助于观察浏览器中发生的某些活动并做出相应的响应。
我们经常处理用 addEventListener 来添加事件监听用户的各种操作,比如:click, mousedown, drag, wheel 等(完整事件列表参考:https://developer.mozilla.org/zh-CN/docs/Web/Events) 。
现代浏览器提供了 5 种 Observer ,使用这些 Observer ,我们可以观察浏览器中发生的不同类型的活动,并采取必要的行动。例如。我们可以观察到,视频是否显示在视口中并启用自动播放,是否已从父 DOM 元素中添加或删除子元素,是否更改了框元素的大小 / 尺寸等等。
以下是现代浏览器支持的五种不同类型的 Observer 。
- Intersection Observer
- Mutation Observer
- Performance Observer
- Resize Observer
- ReportingObserver
[
](https://developer.mozilla.org/zh-CN/docs/Web/API/IntersectionObserver/observe)
Mutation Observer
用于观察 DOM 元素的变化。观察父节点中子节点的添加或删除、属性值或数据内容的变化等变化是很有用的。在 MutationObserver 之前,DOM 变化事件由 Mutation 事件处理,例如 DOMAttrModified, DOMAttributeNameChanged, DOMNodeInserted.
Mutation Observer 被设计为替代 DOM3 事件规范中定义的突变事件。避免突变事件的实际原因是性能问题和跨浏览器支持。
这个新 api 的最大受众可能是构建 JS 框架的人,主要是为了解决问题和创建交互。另一个用例是您正在使用操纵 DOM 的框架并且需要有效地对这些修改做出反应(并且没有 setTimeout hack)。
** 浏览器支持:** 它对不同的浏览器都有很好的支持。
一般来说,MutationObserver 的实现需要三个步骤
a) 创建观察者
只需调用其构造函数并传递处理函数和配置选项即可创建。我们可以选择指定我们想要跟踪或观察什么样的变化。
const config = { | |
childList: true, | |
attributes: true, | |
characterData: true, | |
}; | |
const observer = new MutationObserver(handler); |
childList:true,表示观察子节点相关的变化,attributes:true 表示观察属性变化,characterData:true 表示观察目标元素数据内容的变化。
b) 定义目标对象来观察
observer.observe (…) 方法接受应该被观察的目标元素。
const parent = document.querySelector(“.parent”); | |
observer.observe(parent, config); |
c) 定义回调处理程序
根据观察者创建过程中使用的配置,只要目标元素发生更改,就会执行回调函数。回调函数由突变记录对象触发,其中包含目标元素中发生的突变类型。
function handler(mutationRecords, observer) { | |
mutationRecords.forEach((mutationRecord) => { | |
switch(mutationRecord.type) { | |
case “childList”: //child node added or removed | |
break; | |
case “attributes”: // attribute changed | |
break; | |
default: | |
} | |
}); | |
} |
这里准备了一个示例,每当添加或删除新的子节点以及属性更改时,它都会触发 **MutationObserver ** 的回调处理程序。
示例:
<!DOCTYPE html> | |
<html lang="en"> | |
<head> | |
<meta charset="UTF-8" /> | |
<meta http-equiv="X-UA-Compatible" content="IE=edge" /> | |
<meta name="viewport" content="width=device-width, initial-scale=1.0" /> | |
<title>Document</title> | |
<style> | |
.container { | |
margin: 10px; | |
} | |
.button { | |
background: purple; | |
color: white; | |
cursor: pointer; | |
display: inline-block; | |
font-size: 20px; | |
padding: 1.75rem 1rem; | |
text-align: center; | |
text-decoration: none; | |
font-weight: bold; | |
margin: 5px; | |
} | |
.button:hover { | |
background: #ad1457; | |
} | |
.parent { | |
border: 1px solid #ccc; | |
padding: 10px; | |
} | |
.child { | |
background: #673ab7; | |
color: #fff; | |
padding: 10px; | |
margin-top: 5px; | |
} | |
.infoBoard { | |
padding: 10px; | |
border: 1px solid #ccc; | |
font-size: 20px; | |
padding: 1.75rem 1rem; | |
background: #f4f4f4; | |
} | |
</style> | |
</head> | |
<body> | |
<div class="container"> | |
<div> | |
<div class="button addBtn">Add Child Node</div> | |
<div class="button removeBtn">Remove Child Node</div> | |
<div class="button changeAttrBtn">Change Attribute Value</div> | |
</div> | |
<div class="parent" data-count="0"> | |
<div class="child">Child</div> | |
</div> | |
<div class="infoBoard"></div> | |
</div> | |
<script> | |
if (!("MutationObserver" in window)) { | |
document.body.innerText = "Not supported by your browser"; | |
} | |
const config = { | |
childList: true, | |
attributes: true, | |
characterData: true, | |
}; | |
const observer = new MutationObserver(handler); | |
const parent = document.querySelector(".parent"); | |
observer.observe(parent, config); | |
function handler(mutationRecords, observer) { | |
console.log("Handle mutation"); | |
mutationRecords.forEach((mutationRecord) => { | |
const info = document.querySelector(".infoBoard"); | |
switch (mutationRecord.type) { | |
case "childList": | |
info.innerText = | |
"Mutation Observer handler: Child node added or removed"; | |
break; | |
case "attributes": | |
info.innerText = `Mutation Observer handler: Parent attribute modified: ${mutationRecord.attributeName} : ${mutationRecord.target.children.length}`; | |
break; | |
default: | |
info.innerText = ""; | |
} | |
}); | |
} | |
document.querySelector(".addBtn").addEventListener("click", () => { | |
console.log("Add child"); | |
const child = document.createElement("div"); | |
child.className = "child"; | |
child.innerText = "Child "; | |
parent.appendChild(child); | |
}); | |
document.querySelector(".removeBtn").addEventListener("click", () => { | |
console.log("Remove child"); | |
const child = parent.firstChild; | |
parent.removeChild(child); | |
}); | |
/* Change attribute value when clicked on change attribute button */ | |
document.querySelector(".changeAttrBtn").addEventListener("click", () => { | |
console.log("Change attribute value"); | |
parent.setAttribute("data-count", parent.childNodes.length); | |
}); | |
</script> | |
</body> | |
</html> |