MutationObserver与ResizeObserver
1. 起因
最近在项目中遇到一个问题:需要监听某个dom元素的宽度变化,从而给此dom元素的某个子节点样式赋值。其实起初没有想到这么细,只是想到页面尺寸变化时赋值,即
// 搞个防抖
window.onresize = () => {
return (() => {
setTimeout(() => {
...
}, 100)
})()
}
但是想的太简单了,后来实际操作的时候发现还需要监听dom元素的宽度,这下子有点懵,上网查了一下,发现有个MutationObserver
方法,特此来学习下
3. MutationObserver
举个例子:
<div style="width: 200px" id="parent">
<div id="some-id"></div>
</div>
#some-id{
width: 100%;
height: 100px;
background-color: aqua;
}
var targetNode = document.getElementById('some-id')
var parentNode = document.getElementById('parent')
targetNode.innerHTML = `
<div class="text">批量设置</div>
<input class="input" style="left: 100px;width: 100px" />
<input class="input" style="left: 100px;width: 100px" />
`
// 先创建一个回调函数,mutation.type表示监听变化的类型
var callback = function(mutationsList) {
for(var mutation of mutationsList) {
if (mutation.type == 'attributes') {
console.log('The ' + mutation.attributeName + ' attribute was modified.');
} else if (mutation.type == 'childList') {
console.log('A child node has been added or removed.');
}
}
}
// 创建对象实例
var observer = new MutationObserver(callback)
// config 包含需要监听的类型
var config = { attributes: true, childList: true, subtree: true }
// 开始执行监听,传入监听的dom对象与config
observer.observe(targetNode, config)
// 先注释掉
// 阻止 MutationObserver 实例继续接收的通知
// observer.disconnect()
setTimeout(() => {
parentNode.style.width = '400px'
const inputNodes = targetNode.getElementsByTagName('input')
console.log(inputNodes[0].offsetLeft)
}, 2000)
关于config的配置: 参考
- childList:设置true,表示观察目标子节点的变化,比如添加或者删除目标子节点,不包括修改子节点以及子节点后代的变化
- attributes:设置true,表示观察目标属性的改变
- characterData:设置true,表示观察目标数据的改变
- subtree:设置为true,目标以及目标的后代改变都会观察
- attributeOldValue:如果属性为true或者省略,则相当于设置为true,表示需要记录改变前的目标属性值,设置了attributeOldValue可以省略attributes设置
- characterDataOldValue:如果characterData为true或省略,则相当于设置为true,表示需要记录改变之前的目标数据,设置了characterDataOldValue可以省略characterData设置
- attributeFilter:如果不是所有的属性改变都需要被观察,并且attributes设置为true或者被忽略,那么设置一个需要观察的属性本地名称(不需要命名空间)的列表
可是,跑一下上面的代码,发现并没有执行console.log('The ' + mutation.attributeName + ' attribute was modified.');
明明some-id
宽度都发生变化了。然后我把css的width: 100%
改为100px,再让这个宽度变成200px,发现attributes条件下执行console了,原来config的attributes观察目标属性变化,是指css属性变化,width:100%
,虽然宽度px是变了,但是这个属性没变,所以自然就不执行了
3. ResizeObserver
我觉得MutationObserver这个api在跟我玩文字游戏。百度不中用,科学上网后找到了个ResizeObserver
一看这mdn的介绍:
ResizeObserver 接口可以监听到 Element 的内容区域或 SVGElement的边界框改变
ResizeObserver避免了在自身回调中调整大小,从而触发的无限回调和循环依赖。
它仅通过在后续帧中处理DOM中更深层次的元素来实现这一点。
如果(浏览器)遵循规范,只会在绘制前或布局后触发调用
妥妥就是我要的api了,还是上面的例子
省略...
// 新建obsever对象
const resizeObserver = new ResizeObserver(entries => {
for (let entry of entries) {
console.log(entry.target.offsetWidth)
}
});
开始监听,传入dom对象
resizeObserver.observe(targetNode);
console执行了!打印出父元素宽度变化后targetNode的宽度!!
转载自:https://juejin.cn/post/6844904065059536909