被我拖延了将近一个月的javascript事件模型系列终于迎来了第四篇,也是我计划中的最后一篇,说来太惭愧了,本来计划一到两个星期写完的,谁知中间遇到了很多事情,公司的个人的,搞的自己心烦意乱浮躁了一段时间,好在最近这些事情都一件件趋于平息,我也有了精力继续写文章。

  这个自定义事件其实是挺让我纠结的,首先自己平时从未使用过,只是有一次遇到一个问题有人指点说可以用自定义事件,才对这个东西有了印象。在网上搜“javascript自定义事件”,发现也有不少文章在写,不过说实话让我佩服的却一篇也没找到,就连张鑫旭大哥写的漫谈javascript自定义事件也把我看的云里雾里。陆续查阅了一些资料后越发觉得自定义事件这个东西真是个鸡肋,没什么用武之地。这也使我一度想放弃写这篇文章,但后来自己又进行了一些思考,并有了一些新的想法,所以在此还是写出来与大家分享。本来想大手一挥书写标题“javascript自定义事件”,转而一想这个标题真是被用滥了,满大街走的都是长一个样的,谁能认得我呀,亲,我跟他们可真的不太一样哦。所以前面加上了“我所理解的”,并不全是为标题突出,其实真的有很多自己的理解。

一、什么是自定义事件

  这个其实并不难理解,js中有很多的事件,如click(单击)、dbclick(双击)、mouseover(鼠标移上)等等,大部分是一些鼠标或键盘事件,当然也还有其他的如文档的load,只不过我们平时更加关注前者,因为毕竟是要和用户打交道嘛,这些可以统称为DOM事件(都是发生在DOM元素上)。何为自定义DOM事件呢?举个栗子:元素有单击、双击事件,我现在想定义一个三击事件,即元素被连续点击了三次,就给它起名字叫tripleclick。这就是一个自定义的事件。再来个栗子:元素的内部html发生变化,我想监听到此事件,于是可以定义一个htmlchange事件。概念就是这么回事。

  既然有DOM事件,那有没有非DOM事件呢?动脑筋想一想。有了~前段时间刚好研究过history API,其中点击了浏览器后退按钮会触发popstate事件,这就是个非DOM事件。那我们可以自定义些什么非DOM事件呢?再开阔一下思维,javascript好歹也是一门编程语言呀,除了操作DOM、BOM之外还有很多事情可以做呢。比如我写了一个dog对象,也可以给它自定义个事件bark(狗叫),一旦发生了这个事件,我们可以捕捉到,在onbark处理函数中执行一些操作,如把dog给赶走。

  那么,所谓自定义事件就是起个名字?这就完了?先埋个伏笔,后面来谈谈我的看法。

二、如何自定义事件

  关于实现自定义事件的方式,我搜索中文的网页大概也就两种方式,而且就是那么几篇文章被抄来抄去,实在是乏味。总结一下:

  第一种方式是自己模拟一个事件结构,其原理是这样的,我们平时监听事件的时候其实就是一种观察者模式,举个例子吧更明确些。

<input type="button" value="点我" onclick="clickhandler()" />
<script type="text/javascript">
function clickhandler(){
alert('点你怎么了!');
}
</script>

  在这里被观察的主体就是这个button,有一个handler订阅了它的点击事件,当被点击时,button会发布自己被点击的消息,handler接收到消息便开始执行处理函数。是相当标准的一个观察者模式。

  照着这个思路,我们可以把整个过程用代码模拟出来,而不使用浏览器的事件机制,让这个button发布一点其他的消息,比如我们霸气的“三击”,然后写一个handler来监听这个三击事件即可。具体的实现例子我就不写了,因为我觉得这个模拟的办法简直是太土了,根本拿不上台面,想研究的可以看下这篇文章http://www.jb51.net/article/33697.htm 尽管我很恶心脚本之家这种随便剽窃别人文章的行为,但抱歉我真的找不到出处了。。。

  看过了第一种土的掉渣的方式,我们再来看看高端洋气的写法。说白了,其实w3c已经定义了标准的自定义事件写法了。

  第二种方式如下:

