被我拖延了将近一个月的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. Docker-3:Data Volume

    Sometimes,  applications need to share access to data or persist data after a container is deleted. ...

  2. C++写一个带参数运行的程序

    #include <string.h>#include <iostream>#include <cstdlib>using namespace std; int m ...

  3. 如何让电脑公司Win7系统自动关闭停止响应的程序

    在注册表编辑器窗口左侧,依次展开HKEY_CURRENT_USER\ControlPanel\Desktop,选中Desktop,在右边的窗口中选择AutoEndTasks,双击打开AutoEndTa ...

  4. Android手机编程初学遇到的问题及解决方法

    对高手来讲不值一提,可是对我这个初学来讲却是因为这些问题费了老长时间,有的不是编程问题,但不注意也会浪费不少宝贵时间!随时遇到随时更新... 引入第三方类库的问题,开始引用后没什么问题,但发现了该类库 ...

  5. 自动生成Model层中对应表的各个字段

    select 'public '+ case t.name when 'varchar' then 'string' when 'smallint' then 'Int16' when 'int' t ...

  6. MySqlHelper

    package utils; import java.io.IOException; import java.sql.CallableStatement; import java.sql.Connec ...

  7. KMP详解

    原文: http://blog.csdn.net/v_july_v/article/details/7041827 从头到尾彻底理解KMP 1. 引言 本KMP原文最初写于2年多前的2011年12月, ...

  8. H5中的拖拽事件

    最近浏览了张鑫旭大神的基于HTML5 drag/drop模块拖动插入排序删除完整实例,感觉受益匪浅.于是将最做的demo记录下来. 首先浏览一下事件,这些事件比较好记,只要记住用在谁的身上就好了,无非 ...

  9. python 使用字符串名调用类以及调用类方法名

    在python中,有时调用者仅知道类名和类方法,不负责实际的函数调用,而是将要调用的类名和类方法告诉一个中间函数,由中间函数负责实际调用函数.中间函数需以被告知的字符串调用类和类方法.         ...

  10. shell脚本编译安装LAMP环境

    #filename lamp.sh#version Centos6.7;apache2.4.23;mariadb-5.5.40;php5.5.38#data 2016/09/28#mail 23853 ...