原生js和jQuery中的事件event

Posted by Nutlee on 2016-09-04

关于原生 js 和 jQuery 的事件绑定,应该是绝大多数前端的基本功,但是关于 event 对象,其中又有很多细节是需要注意的,本文通过介绍 event 对象旨在优化事件处理效率。

到底绑定在谁身上

以 jQuery 为例,现在 jQuery 绑定事件官方推荐做法是使用 on ,所以一般绑定事件就是:

1
2
3
4
$element.on('click', '.selector',function(event) {
event.preventDefault();
/* Act on the event */
});

这其中关于 event 对象用到的最基础的就是从 event 中取 jQuery 对象,

  • event.target 是实际接触的控件,即始终指向事件发生的元素,有可能等同于 this
  • event.delegrateTarget 是 on 前面监听的控件 (jQuery 特有的),即“受委托”的元素
  • this 是 on 事件绑定时注册到的元素,即 “.selector”, 注意如果直接触发的是这个元素那么 this === event.target
  • event.currentTarget 指向事件所绑定的元素,通常等同于this

注意:

在 jQuery 事件处理函数中要 显式的在回调函数上加上形参 e 或者 event,否则 event.currentTarget 可能是整个文档的 windows.event

this 和 event.target 用的最多

如果省略 on 里面的 ”.selector“ 这几者则会相同,但是分清楚这几个对象,对高效绑定事件还是很有帮助的。

关于绑定的时机问题

还是以 jQuery 为例,许多新司机习惯于这种绑定,简单粗暴。但是这种绑定是委托(监听)对象和实际触发的对象均一致,一方面若在同一容器中存在多个需要监听的对象,这种方式效率不高。更重要的一点,这种方式必须是文档中存在此 dom。

1
2
3
# 不推荐
$(‘#btn’).on(‘click’,function() {
})

推荐使用如 1 中提到的这种方式,充分利用事件的冒泡原理,甚至可以直接在 body 上监控,同时这种绑定只需要监听的对象即 $element 存在就可以,你可以在一个大的包裹容器上绑定尚未添加的元素事件。配合 jQuery 异步操作、链式操作,可以大大优化 创建 dom – 文档增加 dom – 绑定事件 这一过程。

1
2
3
4
5
# 推荐
$element.on('click', '.selector',function(event) {
event.preventDefault();
/* Act on the event */
});

关于绑定多个事件

关于事件处理最常用到的就是事件绑定,对于 jQuery 使用熟练的人来说更习惯事件监听(委托),这其中对于同时绑定多个事件、多个元素绑定同一个事件处理程序等特殊情况下的操作,我个人的总结如下。

多个事件同一个事件处理程序

1
2
3
$('.btnwrap').on('focus blur','input',function(event){
console.log('按钮被触发,事件',event.type);
});

多个元素同时绑定相同的事件

1
2
3
$('.btnwrap').on('click','#button1,#button2',function(){
console.log('按钮被点击,id',this.id);
});

多种事件多种事件处理程序

1
2
3
4
5
6
7
8
$('.btnwrap').on({
'click': function(){
console.log('按钮被点击');
},
'blur': function(){
console.log('按钮blur');
}
},'input');

常见情况,引用单独的事件处理程序

此处只需注意传递的是事件处理程序的引用。

1
2
3
4
5
6
$('.btnwrap').on('click','button',foo);
// 最好显示的写出形参 event,否则容易有兼容性问题
function foo(event){
console.log('按钮被触发,事件',event.type);
}

绑定传值有什么用

jQuery 官方文档中使用 on 委托绑定事件有个可选参数 data

1
.on( events [, selector ] [, data ], handler(eventObject) )

这个 data 有什么用呢,起初以为只是简单的参数层递,后来查阅官方文档,恍然大悟。

官方 demo:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
<button> 0 </button>
<button> 1 </button>
<button> 2 </button>
<button> 3 </button>
<button> 4 </button>
<script>
var logDiv = $( "#log" );
for ( var i = 0; i < 5; i++ ) {
$( "button" ).eq( i ).on( "click", { value: i }, function( event ) {
var msgs = [
"button = " + $( this ).index(),
"event.data.value = " + event.data.value,
"i = " + i
];
logDiv.append( msgs.join( ", " ) + "<br>" );
});
}
</script>

使用这个可选参数 data 传递参数,同时使用 event.data 取出值,能优雅解决循环中绑定事件的闭包问题,考虑到这种绑定方式通常可以用事件委托灵活解决,所以实际开发中使用到的情况并不多。