JavaScrip自定义事件
基本概念
事件在设计模式中也叫观察者模式(发布订阅模式)。观察者模式由两类对象组成:主体和观察者。主体负责发布事件,观察者通过订阅这些事件来观察该主体。当主体对象的状态发生改变时,所有依赖于它的对象都将得到通知。涉及DOM上时,DOM元素便是主体,事件处理代码便是观察者。
事件是与浏览器交互的最常见的方式,但是它们也可以用于非DOM代码中:通过实现自定义事件。
使用对象字面量封装自定义事件
首先观察如何为DOM元素添加事件处理程序
例子:
<button>OK</button>
var btn=document.getElementsByTagName("button")[0]
btn.addEventListener("click",function(){
alert(this.nodeName)
})
btn.addEventListener("click",function(){
alert(this.firstChild.nodeValue)
})
//输出:BUTTON/OK
当button被单击的时候,就会依次触发两个函数,这为实现自定义事件提供了思路:事件和事件处理程序的映射关系可以用数组的形式建立起来。注册事件就是向数组里添加事件处理函数,触发事件就是遍历数组里的函数并依次执行。
例子:
"eventName1":[fn1,fn2]
"eventName2":[fn1,fn2]
"eventName3":[fn1,fn2]
1、使用对象字面量封装自定义事件
代码如下:
var EventTarget={
handlers:{},
addHandler:addHandler,
fire:fire,
removeHandler:removeHandler
}
handlers:保存事件和事件处理程序的映射关系
addHandler:注册给定类型的事件处理程序
fire:触发一个事件
removeHandler:注销某个事件类型的事件处理程序
下面依次讲解
2、注册给定类型的事件处理程序
代码如下:
function addHandler(type, handler){
if (typeof this.handlers[type] == "undefined"){
this.handlers[type] = [];
}
this.handlers[type].push(handler);
}
*addHandler()方法接收两个参数:事件类型和用于处理该事件的函数。
3、触发一个事件
代码如下:
function fire(event){
if (!event.target){
event.target = this;
}
if (this.handlers[event.type] instanceof Array){
var handlers = this.handlers[event.type];
for (var i=0, len=handlers.length; i < len; i++){
handlers[i](event);
}
}
}
*fire()方法接收一个至少包含type属性的event对象作为参数。因为是自定义事件,所以event对象上的属性信息可以在发布该类型事件的时候自行定义。
4、注销某个事件类型的事件处理程序
代码如下:
function removeHandler(type, handler){
if (this.handlers[type] instanceof Array){
var handlers = this.handlers[type];
for (var i=0, len=handlers.length; i < len; i++){
if (handlers[i] === handler){
break;
}
}
handlers.splice(i, 1);
}
}
*removeHandler()方法接收两个参数:事件类型和用于处理该事件的函数。
5、使用自定义事件
代码如下:
var EventTarget={
handlers:{},
addHandler:addHandler,
fire:fire,
removeHandler:removeHandler
}
function addHandler(type, handler){
if (typeof this.handlers[type] == "undefined"){
this.handlers[type] = [];
}
this.handlers[type].push(handler);
}
function fire(event){
if (!event.target){
event.target = this;
}
if (this.handlers[event.type] instanceof Array){
var handlers = this.handlers[event.type];
for (var i=0, len=handlers.length; i < len; i++){
handlers[i](event);
}
}
}
function removeHandler(type, handler){
if (this.handlers[type] instanceof Array){
var handlers = this.handlers[type];
for (var i=0, len=handlers.length; i < len; i++){
if (handlers[i] === handler){
break;
}
}
handlers.splice(i, 1);
}
}
//注册事件(发布订阅模式中的”订阅”)
EventTarget.addHandler("alert",alertMessage1)
EventTarget.addHandler("alert",alertMessage2)
//触发事件(发布订阅模式中的”发布”)
EventTarget.fire({type:"alert",message1:"hello",message2:"baby"})
function alertMessage1(event){
alert(event.message1)
}
function alertMessage2(event){
alert(event.message2)
}
//输出:hello/baby
*使用自定义事件,即观察者模式(发布订阅模式)优势非常明显:触发事件的代码和注册事件的代码是完全分离的。例如在购物网站中,用户登录成功的信息会同时应用在个人信息模块、购物车模块和消息列表模块。这些模块各自订阅用户登录成功的信息,彼此之间没有任何联系。
使用对象原型封装自定义事件
使用对象原型封装自定义事件和使用对象字面量封装自定义事件的思路和写法基本一致。
代码如下:
function EventTarget(){
this.handlers = {};
}
EventTarget.prototype = {
constructor: EventTarget,
addHandler: function(type, handler){
if (typeof this.handlers[type] == "undefined"){
this.handlers[type] = [];
}
this.handlers[type].push(handler);
},
fire: function(event){
if (!event.target){
event.target = this;
}
if (this.handlers[event.type] instanceof Array){
var handlers = this.handlers[event.type];
for (var i=0, len=handlers.length; i < len; i++){
handlers[i](event);
}
}
},
removeHandler: function(type, handler){
if (this.handlers[type] instanceof Array){
var handlers = this.handlers[type];
for (var i=0, len=handlers.length; i < len; i++){
if (handlers[i] === handler){
break;
}
}
handlers.splice(i, 1);
}
}
}
自定义DOM事件
上文所述的自定义事件是在非DOM代码中注册和触发的事件,在DOM3级中也可以让开发人员自己创建自己的DOM事件,即自定义DOM事件。
创建自定义DOM事件,可以使用如下方法:
createEvent(“CustomEvent”):创建自定义事件
initCustomEvent():初始化事件
dispatchEvent():触发事件其中initCustomEvent()方法接收四个参数:
type(字符串):事件类型
bubbles(布尔值):事件是否冒泡
cancelable(布尔值):事件是否可以取消
detail(对象):任意值,保存在event对象的detail属性中
例子:
<button>OK</button>
var btn=document.getElementsByTagName("button")[0]
var event = document.createEvent("CustomEvent")
event.initCustomEvent("alert", false, false,"hello")
btn.dispatchEvent(event)
btn.addEventListener("alert", function(event) {
alert(event.detail)
})
//输出:hello
由于支持自定义DOM事件的浏览器只有IE9+和Firefox6+,因此不建议直接在DOM上进行事件的扩展,本文到此也不再深入探讨。建议在实际项目开发中使用jQuery封装好的DOM事件(支持自定义事件和系统默认事件),或者可以使用下面模拟jQuery封装的DOM事件(同样支持自定义事件和系统默认事件)。
jQuery中的DOM事件
例子:
<script class="lazyload" src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAADsQAAA7EAZUrDhsAAAANSURBVBhXYzh8+PB/AAffA0nNPuCLAAAAAElFTkSuQmCC" data-original="http://libs.baidu.com/jquery/2.0.0/jquery.min.js"></script>
<button>OK</button>
$("button").on("alert",function(){
alert(this.nodeName)
})
$("button").trigger("alert")
//输出:BUTTON
模拟jQuery封装的DOM事件(源自网络)
例子:
<button>OK</button>
ar $ = function(el) {
return new _$(el);
}
var _$ = function(el) {
this.el = (el && el.nodeType == 1)? el: document;
}
_$.prototype = {
constructor: this,
addEvent: function(type, fn, capture) {
var el = this.el;
if (window.addEventListener) {
el.addEventListener(type, fn, capture);
var ev = document.createEvent("HTMLEvents");
ev.initEvent(type, capture || false, false);
if (!el["ev" + type]) {
el["ev" + type] = ev;
}
} else if (window.attachEvent) {
el.attachEvent("on" + type, fn);
if (isNaN(el["cu" + type])) {
el["cu" + type] = 0;
}
var fnEv = function(event) {
if (event.propertyName == "cu" + type) {
fn.call(el);
}
};
el.attachEvent("onpropertychange", fnEv);
if (!el["ev" + type]) {
el["ev" + type] = [fnEv];
} else {
el["ev" + type].push(fnEv);
}
}
return this;
},
fireEvent: function(type) {
var el = this.el;
if (typeof type === "string") {
if (document.dispatchEvent) {
if (el["ev" + type]) {
el.dispatchEvent(el["ev" + type]);
}
} else if (document.attachEvent) {
el["cu" + type]++;
}
}
return this;
},
removeEvent: function(type, fn, capture) {
var el = this.el;
if (window.removeEventListener) {
el.removeEventListener(type, fn, capture || false);
} else if (document.attachEvent) {
el.detachEvent("on" + type, fn);
var arrEv = el["ev" + type];
if (arrEv instanceof Array) {
for (var i=0; i<arrEv.length; i+=1) {
el.detachEvent("onpropertychange", arrEv[i]);
}
}
}
return this;
}
}
//调用方法
var btn=document.getElementsByTagName("button")[0]
$(btn).addEvent("alert", fnAlert)
$(btn).fireEvent("alert")
function fnAlert(){
alert(this.nodeName)
}
//输出:BUTTON
文中的代码部分,带有“例子”和“测试代码”字样的,只是用来学习或测试某一功能用的代码,不可以直接用于项目的开发中。带有“代码如下”字样的,都是经过本人测试,简单修改即可用于项目开发中的代码,如有错误,欢迎指出。
共同学习,写下你的评论
评论加载中...
作者其他优质文章