背景:
今天写代码时使用了一下jQuery的事件监听,于是与原生DOM操作进行了比较,特别是event对象和this的指向两者之间的异同。
复制代码
为什么进行比较
为什么我要抠这个细节?主要是避免使用时不至于混淆this或event.target指向的元素而出现莫名其妙的问题。第二个原因是强迫症(请忽略..).
开始比较
第一次比较
有如下html片段:
目标:委托 #outer 作为监听者,点击a#one
元素,观察ev.target及this的指向
<div id="outer">
<div><a id="one" href="ca.html">ONE</a></div>
<a id="two" href="cb.html">ONE</a>
<a id="three" href="cc.html">ONE</a>
</div>
复制代码
事件处理程序:
function test(ev) {
console.log(ev.target);
ev.preventDefault();
console.log(this);
}
复制代码
- 原生DOM
var el = document.getElementById('outer');
el.addEventListener('click',test,false);
复制代码
- jQuery
$('#outer').on('click',test);
复制代码
结果:
不出所料,this指向监听者div#outer
,ev.target指向了a#one
,而且原生DOM和jquery的输出结果一样。这与我们的直觉一致。
第二次比较
jQuery加入了一个选择器,筛选了div#outer
下面的a元素,同样点击a#one
元素
$('#outer').on('click',test);
复制代码
结果:
第二次使用,jquery的this却指向了a#one
,不再是监听者div#outer
,看起来有点奇怪.
你可能会觉得,添加了选择器,是不是相当于给a元素也添加了监听器,如果是这样的话我为什么不写成下面的第二种形式呢?
$('#outer').on('click','a',test);
复制代码
而且输出还和上面一致也是log出来两个a元素,所以得出结论:这两行代码等价!
可是,先感性地想想,和前面的写法的结果一样,所以第一种写法是多余的吗?如果我的#outer
里面有很多个a子元素,使用第二种写法,不就添加了很多事件监听器吗?事件委托不就失去了作用?所以我相信jquery的设计者们没那么笨,这么写一定有它的原因.
我再认真观察了以下DOM结构,发现浏览器的处理方法明显不一样:
对于$('#outer').on('click','a',test);
这种写法,只有被委托的父元素创建了事件监听对象.
对于$('#outer a').on('click',test);
这种写法,每一个a元素都绑定了事件,如果a的元素很多,那么可以预见的问题是页面性能下降.
好了,结论出来了,我们可以猜想,第一种写法,与一般的事件委托大同小异,委托了父元素进行监听.但又仅对a元素的点击产生响应.
你可能会问,真的只对a元素产生响应吗?感性的想法那当然是,不然这样设计接口的意义就不存在了.
第三次比较
于是,我们可以考虑一下,jquery的事件监听方法大致等价于:
function test(ev) {
if (ev.target.tagName === 'A') {
console.log(ev.target);
ev.preventDefault();
console.log(this);
}
var el = document.getElementById('outer');
el.addEventListener('click',test,false);
复制代码
嗯~看起来有点那么回事,先进行条件判断,如果元素名是a那么才执行代码块,大体上逻辑没问题,可是...
点击一下a#one
试试,结果:
嗯?跟jquery的不一样来着,刚才我们已经发现了,使用jquery的on方法,this指向了产生事件对象的元素,即a#one
而原生DOM的this指向了事件监听者div#outer
指向事件监听者更符合我们的认知.
不过我们还是可以得出结论,jquery的on方法的selector参数,类似于在事件委托的基础上做了一次if判断,只有符合结果的元素才会响应事件.
那么,ev.target 还是 this?
ev.target和this哪一个更好呢?
综合以上比较,我认为需要分情况灵活应用,因为:
- this有可能指向监听者,这有时候往往是我们想要的,特别是创建了多个事件监听器的时候.
- 一旦处理程序被包裹在匿名函数中,this的指向就会发生变化,而ev.target却始终保持一致.这时ev.target是更好的选择.
- 值得注意的是,如果使用this,jquery(在on方法传入selector参数的情况下)和原生DOM的this指向还会不一样.jquery的on方法里面的this跟ev.target一样,但原生DOM的this却指向事件监听者.