参考:ResizeObserver API详解-CSDN博客
有的时候需要监听某个元素的宽高变化,这个时候可以使用JS的 resizeObserver 钩子函数。
用于监视元素的大小变化。它可以观察一个或多个 DOM 元素,以便在元素的大小或形状发生变化时触发回调函数。ResizeObserver 是为了更有效地处理元素尺寸变化而引入的,特别适用于响应式设计和自适应布局。
观察多个元素:你可以同时观察多个 DOM 元素,当这些元素的尺寸发生变化时,ResizeObserver 会在每个元素上触发回调函数。
异步回调:ResizeObserver 的回调是异步执行的,这意味着它会在浏览器准备好更新时触发,而不会引发性能问题。
兼容性:
完全不兼容ie浏览器,主流浏览器只兼容高版本,基本上2018年以前的浏览器都不兼容,部分浏览器甚至2020年以前都不兼容。
ResizeObserver是个构造函数。在使用new关键字调用构造函数,返回实例对象时,需要传入一个回调函数,这个回调用于监听实例对象某个DOM节点的变化。
回调函数的第一个参数entries是一个数组,数组里有几个对象。
entry.contentRect:被监听元素content的宽高及位置:
bottom: 700 指top + height的值
height: 600 指元素本身的高度,不包含padding,border值
left: 100 指padding-left的值
right: 1143 指left + width的值
top: 100 指padidng-top的值
width: 1043 指元素本身的宽度,不包含padding,border值
x: 100
y: 100
entry.borderBoxSize:被监听元素的宽高:
blockSize: 1000
inlineSize: 1443
entry.contentBoxSize:被监听元素content部分的宽高:
blockSize: 600
inlineSize: 1043
entry.target:被监听的元素
// 监听el-form的宽高
createResizeObserver(selector) {
const wrapper = this.$el.querySelector(selector);
this.observer = new ResizeObserver((entries) => {
entries.forEach((entry) => {
this.totalWidth = entry.contentRect.width; //被监听元素宽
this.totalHeight = entry.contentRect.height;//被监听元素高
});
resolve()
});
this.observer.observe(wrapper);
}
this.createResizeObserver("#searchLeft1")
因为 ResizeObserver是异步的,所以会暂时被放到异步任务队列中,等待同步执行完毕后,才回去执行异步函数。如果需要在被监听的元素变化后执行某些操作,可以使用promise处理,如下:
createResizeObserver(selector) {
return new Promise(resolve => {
const wrapper = this.$el.querySelector(selector);
this.observer = new ResizeObserver((entries) => {
entries.forEach((entry) => {
// for (const entry of entries) {
this.totalWidth = entry.contentRect.width;
this.totalHeight = entry.contentRect.height;
this.$store.commit("teachResearch/SET_ACTIVEHEIGHT", this.totalHeight); //存储查询条件动态高
// }
});
resolve()//注意resolve的位置
});
this.observer.observe(wrapper);
})
},
this.createResizeObserver("#searchLeft1").then(()=>{
// 监听元素大小后,要执行的某些操作
//XXXXX
})
取消和结束对目标元素的监听
vue2的写法:
beforeDestroy() {
resizeObserver.disconnect(this.$refs.wrapper);
},
vue3的写法:
beforeUnmount() {
resizeObserver.disconnect(this.$refs.wrapper);
}
react的写法:
useEffect(() => {
return () => {
resizeObserver.disconnect(this.$refs.wrapper);
}
},[])
componentWillUnmount() {
resizeObserver.disconnect(this.$refs.wrapper);
}
节流使用
最后,在使用ResizeObserver API的时候,在每次触发元素的大小变化时,会在1s内触发回调蛮多次的。如果想进一步优化性能,可以加上throttle节流函数处理
const throttle = (fun,delay) => {
let timer = null;
return function() {
const args = arguments
if(!timer) {
timer = setTimeout(() => {
timer = null
}, delay)
fun(args )
}
}
}
const myObserver = new ResizeObserver(throttle(entries => {
entries.forEach(entry => {
console.log('大小位置 contentRect', entry.contentRect)
console.log('监听的DOM target', entry.target)
})
}), 500)
myObserver.observe(document.body)