如何比较优雅的处理元素挂载监听事件

638 阅读2分钟

本文已参与「新人创作礼」活动,一起开启掘金创作之路。

需求背景

在页面开发中,经常需要在一些DOM元素上挂载监听用户的的操作事件,如:鼠标点击、鼠标拖动等等。通常都是使用addEventListener来给元素挂载监听事件,使用removeEventListener取消监听事件。然而,在实际开发中,本人遇到过以下的痛点:

1.无论是挂载还是取消,都需要传入的事件名称和处理函数必须一致,一旦有一个参数传入错误便无法取消监听事件,造成程序错误,有时难以察觉。

2.有的需求需要根据用户的交互操作,切换不同的监听函数,这使得取消监听函数时,也是比较麻烦。

3.监听函数如果使用局部定义的函数或者匿名函数,取消时若是找不到相应的函数,则无法进行取消。

解决方案

解决方案比较简单,可以直接封装一个创建DOM监听事件的函数,同时根据传入的参数,生成对应监听事件的取消函数,在需要取消监听事件的地方调用即可。

技术栈

typeScript

代码

首先,定义创建函数的参数类型:

export interface eventParams {
  // 挂载监听事件的元素,可选,不存在时默认挂载在window
  el?: Element | Window
  // 事件名称,必填
  name: string
  // 处理函数
  listener: EventListener | Function
  // addEventListener 第三个参数,描述事件是冒泡还是捕获,可选
  options?: boolean | AddEventListenerOptions
}

然后,定义创建函数:

export function createListener({
  el = window, // 挂载元素默认为window
  name,
  listener, // 将处理函数命名为listener,本质上还是指向传入处理函数
  options = false
}: eventParams): () => void {
  // DOM元素挂载监听事件
  el.addEventListener(name, listener, options)
  // 生成取消函数,使用了闭包,所以不用担心取消时出错或者找不到,
  const removeListener = () => el.removeEventListener(name, listener, options)
  // 返回取消函数
  return removeListener
}

使用

定义一个全局取消函数,或者在需要调用取消函数的作用域内

let removeEvent = () => {}

在需要的地方挂载监听事件,并将上面定义的取消函数赋值

removeEvent = createListener({
    el: dom,
    name: 'mousemove',
    listener: (event: MouveEvent) => {
        // do something
    }
})

取消时,直接调用取消函数即可:

removeEvent()