var e = document.createEvent('Event');//创建一个Event对象e
e.initEvent('myevent',true,true);//进行事件初始化
var d1 = document.getElementById('d1');//获取DOM元素
d1.addEventListener('myevent',function(event){
alert(‘我监听到了自定义事件’+event.type);
},false);//绑定监听器
d1.dispatchEvent(e);//触发该事件

  使用标准方法还是相当简单的,首先利用document的createEvent方法可以创建一个事件对象,createEvent接收一个参数表示事件的构造器,如Event、MouseEvent、UIEvent、CustomEvent,至于这些事件类都有哪些这里就不详细讲了,你可以查看我之前写的系列,有提到相关内容可以追踪链接。然后使用initEvent函数进行事件的初始化,接收的参数分别表示事件的类型、是否冒泡、是否可以用preventDefault()函数禁止默认行为,在这里你就可以为自定义事件起名字了。然后我们注册监听器并触发事件,这样d1便能监听到自己定义的事件了,ok,就这么简单!

  本来自定义事件的方式就该到此结束了,一个小小的意外,我搜到了一篇国外的文章,看到了如下字样:

  来自mozilla开发者官网,说的就是上面的第二种方式。deprecated?啥意思?google之,藐视的意思!这种方式已经被藐视了哇!竟然还在国内的各网站中被转来转去,国外的同仁正在藐视我们。。。不能忍!赶快看看现在都用什么方式了。

  第三种:

var event = new CustomEvent('build', { 'detail': elem.dataset.time });//区别就在这里~
elem.addEventListener('build', function (e) { ... }, false);
elem.dispatchEvent(event);

  原来是直接创建Event对象,取代了原来的document.createEvent(),而且事件的初始化工作也在这里完成了,不必调用initEvent()了。嗯~不错,是能省一行代码。探讨为什么要这么写也没什么意义,咱跟着国际潮流走就是了。现在已经越来越明显,这个所谓的自定义事件,其实与其他事件是同宗同源,只是名字(类型)不同罢了。

三、自定义事件实例

  了解了这么多,你肯定也和我一样还在困惑,上面的东西都是纸上兵法,这自定义事件到底怎么用我还是不知道。比如我就想要一个tripleclick(三击)事件,具体该如何实现呢?下面就来实践一下,GO~

  自定义事件的步骤我总结为“三板斧”,下面开始操练:

  ①   创建自定义事件

var e = new CustomEvent('tripleclick',{'detail':'somemsg'});//创建自定义事件tripleclick

  ② 在合适的时机触发事件

var counter = 0;
var d1 = document.getElementById('d1');
d1.onclick = function(){
setTimeout(function(){counter=0;},500);
if(++counter==3){
d1.dispatchEvent(e);
}
}

  其实这第二步才是实现tripleclick事件的核心,首先声明一个计数器,每次元素点击便自增,当累计点击三次的时候将事件派发出去,即触发事件。为了防止每次点击之间的间隔时间过长,每次点击后由一个延时函数进行清零,保证只有是连续点击才触发。代码不难理解。我也想在这里说说我的看法,自定义事件不单单是起个自定义名字,还要给这个事件加以描述,定义好它是在什么样的情况下发生

  在这里,tripleclick是依赖于click的,看上去更像是一个逻辑事件,非真正的事件。但由于我们的对象确实是CustomEvent的实例,那它便无疑是一个货真价实的自定义事件。你可能会担心难道我们的自定义事件都要依赖于现有的事件?其实也未必,稍后会写另外一个例子来说明。

  ③   为事件注册监听函数

d1.addEventListener('tripleclick',function(event){
alert(‘我被三击了~’);
},false);

  在此处就可以把我们定义的tripleclick光明正大的写在addEventListener函数中了。

  全部步骤就这些,完整的代码如下:

<div id="d1">有本事点我三次</div>

<script type="text/javascript">
var d1 = document.getElementById('d1');
d1.addEventListener('tripleclick',function(event){
alert('我被三击了~');
},false); var e = new CustomEvent('tripleclick',{'detail':'somemsg'}); var counter = 0;
d1.onclick = function(){
setTimeout(function(){counter=0;},500);
if(++counter==3){
d1.dispatchEvent(e);
}
}
</script>

  你可以轻轻抖动三下手指,点击下面这个嚣张的div:

有本事点我三次

  有点感觉了吧~不过这个三击确实有点小儿科,实际上能派上用场的概率基本为0,下面就来整点有用的东西,我们来实现一个htmlchange事件,即元素的内部html发生变化时触发该事件。这个东西在平时或许还真能用得着。

  首先,事件的创建和监听与上面基本一样

var e2 = new CustomEvent('htmlchange',{'detail':'somemsg'});
d1.addEventListener('htmlchange',function(event){
alert('检测到html发生变化!');
},false);

  然后,我们需要采用一个定时函数来不断检测d1内部的html,当发现与旧值不同时便派发htmlchange事件,代码如下:

var oldhtml = d1.innerHTML;
setInterval(function(){
if(d1.innerHTML!==oldhtml){
d1.dispatchEvent(e2);
       oldhtml = d1.innerHTML;
}
},100);

  三板斧完毕,来看看效果:

我得儿意的飘~

  最慢能在0.1s内作出反应,你也可以把时间设置的更小一些。我们构想一个场景,你写的一个子页面A要被别人的页面B嵌套,并且B会修改A页面中某个元素的内部html,A在无权干涉B中代码的情况下,就可以在自己页面中定义一个htmlchange事件,监听B对A的修改并作出处理。怎么样,体会到自定义事件的威力了吧。

四、谈谈自定义事件的用武之地

  看了很多纸上谈兵的介绍之后我就一直在想,自定义事件的研究和使用为何如此少?它的真正用处到底在哪里?难道真的是javascript中的鸡肋?

  在第三节举出的两个实例或许能说明一点什么,至少它能帮我们扩展一下DOM事件,在当前已有的事件不能满足需求时,可以自己定义一个来使用。其他的用途呢?此时我想到了开篇提到的观察者模式,我们认为浏览器对事件的处理是一种观察者模式,如果反过来呢,我想设计一种观察者模式,是否可以用自定义事件来实现呢?主体需要发布的各种消息通过创建各种自定义事件来实现,对于消息的订阅则通过注册监听器来实现,岂不是利用现有资源便完成了一个观察者模式,而不必再写那么多的代码去模拟。到这里我又情不自禁的想到了node.js,在node中,用事件驱动来完成代码逻辑,其事件是否跟这里的自定义事件如出一辙?只是个猜测,我对node一知半解,真相也不得而知。

  不过至少也可以得出一个结论,自定义事件并非鸡肋,站在设计模式或者是设计一个框架的角度来看,它的特性或许真是处理某类问题的灵丹妙药。

