js基础之自定义事件

一、什么是事件?

事件是浏览器与用户交互的主要方式。

举例来说,当用户点击页面上的一个按钮,对浏览器来说,就是在这个按钮上触发了一个“点击事件”。浏览器会去执行开发者为这个“点击事件”定义的回调函数,以此来响应用户的操作。也就是说,浏览器通过“事件”,来感知用户操作,然后执行开发者预定义的处理流程。举个简单的例子:

let btn = document.getElementById("btn"); //获取按钮对象

btn.addEventListener("click", function(){
  ...  //该按钮被点击后,就会执行这里的操作
})

这就是最简单的原生事件模式。当你点击了该按钮,浏览器就会立即执行addEventListener中传入的回调函数。除了click事件,浏览器还定义了大量的原生事件,在控制台输入window,然后回车,以on开头的都是浏览器提供的原生事件(这里只截了其中一部分):
在这里插入图片描述
原生事件并不总与用户操作相关。比如onload,对应的是当前页面加载完毕这个事件。

总的来说,浏览器通过事件系统来监测用户行为和页面状态,然后执行开发者针对这些行为或状态定义的处理流程。

二、自定义事件的实现

事件本质上就是浏览器提供的一种在某个时机执行某些操作的机制。

常见的时机包括点击、键盘输入等,它们都有原生的事件与之对应。但浏览器能提供的原生事件终究是有限的,而开发者所关注的“时机”却可能有很多。比如,我希望当用户鼠标在某个区域停留三秒时执行某个操作,“在某个区域停留三秒”就是一个时机。

然而浏览器并没有为为这个“时机”提供原生事件,怎么办呢?

好在浏览器为我们提供了自定义事件的能力(你也可以在监测到鼠标停留三秒时直接手动执行操作,但是如果有多个模块对该事件感兴趣,你往往不得不在每个模块内写相同的监测逻辑,这就是强耦合的代价)。

我们将一步步演示如何实现这样一个自定义事件。假设我们有下面这样一个div。

<div id="div" style="width: 100px; height: 100px;">
  请将鼠标放在该区域
</div>

然后我们来实现一个自定义事件。

1. 生成事件对象

原生事件在触发时,浏览器会自动封装出一个事件对象,传递给回调函数使用,用以描述与该事件相关的所有细节。自定义事件也需要这样一个对象,不过需要我们用浏览器提供的构造函数手动生成。

用于生成事件对象的构造函数有window.Event和window.CustomEvent,前者用于生成简单事件对象,后者用于生成复杂事件对象。如果该自定义事件不需要携带任何参数,就可以用Event创建一个简单事件对象;如果需要在事件对象中携带参数,就需要使用CustomEvent。两者的用法如下:

let event = new Event("stayFor3Seconds");

let customEvent = new CustomEvent("stayFor3Seconds", {
  ... //在这里添加事件参数
})

我们给这个自定义事件取名为“stayFor3Seconds”,表示鼠标停留三秒。为了展示如何用CustomEvent携带参数,我们在这里用CustomEvent生成事件对象,并携带触发该事件的时间。代码如下:

let customEvent = new CustomEvent("stayFor3Seconds", {
  detail: {
    date: new Date()
  }
})

2. 监听事件

我们需要在监听到该事件后执行某个操作,于是写了下面的代码:

let target = document.getElementById("div");

target.addEventListener("stayFor3Seconds", function(event){
  console.log(event.detail.date);
})

我们在这里监听了“stayFor3Seconds”这个自定义事件,一旦触发了,就在控制台输出事件对象里携带的时间参数(这里的event.detail.date是我们在生成customEvent时写入的)。

3. 触发自定义事件

接下来我们需要实现监测用户鼠标在该div上停留3秒的逻辑,并触发我们的自定义事件。我们通过一个定时器来进行监测:

let div = document.getElementById("div");
let timer;

div.addEventListener("mouseenter", function(){
  //鼠标进入div内,则设置一个3秒的计时器,通过dispatchEvent触发事件
  timer = setTimeout(() => {
    this.dispatchEvent(customEvent);
  }, 3000)
})

//鼠标离开时解除上述定时任务
div.addEventListener("mouseleave", function(){
  clearTimeout(timer);
  timer = null;
})

我们在鼠标进入div时设置一个3秒的计时器。假如用户鼠标在3秒内离开了div,就会触发mouseleave事件,我们会通过clearTimeout来取消自定义事件的触发,否则会通过dispatchEvent语句触发“stayFor3Seconds”事件。

可以看到,只要调用某个DOM元素的dispatchEvent方法,传入一个事件对象,就可以在该元素上触发该事件(window上也有这个方法,通过window触发的通常都是全局事件)。

以上三步综合来看是这样的:

// 1. 生成事件对象
let event = new CustomEvent("event_name", {
  ... //事件参数
});

// 2. 监听事件
target.addEventListener("event_name", function(e){
  ... //处理事件
})

// 3. 触发事件
target.dispatchEvent(event);

三、自定义事件的用途

仅仅三步,就实现了一个自定义事件的触发和监听,是不是非常简单?

虽然简单,但是它的作用却不容小觑。假如现在有三个模块都对“鼠标停留在div上3秒”这个事件感兴趣,如果不借助自定义事件,你要么在三个模块书写同样的监测逻辑,要么就需要向其他模块暴露关于该事件的处理函数。前者违背了软件开发的DRY(Don’t Repeat Yourself,不要重复)原则,后者破坏了模块的封装。如果借助自定义事件,你就可以在一个模块中进行事件监测,然后在其他模块进行监听,模块之间保持独立,并且监测逻辑只编写一次。

在Vue中,这种模式得到了广泛应用。比如:

<my-component
  @stayFor3Seconds="handleStay"
>
</my-component>

这是Vue为组件添加事件监听的最基本方式,组件内部通过以下方式触发自定义事件:

//MyComponent.vue
...
  this.$emit("stayFor3Seconds", args);
...

这里事件的触发对象就是MyComponent组件,父组件向该组件绑定了名为“stayFor3Seconds”的监听,并指定“handleStay”为处理该事件的回调函数。而在组件内部通过$emit来触发该自定义事件,它就类似于下面的实现方式:

let e = new CustomEvent("stayFor3Seconds", {
  data: args
})
this.dispatchEvent(e);

总结

什么是浏览器的事件机制?简单来说就是浏览器在特定时机执行特定操作的机制,每当这个时机到来,我们就称发生了一个事件。

如果浏览器为你关心的那个时机(如用户的点击)提供了原生事件,那么你就可以绑定这个原生事件处理用户操作。否则,就是使用自定义事件的时候了。

发布了37 篇原创文章 · 获赞 90 · 访问量 1万+

猜你喜欢

转载自blog.csdn.net/qq_41694291/article/details/103479727