详解JavaScript中的自定义事件编写


Posted in Javascript onMay 10, 2016

我们可以自定义事件来实现更灵活的开发,事件用好了可以是一件很强大的工具,基于事件的开发有很多优势(后面介绍)。

与自定义事件的函数有 Event、CustomEvent 和 dispatchEvent。

直接自定义事件,使用 Event 构造函数:

var event = new Event('build');

// Listen for the event.
elem.addEventListener('build', function (e) { ... }, false);

// Dispatch the event.
elem.dispatchEvent(event);

CustomEvent 可以创建一个更高度自定义事件,还可以附带一些数据,具体用法如下:

var myEvent = new CustomEvent(eventname, options);

其中 options 可以是:

{
  detail: {
    ...
  },
  bubbles: true,
  cancelable: false
}

其中 detail 可以存放一些初始化的信息,可以在触发的时候调用。其他属性就是定义该事件是否具有冒泡等等功能。

内置的事件会由浏览器根据某些操作进行触发,自定义的事件就需要人工触发。dispatchEvent 函数就是用来触发某个事件:

element.dispatchEvent(customEvent);

上面代码表示,在 element 上面触发 customEvent 这个事件。结合起来用就是:

// add an appropriate event listener
obj.addEventListener("cat", function(e) { process(e.detail) });

// create and dispatch the event
var event = new CustomEvent("cat", {"detail":{"hazcheeseburger":true}});
obj.dispatchEvent(event);

使用自定义事件需要注意兼容性问题,而使用 jQuery 就简单多了:

// 绑定自定义事件
$(element).on('myCustomEvent', function(){});

// 触发事件
$(element).trigger('myCustomEvent');
此外,你还可以在触发自定义事件时传递更多参数信息:

$( "p" ).on( "myCustomEvent", function( event, myName ) {
 $( this ).text( myName + ", hi there!" );
});
$( "button" ).click(function () {
 $( "p" ).trigger( "myCustomEvent", [ "John" ] );
});

JavaScript 自定义事件就是有别于如 click, submit 等标准事件的自行定制的事件,在叙述自定义事件有何好处之前,先来看一个自定义事件的例子:

<div id="testBox"></div>

// 创建事件
var evt = document.createEvent('Event');
// 定义事件类型
evt.initEvent('customEvent', true, true);
// 在元素上监听事件
var obj = document.getElementById('testBox');
obj.addEventListener('customEvent', function(){
  console.log('customEvent 事件触发了');
}, false);

具体效果可以查看 Demo,在 console 中输入 obj.dispatchEvent(evt),可以看到 console 中输出“customEvent 事件触发了”,表示自定义事件成功触发。

在这个过程中,createEvent 方法创建了一个空事件 evt,然后使用 initEvent 方法定义事件的类型为约定好的自定义事件,再对相应的元素进行监听,接着,就是使用 dispatchEvent 触发事件了。

没错,自定义事件的机制如普通事件一样——监听事件,写回调操作,触发事件后执行回调。但不同的是,自定义事件完全由我们控制触发时机,这就意味着实现了一种 JavaScript 的解耦。我们可以把多个关联但逻辑复杂的操作利用自定义事件的机制灵活地控制好。

当然,可能你已经猜到了,上面的代码在低版本的 IE 中并不生效,事实上在 IE8 及以下版本的 IE 中并不支持 createEvent(),而有 IE 私有的 fireEvent() 方法,但遗憾的是,fireEvent 只支持标准事件的触发。因此,我们只能使用一个特殊而简单的方法触发自定义事件。

// type 为自定义事件,如 type = 'customEvent',callback 为开发者实际定义的回调函数
obj[type] = 0;
obj[type]++;
 
obj.attachEvent('onpropertychange', function(event){
  if( event.propertyName == type ){
    callback.call(obj);
  }
});

这个方法的原理实际上是在 DOM 中增加一个自定义属性,同时监听元素的 propertychange 事件,当 DOM 的某个属性的值发生改变时就会触发 propertychange 的回调,再在回调中判断发生改变的属性是否为我们的自定义属性,若是则执行开发者实际定义的回调。从而模拟了自定义事件的机制。

为了使到自定义事件的机制能配合标准事件的监听和模拟触发,这里给出一个完整的事件机制,这个机制支持标准事件和自定义事件的监听,移除监听和模拟触发操作。需要注意的是,为了使到代码的逻辑更加清晰,这里约定自定义事件带有 'custom' 的前缀(例如:customTest,customAlert)。

