参考: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: 100y: 100entry.borderBoxSize:被监听元素的宽高:blockSize: 1000inlineSize: 1443entry.contentBoxSize:被监听元素content部分的宽高:blockSize: 600inlineSize: 1043entry.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 = argumentsif(!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)