在前端开发的世界里,JavaScript和HTML经常通过事件进行交互。其中大部分是内置事件。本文主要介绍了JS自定义事件的概念和实现,并结合案例详细分析了自定义事件的原理、作用、应用及注意事项。?1。什么是自定义事件在日常开发中,我们习惯于监听页面上的很多事件,比如:点击事件(click)、鼠标移动事件(mousemove)、元素失去焦点事件(blur)等等。事件的本质是一种沟通方式,一种消息。只有当有多个对象和多个模块时,才有可能使用事件进行通信。在多模块开发过程中,可以使用自定义事件进行模块间通信。当一些基础事件不能满足我们的业务时,我们可以尝试用自定义事件来解决。?2。实现方式介绍目前实现自定义事件的方式主要有JS原生Event()构造函数和CustomEvent()构造函数来创建。1.Event()Event()构造函数创建一个新的事件对象Event。1.1语法letmyEvent=newEvent(typeArg,eventInit);1.2参数typeArg:DOMString类型,表示要创建的事件名称;eventInit:可选的配置项,包括:字段名表示是否可选类型默认值bubbles表示事件是否为falseBubble。可选的Booleanfalsecancelable指示事件是否可以取消。可选的布尔值falsecomposed指示事件是否会触发影子DOM根节点之外的侦听器。OptionalBooleanfalse1.3Demoexample//创建一个支持冒泡且不可取消的平安事件letmyEvent=newEvent("pingan",{"bubbles":true,"cancelable":false});document.dispatchEvent(myEvent);//事件可以在任何元素上触发,而不仅仅是documenttestDOM.dispatchEvent(myEvent);1.4兼容性图片来源:https://caniuse.com/2。CustomEvent()CustomEvent()构造函数,创建一个新的事件对象CustomEvent。2.1语法letmyEvent=newCustomEvent(typeArg,eventInit);2.2参数typeArg:DOMString类型,表示创建的事件名称;eventInit:可选的配置项,包括:字段名,表示可选类型默认值detail表示是否需要事件传递的数据在EventListener中获取。可选Anynullbubbles指示事件是否冒泡。可选的Booleanfalsecancelable指示事件是否可以取消。OptionalBooleanfalse2.3Demoexample//创建事件letmyEvent=newCustomEvent("pingan",{detail:{name:"wangpingan"}});//添加合适的事件监听器window.addEventListener("pingan",e=>{alert(`pinganeventistriggeredby${e.detail.name}.`);});document.getElementById("leo2").addEventListener("click",function(){//调度事件窗口。dispatchEvent(myEvent);})我们也可以给自定义事件添加属性:myEvent.age=18;2.4兼容性图片来源:https://caniuse.com/2.5IE8兼容dispatch事件时,需要使用dispatchEvent事件触发,需要兼容IE8及以下的fireEvent方法:if(window.dispatchEvent){window.dispatchEvent(myEvent);}else{window.fireEvent(myEvent);}3.Event()和CustomEvent()的区别从两者支持的参数可以看出Event()适用用于创建简单的自定义事件,而CustomEvent()支持自定义事件传参,支持详细参数,需要在事件Data中传入,在EventListener中获取。注意:当事件触发时,如果对应的元素及其父元素没有被事件监听,则不会执行回调操作。当需要监听子元素时,可以在其父元素上进行事件托管,使得事件在事件冒泡阶段被监听器捕获并执行。此时,您可以使用event.target获取触发事件的具体元素。?三、使用场景事件本质上是一种消息,事件模式本质上是观察者模式的实现,也就是说,可以使用观察者模式的地方,自然也可以使用事件模式。1.场景介绍比如这两个场景:场景一:单个目标对象发生变化,需要通知多个观察者一起变化。例如:当你点击微博列表中的“关注”时,会同时发生很多事情:推荐更多类似的微博,个人关注人数会增加……场景二:解耦多模块协同。例如:小王负责模块A的开发,小陈负责模块B的开发,只有模块A正常运行后才能执行模块B。二、代码实现2.1场景一实现场景一:当单个目标对象发生变化时,需要通知多个观察者一起变化。本例模拟三个页面进行演示:1.微博列表页(Weibo.js)2.粉丝列表页(User.js)3.微博首页(Home.js)在微博列表页(Weibo.js)中,我们导入另外两个页面,监听【关注微博】按钮的点击事件。在回调事件中,创建自定义事件focusUser,并使用文档上的dispatchEvent方法来调度自定义事件。//Weibo.jsimportUserModulefrom"./User.js";importHomeModulefrom"./Home.js";consteventButton=document.getElementById("eventButton");eventButton.addEventListener("click",event=>{constfocusUser=newEvent("focusUser");document.dispatchEvent(focusUser);})接下来两页实现的代码基本一致。为了方便观察,这里设置了不同的输出日志。//User.jsconsteventButton=document.getElementById("eventButton");document.addEventListener("focusUser",event=>{console.log([粉丝列表页]监听自定义事件触发,event:",event);})//Home.jsconsteventButton=document.getElementById("eventButton");document.addEventListener("focusUser",event=>{console.log([微博首页]监听自定义事件触发,event:",event);})点击【关注微博】按钮后,会看到控制台输出如下日志信息:最后微博列表页(Weibo.js)组件负责事件派发,其他组负责监听事件,使得三个组件之间的耦合度很低,完全不需要相互关联,互不影响。其实这也是观察者模式的实现。2.2场景二实现场景二:解耦多模块协作。举一个更直观的例子,当微博需要增加【一键三连】的新功能时,程序员只能在产品原型和UI设计完成后才能进行开发。本例模拟了四个模块:1.流程控制(Index.js)2.产品设计(Production.js)3.UI设计(Design.js)4.流程控制(Index.js)中的程序员开发(Develop.js).js)模块,我们需要导入其他三个进程的模块,然后监听【启动任务】按钮的点击事件。在回调事件中,创建自定义事件startTask,并使用文档上的dispatchEvent方法从Define事件中调度。//Index.jsimportProductionModulefrom"./Production.js";importDesignModulefrom"./Design.js";importDevelopModulefrom"./Develop.js";conststart=document.getElementById("start");start.addEventListener("click",event=>{console.log("Startexecutiontask")conststartTask=newEvent("startTask");document.dispatchEvent(startTask);})Production产品设计模块,监控task事件startTask启动后,模拟1秒后原型设计完成,派发新的事件productionSuccess,开始下一次UI草稿设计。//Production.jsdocument.addEventListener("startTask",()=>{console.log("产品设计开始...");setTimeout(()=>{console.log("产品原型设计完成");console.log("------------");document.dispatchEvent(newEvent("productionSuccess"));},1000);});在UI设计稿和程序开发模块中,其实是类似的,代码实现://Dedign.jsdocument.addEventListener("productionSuccess",()=>{console.log("UI设计稿...");setTimeout(()=>{console.log("UI草稿设计完成");console.log("------------");document.dispatchEvent(newEvent("designSuccess"));},1000);});//Production.jsdocument.addEventListener("designSuccess",function(e){console.log("开始开发函数...");setTimeout(function(){console.log([一键三连】开发完成");},2000)});开发完成后,我们点击【启动任务】按钮,看到控制台输出如下日志信息:最后流程控制(Index.js)模块负责事件派发,其他组件负责监听事件和按照流程完成其他任务。可以看出,原型设计、UI草图设计和程序开发的任务互不影响,很容易扩展任务。4.小结本文详细介绍了JS自定义事件的概念和实现,并结合两个实际场景对代码进行了演示。细心的朋友会发现这两个实际场景都是用Event()构造函数实现的,当然也可以用CustomEvent构造函数代替。此外,本文还详细介绍了这两种实现方式,包括它们的区别和兼容性。最后希望大家在实际开发中能够多思考代码解耦,适当使用自定义事件来提高代码质量。如有错误,请指出。?5。参考文章《javascript自定义事件功能与用法实例分析》《Event - MDN》《CustomEvent - MDN》推荐阅读《1.2w字 | 初中级前端 JavaScript 自测清单 - 1》《了不起的 Webpack 构建流程学习指南》《了不起的 Webpack HMR 学习指南(含源码分析)》《你不知道的 WeakMap》番外篇《你不知道的 Blob》番外篇《了不起的 tsconfig.json 指南》
