监测DOM元素尺寸大小变化|Vue

10,684 阅读1分钟

小知识,大挑战!本文正在参与“程序员必备小知识”创作活动。

如题,第一反应:

window.addEventListener("resize", handler, useCapture)

简单粗暴,但缺点也明显:文档视图调整大小时会触发 resize 事件,针对的 仅是window,而普通的DOM元素没有 onresize 事件的。

所以,对普通的DOM元素进行尺寸大小变化的监听,抛弃错误的第一感觉,可选择以下方法:

ResizeObserver

ResizeObserver属于Web API

可以监听到 Element 的内容区域或 SVGElement的边界框改变 —— MDN ResizeObserver

通过构造器函数,new ResizeObserver(callback),创建并返回一个ResizeObserver对象,接着:

进行观察

ResizeObserver.observe(target, options)
  • 开始观察指定dom

  • 支持设置观察者将以哪种盒子模型来观察:指定options参数的box属性,可选值有content-box (默认值),border-box,和 device-pixel-content-box。传送门

结束观察

ResizeObserver.disconnect()
  • 取消和结束所有目标dom的观察
ResizeObserver.unobserve(target)
  • 取消和结束指定dom的观察

示例

<script>
export default {
  methods: {
    handleResize() {
      console.log("handle resize");
    }
  },
  mounted() {
    const dom = this.$refs.target.$el;   // 假设this.$refs.target返回是VueComponent对象
    this.observer = new ResizeObserver(this.handleResize);
    this.observer.observe(dom, { box: "border-box" });
  },
  beforeDestroy() {
    this.observer.disconnect();
  }
};
</script>

什么时候触发执行callback的?

  1. 初始化执行一次
  2. 文档视窗改变大小会执行
  3. 元素发现尺寸改变(非2导致)会执行

resize-observer-polyfill

ResizeObserver存在一定的浏览器兼容问题,详见 can i use

npm包的定位是:

A polyfill for the Resize Observer API.

基于MutationObserve或Mutation Events实现:为了解决ResizeObserver API的浏览器兼容问题

(请注意:在 IE10 及更低版本中存在样式问题)

示例

Element UI的走马灯和隐藏组件:滚动条

// https://github.com/ElemeFE/element/blob/dev/src/utils/resize-event.js
import ResizeObserver from 'resize-observer-polyfill';
import { debounce } from 'throttle-debounce';

export const addResizeListener = function(element, fn) {
  if (isServer) return;
  if (!element.__resizeListeners__) {
    element.__resizeListeners__ = [];
    element.__ro__ = new ResizeObserver(debounce(16, resizeHandler));
    element.__ro__.observe(element);
  }
  element.__resizeListeners__.push(fn);
};

// https://github.com/ElemeFE/element/blob/dev/packages/scrollbar/src/main.js
import { addResizeListener, removeResizeListener } from "@/utils/resize-event";

mounted() {
    !this.noresize && addResizeListener(this.$refs.resize, this.update);
}

element-resize-detector

同resize-observer-polyfill,也是可监测DOM元素大小变化的npm包

定位是:

Optimized cross-browser resize listener for elements.

性能有所提升,用法类似ResizeObserver API

开始监听

listenTo(element, listener) 或 listenTo(options, element, listener)

结束监听

removeListener(element, listener) 或 removeAllListeners(element) 或 uninstall(element)

示例

<script>
import elementResizeDetectorMaker from "element-resize-detector";
export default {
  methods: {
    handleResize() {
      console.log("do resize");
    }
  },
  mounted() {
    // Must be a DOM element or a collection of DOM elements.
    const dom = this.$refs.col.$el;

    this.observer = elementResizeDetectorMaker();
    this.observer.listenTo(
      { strategy: "scroll" }, // 基于滚动的情况下,提高性能
      dom,
      this.handleResize
    );
  },
  beforeDestroy() {
    const dom = this.$refs.col.$el;
    this.observer.removeListener(dom, this.handleResize);
  }
};
</script>

vue-resize-observer

如上所述的3种方法,或多或少都需要记忆一些语法

Q:Vue中,为什么不可以是一个自定义指令呢?

A:主角往往在最后出现,使用 vue-resize-observer 即可

注意:已支持Vue 3.x

示例

安装,在入口文件中引用:

import VueResizeObserver from "vue-resize-observer";
Vue.use(VueResizeObserver);
<div class="demo" v-resize="handleResize"></div>
export default {
  methods: {
    handleResize(info) {
      console.log("do resize", info); // info输出:对象,元素当前width、height
    }
  }
};
</script>

什么时候触发执行handleResize?

  1. 初始化不会执行一次
  2. 文档视窗改变大小会执行
  3. 元素发现尺寸改变(非2导致)会执行

链接传送门

如何优雅监听容器高度变化

Last but not least

如有不妥,请多指教呀~