目录
前言
一、addEventListener
1.示例
二、MutationObserver
1.示例1
2.示例2
总结
参考资料
前言
今天项目遇到一个需要监听DOM变化,根据DOM的变化情况,来展示不同的内容,于是上网搜索了各种方式,做一下总结,方便下次查看。
一、addEventListener
关于这个API不要查看菜鸟教程中的解释,直接看MDN即可
- addEventListener函数说明 :https://developer.mozilla.org/zh-CN/docs/Web/API/EventTarget/addEventListener
- addEventListener事件参考:事件参考 | MDN
1.示例
对于DOMNodeInserted事件,我们只需要知道使用函数document.addEventListener传递第一个参数为字符串DOMNodeInserted,第二个参数为事件处理函数。
事件处理函数的第一个参数为事件对象,其中属性target为被插入的对象,根据该对象的各种属性,我们执行我们的各种操作(禁用、删除、增加等等)。
代码如下:
<body>
<table id="outside">
<tr>
<td id="t1">one</td>
</tr>
<tr>
<td id="t2">two</td>
</tr>
</table>
<script>
// 改变 t2 的函数
function modifyText() {
var t2 = document.getElementById("t2");
if (t2.firstChild.nodeValue == "three") {
t2.firstChild.nodeValue = "two";
} else {
t2.firstChild.nodeValue = "three";
}
}
// 为 table 添加事件监听器
var el = document.getElementById("outside");
el.addEventListener("click", modifyText, false);
</script>
</body>
二、MutationObserver
参考文档MDN MutationObserver - Web API 接口参考 | MDN。
基础核心:
- MutationAbserver
- MutationAbserverInit
- attributes: true 【是否监听属性变化】
- attributeFilter: ['popkey'] | undefined 【监听的属性范围,如果设置为undefined | 没有设置表示监听全部的属性】
- attibuteOldValue: true 【是否把 回调函数中的MutationRecord.oldValue 对象是否包含了更改前的数据
- characterData: true 【是否监听 内部子文本节点的数据发生变化】
- characterDataOldValue: true 【是否把 之前的数据发送给 MutationRrecord】
- childList: true,【是否 监听子节点的插入/删除】
- subtree: true 【是否 把监听的范围扩展到内部的全部子孙节点】
- MutationRecor
1.示例1
代码如下:
<body>
<div id="test"></div>
<script>
const targetNode = document.getElementById('test');
// 观察器的配置(需要观察什么变动)
const config = { attributes: true, childList: true, subtree: true };
// 当观察到变动时执行的回调函数
const callback = function (mutationsList, observer) {
for (let mutation of mutationsList) {
if (mutation.type === 'childList') {
console.log('有节点发生改变,当前节点的内容是:');
console.log(item.target.innerHTML);
}
else if (mutation.type === 'attributes') {
console.log('修改了'+item.attributeName+'属性');
}
}
};
// 创建一个观察器实例并传入回调函数
const observer = new MutationObserver(callback);
// 以上述配置开始观察目标节点
observer.observe(targetNode, config);
// 之后,可停止观察
// observer.disconnect();
</script>
</body>
效果如下图所示:
2.示例2
<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>mutationObserver</title>
</head>
<body>
<div class='parent'>
原始文本
</div>
<script type="text/javascript">
const el_class_parent = document.querySelector('.parent');
const el_div = document.createElement('div')
// 文本节点的data就是文本的内容
const node_text = el_class_parent.childNodes[0]
// observer 观察者
// 创建一个观察者
const observer = new MutationObserver(callback)
// 事件处理器
function callback(mutationRecords, observer){
mutationRecords.forEach(mutationRecord => {
console.dir(mutationRecord)
if(mutationRecord.type === "attributes") return console.log('属性发生了变化 target =', mutationRecord.target)
if(mutationRecord.type === 'childList') return console.log('添加or删除了 childList: ',mutationRecord.target)
if(mutationRecord.type === 'characterData') return console.log('文本节点的数据发生了变化',mutationRecord.target)
})
}
/*
* 配置文件
*/
const config = {
attributes: true,
attributeFilter: undefined, /*需要监听的属性名称列表,如果没有表示监听全部的属性*/
attributeOldValue: true, /*传递之前旧的值给mutationRecord*/
characterData: true, /*是否监听内部文本节点的数据变化*/
characterDataOldValue: true, /*mutationRecord 是否包含内部文本节点变化前的数据*/
childList: true,
subtree: true /*是否把监听的方位放到节点树中的全部子节点上*/
}
observer.observe(el_class_parent, config)
// 监听内部文本节点的数据变化characterData
setTimeout(() => {
node_text.data = 'aaa'
}, 0)
// 如果在一个事件循环内设置多个变化
// 会在callback中接受到多个 mutationRecord
setTimeout(() => {
Promise
.resolve()
.then(() => {
console.log('-------------promise 队列执行')
el_class_parent.setAttribute('path', "promise")
})
// 设置属性发生变化
el_class_parent.setAttribute('path', 'old-path');
el_class_parent.setAttribute('path', 'new-path')
// 设置内容发生变化
el_class_parent.innerText = "aaa"
// 每一次的设置都会调用 callback
el_class_parent.append(el_div)
console.error('----------------------设置更改完毕')
setTimeout(() => {
console.log('--------------------------------下一次事件循环')
el_class_parent.setAttribute('path', 'new-path-2')
},0)
setTimeout(() => {
console.log('--------------------------- 下一次事件循环')
el_div.setAttribute('path', 'div-path')
}, 0)
}, 1000)
</script>
</body>
</html>
总结
addEventListener是W3C
的API,历史遗留接口,很多问题,整体来说不推荐使用
。
MutationObserver是H5
的API,从设计和兼容性上都要好的多,如果在开发中,使用该api更合适。
DOM MutationObserver在不影响浏览器性能的状态下响应DOM更改
等待全部脚本任务执行完成后,才会运行,即采用异步方式
不过在Chrome测试中发现,addEventListener有些状态监控不到,MutationObserver可以弥补这种情况。而MutationObserver像是消息队列的实现方式,不能查看运行时堆栈。所以开发过程中根据需要使用不同的api,也可以同时使用两套api达到目的。
参考资料
javascript 监听DOM内容改变事件 https://juejin.cn/post/7025877062430752775
书籍《JavaScript框架设计》https://book.douban.com/subject/27133542/
Chrome设置断点的各种姿势 https://cloud.tencent.com/developer/article/1093731
参考文章:https://blog.csdn.net/kinghzking/article/details/123684377