Javascript事件模型系列(四)我所理解的javascript自定义事件的更多相关文章

  1. Paddle Graph Learning (PGL)图学习之图游走类模型[系列四]

    Paddle Graph Learning (PGL)图学习之图游走类模型[系列四] 更多详情参考:Paddle Graph Learning 图学习之图游走类模型[系列四] https://aist ...

  2. (尚031)Vue_案例_自定义事件(组件间通信第2种方式:vue自定义事件)

    自定义事件: 我们知道,父组件使用prop传递数据的子组件,但子组件怎么跟父组件通信呢? 这个时候Vue的自定义事件系统就派得上用场了. 自定义事件知道两件事: (1).绑定 (2).触发 注意:$o ...

  3. Javascript事件模型系列(一)事件及事件的三种模型

    一.开篇 在学习javascript之初,就在网上看过不少介绍javascript事件的文章,毕竟是js基础中的基础,文章零零散散有不少,但遗憾的是没有看到比较全面的系列文章.犹记得去年这个时候,参加 ...

  4. Javascript事件模型系列(三)jQuery中的事件监听方式及异同点

    作为全球最知名的js框架之一,jQuery的火热程度堪称无与伦比,简单易学的API再加丰富的插件,几乎是每个前端程序员的必修课.从读<锋利的jQuery>开始,到现在使用jQuery有一年 ...

  5. Javascript事件模型系列(二)事件的捕获-冒泡机制及事件委托机制

    一.事件的捕获与冒泡 由W3C规定的DOM2标准中,一次事件的完整过程包括三步:捕获→执行目标元素的监听函数→冒泡,在捕获和冒泡阶段,会依次检查途径的每个节点,如果该节点注册了相应的监听函数,则执行监 ...

  6. 关于SWT/JFace的事件模型的四种方式

    事件的4种写法 1.匿名内部类方式的写法 2.命名内部类的写法 3.外部类写法 4.实现监听接口的写法 第一种用匿名内部类的方法: public class HelloWorld { private ...

  7. ECharts 报表事件联动系列四:柱状图,折线图,饼状图实现联动

    代码如下: <!DOCTYPE html> <html> <head> <meta http-equiv="Content-Type" c ...

  8. javascript类继承系列四(组合继承)

    原理: 结合了原型链和对象伪装各自优点的方式,基本思路是:使用原型链继承原型上的属性和方法,使用对象伪装继承实例属性,通过定义原型方法,允许函数复用,并运行每个实例拥有自己的属性 function B ...

  9. 理解的javascript自定义事件

    理解的javascript自定义事件 被我拖延了将近一个月的javascript事件模型系列终于迎来了第四篇,也是我计划中的最后一篇,说来太惭愧了,本来计划一到两个星期写完的,谁知中间遇到了很多事情, ...

随机推荐

  1. 在 Linux 下将 PNG 和 JPG 批量互转的四种方法

    计算机术语中,批处理指的是用一个非交互式的程序来执行一序列的任务[1]的方法.这篇教程里,我们会使用 Linux 命令行工具,并提供 4 种简单的处理方式来把一些 .PNG 格式的图像批量转换成 .J ...

  2. 2. Abstract Factory(抽象工厂)

    意图: 提供一个创建一系列相关或相互依赖对象的接口,而无需指定它们具体的类. 适用性: 一个系统要独立于它的产品的创建.组合和表示时. 一个系统要由多个产品系列中的一个来配置时. 当你要强调一系列相关 ...

  3. OSG配置问题

    BUILD_OSG_ARAPPERS: 这一项要选,不然后面编译自己的测试程序的时候,会报无法打开文件"osgIntrospectiond.lib"错误. Could not fi ...

  4. SQL 订阅发布备注

    单个用户问题 use mastergodeclare @SQL varchar(max)set @SQL=''select @SQL=@SQL+';kill '+RTRIM(spid)from mas ...

  5. Kiwi iOS驱动测试开发

    Kiwi是iOS开发一个行为驱动开发库.目标是提供一个BDD库,精美的简单安装和使用. 安装 使用CocoaPods安装: pod 'Kiwi'如果使用Xcode5则使用: pod 'Kiwi/XCT ...

  6. mysql5.6.34-debug Source distribution编译的几个错误

    raspberrypi下编译mysql5.6 debug版源码. 1. 启动错误 和mysqld相关的文件及文件夹权限必须设置为mysql用户可读可写可执行!谨记! 2. gdb错误 root@: g ...

  7. Java EE 和 Java Web

    什么是 Java Web 应用程序? Java Web 应用程序会生成包含各种类型的标记语言(HTML 和 XML 等)和动态内容的交互式 Web 页.它通常由 Web 组件组成(如 JavaServ ...

  8. android studio 使用 jni 编译 opencv 完整实例 之 图像边缘检测!

    目录: 1,过程感慨: 2,运行环境: 3,准备工作: 4,编译 .so 5,遇到的关键问题及其解决方法 6,实现效果截图. ------------------------------------- ...

  9. 黑马程序员_Java基础:反射机制(Reflection)总结

    ------- android培训.java培训.期待与您交流! ---------- 反射在java中有非常重大的意义,它是一种动态的相关机制,可以于运行时加载.探知.使用编译期间完全未知的clas ...

  10. Inno setup定制安装界面

    Innosetup功能很强大,可以通过它提供的Wizard接口来定制界面,但我对PASCAL语言不熟悉,也不清楚通过那种接口可改动的范围有多大,最后做出来的效果是否好,所以选择了通过一个DLL来实现我 ...