我们使用 CustomEvent 接口的事件可用于传送自定义数据。
它的内容其实很少,我们一起来了解了解吧。
(1)event = new CustomEvent(type [, eventInitDict])
类似于 Event 事件的构造函数进行工作,除了可选的 eventInitDict 参数现在还允许设置 detail 属性。
其中 detail 属性必须返回其被初始化的值。
事件不仅告诉我们什么时候发生交互,而且事件告诉我们交互类型,涉及的节点,以及为我们提供了处理事件的方法。创建自定义事件并触发它们总是很棘手。使用 JavaScript 的 CustomEvent API,可以消除该欺骗。CustomEvent API 允许开发人员不仅创建自定义事件,而且在 DOM 节点上触发它们,同时传递数据。最重要的是,API 超级简单!
使用自定义事件时,“自定义”事件包含两个组件:自定义事件名称和触发该事件的能力。但是,向元素添加事件监听器仍旧保持不变:
myElement.addEventListener("userLogin", function(e) {
console.info("Event is: ", e);
console.info("Custom data is: ", e.detail);
})
这里我们添加了一个 userLogin 事件,就像我们可以添加一个 mouseover
或 focus
事件一样容易,这没有特别的地方。其特殊的部分是创建和触发事件:
// 首先创建一个事件
let myEvent = new CustomEvent("userLogin", {
detail: {
username: "davidwalsh"
}
});
// 触发它!
myElement.dispatchEvent(myEvent);
触发后的结果如下所示:
CustomEvent 构造函数允许开发人员创建自定义事件,允许您传递自定义事件名称以及一些特殊属性;而 dispatchEvent
触发给定元素上的事件。让我们通过配置其 bubbles,cancelable,detail 属性来触发超级定制(super-customized)的事件:
let myEvent = new CustomEvent("userLogin", {
detail: {
username: "davidwalsh"
},
bubbles: true,
cancelable: false
});
使用自定义数据创建和触发自定义事件非常有用。您不仅可以为事件创建自己的命名约定,而且还可以沿途传递自定义数据!
此 API 的兼容性如下图:
可以看到现代浏览器基本都支持了这个自定义的事件。
当然,我知道,你肯定还是不懂,没关系。手把手地再实现另一个案例吧。
首先,我们先创建一个 form 表单,它拥有一个 input 框,以及一个 button 按钮。
但是我们的事件有一个缺点,那就是:事件会与 DOM 元素不可分割地链接在一起。
const msgBox = document.querySelector('#msgBox');
msgBox.addEventListener('submit',(e) => {
e.preventDefault();
let msg = e.currentTarget.getElementById('msg').value.trim();
if (msg) {
alert(msg);
}
})
当然,虽然我不知道为什么这个代码会有问题,但是先不管这个了。
我们主要突出的是下面的问题:
- 向现有处理程序中添加其他代码
这是不灵活的,因为我们需要在每次添加、更改或删除功能时都会更新和测试我们的处理函数。可能有几十个用于发布的消息,我们正试图在同一个代码块中应用它们。
- 为每次使用创建其他事件处理程序
这将获得更优雅的代码,但导致维护问题。首先,每个函数必须执行类似的操作来提取和验证消息。如果我们需要改变我们的形式怎么办?简单地重命名 ID 将需要我们改变每个订阅者的事件处理代码。
如果我们可以在有效消息发布时简单地提交一个自定义的 “newMessage” 事件,这不是很好吗? 如果我们可以简单地监视 document 或 body 标签,而不是引用特定的表单节点,则会更好。 这正是自定义事件允许我们做的。
创建它很简单,我们将名称,详细信息和选项传递给一个新的 CustomEvent 对象:
const event = new CustomEvent('newMessage', {
detail: {
message: 'Hello World',
time: new Date(),
},
bubbles: true,
cancelable: true,
}
);
在此示例中,“newMessage” 是自定义事件类型。第二个参数是一个具有三个属性的对象:
- detail:提供有关事件的自定义信息的子对象。在此示例中,我们添加了一条消息和时间。
- bubbles:如果为 true,事件将冒泡到触发事件的元素的祖先。
- cancelable:如果为 true,可以使用事件对象的 stopPropagation() 方法取消事件传播。
现在,我们需要在特定元素上分配此事件,例如:
const msgBox = document.querySelector('#msgBox');
msgBox.dispatchEvent(event);
现在我们的自定义事件类型 “newMessage” 就被分配到 form 表单上了。
但是,我们还没有为其添加事件监听呢,因此它现在是无法工作的。那么现在,我们就给其添加上事件监听:
msgBox.addEventListener('submit',SendMessage);
现在,我们的 button 按钮有了事件监听,当表单提交时,就会触发 SendMessage 回调函数(此函数为前面的自定义事件的完整代码):
function SendMessage (e) {
e.preventDefault();
let msg = document.getElementById("msg").value.trim();
if (msg && window.CustomEvent) {
let event = new CustomEvent("newMessage", {
detail: {
message: msg,
time: new Date(),
},
bubbles: true,
cancelable: true
});
e.currentTarget.dispatchEvent(event);
}
}
此时,我们就完成了一半了。我知道你会说:“啊!?才一半啊!”
没错,我们现在给我们的 form 表单设置了回调函数,在提交表单时就会触发 SendMessage。但是,我们发送(send)出去的信息是什么呢?
因此,我们接着完成我们的事件监听,在 form 表单上我们还可以定义一个事件监听来返回我们 input 中键入的信息:
function newMessageHandler(e) {
console.log(`Event subscriber on ${e.currentTarget.nodeName},
${e.detail.time.toLocaleString()}, message: ${e.detail.message}`);
}
document.addEventListener("newMessage", newMessageHandler);
注释:如果你不了解我上面 console.log() 中的 `` 这个 ES6 中的模板字符串写法,请去我的 ECMAScript 系列中的 String 类型中寻找答案。
好了,现在我们就完成了我们的全部代码了。
结果如下:
这个例子很长,比第一个例子难很多。因此我带着你们再回顾一遍:
首先,我们的需求是为 form 表单提供一个事件监听,使其能够返回我们需要的参数,假如我们不使用自定义事件,那么我们就会有很多麻烦,比如在这一个代码块中监听事件,假如我们需要改需求,那么我们是不是就又要重新定义一个事件监听呢?假如我们不在该 form 中调用,换做其他的 form 呢?我们这个代码就与我们的 #msgBox
具有强耦合的关系,这不利于代码复用。
而现在,我们将其自定义为 newMessage 事件类型,我们想在哪一个 form 上注册事件,只需要使用 dispatchEvent() 方法,将其注册到另一个 form 中即可;而如果我们需要改变我们的需求,也只需要修改自定义事件中的代码就可以了,再将其放入另一个代码块中,也就轻易地实现了代码复用的原则。
好了,本节就到此结束。如果你有问题,请再拉到开头重新阅读一遍!!!请注意:这不是你的理解能力上的问题,而是需要反复理解其中的细节之处。