/**
 * @description 包含事件监听、移除和模拟事件触发的事件机制,支持链式调用
 *
 */
 
(function( window, undefined ){
 
var Ev = window.Ev = window.$ = function(element){
 
  return new Ev.fn.init(element);
};
 
// Ev 对象构建
 
Ev.fn = Ev.prototype = {
 
  init: function(element){
 
    this.element = (element && element.nodeType == 1)? element: document;
  },
 
  /**
   * 添加事件监听
   * 
   * @param {String} type 监听的事件类型
   * @param {Function} callback 回调函数
   */
 
  add: function(type, callback){
 
    var _that = this;
     
    if(_that.element.addEventListener){
       
      /**
       * @supported For Modern Browers and IE9+
       */
       
      _that.element.addEventListener(type, callback, false);
       
    } else if(_that.element.attachEvent){
       
      /**
       * @supported For IE5+
       */
 
      // 自定义事件处理
      if( type.indexOf('custom') != -1 ){
 
        if( isNaN( _that.element[type] ) ){
 
          _that.element[type] = 0;
 
        } 
 
        var fnEv = function(event){
 
          event = event ? event : window.event
           
          if( event.propertyName == type ){
            callback.call(_that.element);
          }
        };
 
        _that.element.attachEvent('onpropertychange', fnEv);
 
        // 在元素上存储绑定的 propertychange 的回调,方便移除事件绑定
        if( !_that.element['callback' + callback] ){
     
          _that.element['callback' + callback] = fnEv;
 
        }
    
      // 标准事件处理
      } else {
    
        _that.element.attachEvent('on' + type, callback);
      }
       
    } else {
       
      /**
       * @supported For Others
       */
       
      _that.element['on' + type] = callback;
 
    }
 
    return _that;
  },
 
  /**
   * 移除事件监听
   * 
   * @param {String} type 监听的事件类型
   * @param {Function} callback 回调函数
   */
   
  remove: function(type, callback){
 
    var _that = this;
     
    if(_that.element.removeEventListener){
       
      /**
       * @supported For Modern Browers and IE9+
       */
       
      _that.element.removeEventListener(type, callback, false);
       
    } else if(_that.element.detachEvent){
       
      /**
       * @supported For IE5+
       */
       
      // 自定义事件处理
      if( type.indexOf('custom') != -1 ){
 
        // 移除对相应的自定义属性的监听
        _that.element.detachEvent('onpropertychange', _that.element['callback' + callback]);
 
        // 删除储存在 DOM 上的自定义事件的回调
        _that.element['callback' + callback] = null;
      
      // 标准事件的处理
      } else {
      
        _that.element.detachEvent('on' + type, callback);
      
      }
 
    } else {
       
      /**
       * @supported For Others
       */
       
      _that.element['on' + type] = null;
       
    }
 
    return _that;
 
  },
   
  /**
   * 模拟触发事件
   * @param {String} type 模拟触发事件的事件类型
   * @return {Object} 返回当前的 Kjs 对象
   */
   
  trigger: function(type){
 
    var _that = this;
     
    try {
        // 现代浏览器
      if(_that.element.dispatchEvent){
        // 创建事件
        var evt = document.createEvent('Event');
        // 定义事件的类型
        evt.initEvent(type, true, true);
        // 触发事件
        _that.element.dispatchEvent(evt);
      // IE
      } else if(_that.element.fireEvent){
         
        if( type.indexOf('custom') != -1 ){
 
          _that.element[type]++;
 
        } else {
 
          _that.element.fireEvent('on' + type);
        }
    
      }
 
    } catch(e){
 
    };
 
    return _that;
       
  }
}
 
Ev.fn.init.prototype = Ev.fn;
 
})( window );
测试用例1(自定义事件测试)

// 测试用例1(自定义事件测试)
// 引入事件机制
// ...
// 捕捉 DOM
var testBox = document.getElementById('testbox');
// 回调函数1
function triggerEvent(){
    console.log('触发了一次自定义事件 customConsole');
}
// 回调函数2
function triggerAgain(){
    console.log('再一次触发了自定义事件 customConsole');
}
// 封装
testBox = $(testBox);
// 同时绑定两个回调函数,支持链式调用
testBox.add('customConsole', triggerEvent).add('customConsole', triggerAgain);

完整的代码在 Demo。

打开 Demo 后,在 console 中调用 testBox.trigger('customConsole') 自行触发自定义事件,可以看到 console 输出两个提示语,再输入 testBox.remove('customConsole', triggerAgain) 移除对后一个监听,这时再使用 testBox.trigger('customConsole') 触发自定义事件,可以看到 console 只输出一个提示语,即成功移除后一个监听,至此事件机制所有功能正常工作。

Javascript 相关文章推荐
excel操作之Add Data to a Spreadsheet Cell
Jun 12 Javascript
扩展js对象数组的OrderByAsc和OrderByDesc方法实现思路
May 17 Javascript
浅析JavaScript中的CSS属性及命名规范
Nov 28 Javascript
node.js中的console.error方法使用说明
Dec 10 Javascript
JavaScript获得url所有参数键值表的方法
Mar 21 Javascript
以Python代码实例展示kNN算法的实际运用
Oct 26 Javascript
jQuery操作基本控件方法实例分析
Dec 31 Javascript
解析预加载显示图片艺术
Dec 05 Javascript
js实现年月日表单三级联动
Apr 17 Javascript
JSONP基础知识详解
Mar 19 Javascript
详解vue-resource promise兼容性问题
Jun 20 Javascript
解决使用Vue.js显示数据的时,页面闪现原始代码的问题
Feb 11 Javascript
BootstrapTable与KnockoutJS相结合实现增删改查功能【一】
May 10 #Javascript
JS组件系列之Bootstrap table表格组件神器【二、父子表和行列调序】
May 10 #Javascript
解决jquery无法找到其他父级子集问题的方法
May 10 #Javascript
JS组件系列之Bootstrap table表格组件神器【终结篇】
May 10 #Javascript
Bootstrap Fileinput文件上传组件用法详解
May 10 #Javascript
javascript实现的猜数小游戏完整实例代码
May 10 #Javascript
BootStrap文件上传样式超好看【持续更新】
May 10 #Javascript
You might like
PHP 生成N个不重复的随机数
2015/01/21 PHP
PHP中的一些常用函数收集
2015/05/26 PHP
深入浅析PHP7.0新特征(五大新特征)
2015/10/29 PHP
PHP二维数组实现去除重复项的方法【保留各个键值】
2017/12/21 PHP
解决thinkPHP 5 nginx 部署时,只跳转首页的问题
2019/10/16 PHP
php swoft框架实例用法
2020/12/22 PHP
jQuery 选择器理解
2010/03/16 Javascript
关于Ext中form移除textfield方法:hide(),setVisible(false),remove()
2010/12/02 Javascript
js列举css中所有图标的实现代码
2011/07/04 Javascript
JS实现控制表格行文本对齐的方法
2015/03/30 Javascript
原生js实现的贪吃蛇网页版游戏完整实例
2015/05/18 Javascript
详细总结Javascript中的焦点管理
2016/09/17 Javascript
vue 和vue-touch 实现移动端左右导航效果(仿京东移动站导航)
2017/04/22 Javascript
JavaScript的继承实现小结
2017/05/07 Javascript
JavaScript运动框架 多值运动(四)
2017/05/18 Javascript
微信小程序组件 marquee实例详解
2017/06/23 Javascript
uni-app微信小程序登录授权的实现
2020/05/22 Javascript
[25:45]2018DOTA2亚洲邀请赛4.5SOLO赛 Sylar vs Paparazi
2018/04/06 DOTA
跟老齐学Python之用Python计算
2014/09/12 Python
浅谈tensorflow中几个随机函数的用法
2018/07/27 Python
python自动循环定时开关机(非重启)测试
2019/08/26 Python
Python scrapy增量爬取实例及实现过程解析
2019/12/24 Python
Python获取excel内容及相关操作代码实例
2020/08/10 Python
python如何实现图片压缩
2020/09/11 Python
html5关于外链嵌入页面通信问题(postMessage解决跨域通信)
2020/07/20 HTML / CSS
摩托车和ATV零件、配件和服装的首选在线零售商:MotoSport
2017/12/22 全球购物
德国玩具商店:Planet Happy DE
2021/01/16 全球购物
请假条怎么写
2014/04/10 职场文书
作风整顿剖析材料
2014/09/30 职场文书
小学教育见习报告
2014/10/31 职场文书
2014年物资管理工作总结
2014/12/02 职场文书
高考学习决心书
2015/02/04 职场文书
活动总结模板大全
2015/05/11 职场文书
《金钱的魔力》教学反思
2016/02/20 职场文书
PHP策略模式写法
2021/04/01 PHP
解决pytorch-gpu 安装失败的记录
2021/05/24 Python