内容简介:说谁你只是"会用"jQuery?
前言
套用上篇文章 向zepto.js学习如何手动触发DOM事件 的开头:grinning::grinning::grinning:
前端在最近几年实在火爆异常,vue、react、angular各路框架层出不穷,咱们要是不知道个双向数据绑定,不晓得啥是虚拟DOM,也许就被鄙视了。火热的背后往往也是无尽的浮躁,学习这些先进流行的类库或者框架可以让我们走的更快,但是静下心来回归基础,把基石打牢固,却可以让我们走的更稳,更远。
最近一直在看zepto的源码,希望通过学习它掌握一些框架设计的技巧,也将很久不再拾起的js基础重新温习巩固一遍。如果你对这个系列感兴趣,欢迎 点击 watch ,随时关注动态。这篇文章主要想说一下zepto中事件模块(event.js)的添加事件 on 以及移除事件 off 实现原理。
说在前面
在没有vue和react,甚至angular都没怎么接触的刀耕火种的时代,jQuery或者zepto是我们手中的利器,是刀刃,他让我们游刃有余地开发出兼容性好的漂亮的网页,我们膜拜并感叹作者带来的便利,沉浸其中,无法自拔。
=== 准备放一张照片一梭子jQuery
但是用了这么久的zepto你知道这样写代码
$('.list').on('click', 'li', function(e){
console.log($(this).html())
})
是怎么实现事件委托的吗?为啥此时的 this 就是你点中的 li 呢?
平常我们可能还会这样写。
$('.list li').bind('click', function(){})
$('.list').delegate('li', 'click', function(){})
$('.list li').live('click', function(){})
$('.list li').click(function(){})
写法有点多,也许你还有其他的写法,那么
on
bind
delegate
live
click()
这些添加事件的形式,有什么区别,内部之间又有什么联系呢?
相信你在面试过程中也遇到过类似的问题( 看完这边文章,你可以知道答案的噢:hushed: )?
接下来我们从源码的角度一步步去探究其内部实现的原理。
一切从 on 开始
为什么选择从 on 添加事件的方式开始说起,原因在于其他写法几乎都是 on 衍生出来的,明白了 on 的实现原理,其他的也就差不多那么回事了。
祭出一张画了好久的图
上面大概是zepto中 on 形式注册事件的大致流程,好啦开始看源码啦,首先是on函数,它主要做的事情是注册事件前的参数处理,真正添加事件是内部函数add。
$.fn.on = function(event, selector, data, callback, one){
// 第一段
var autoRemove, delegator, $this = this
if (event && !isString(event)) {
$.each(event, function(type, fn){
$this.on(type, selector, data, fn, one)
})
return $this
}
// 第二段
if (!isString(selector) && !isFunction(callback) && callback !== false)
callback = data, data = selector, selector = undefined
if (callback === undefined || data === false)
callback = data, data = undefined
if (callback === false) callback = returnFalse
// 以上为针对不同的调用形式,做好参数处理
// 第三段
return $this.each(function(_, element){
// 处理事件只有一次生效的情况
if (one) autoRemove = function(e){
remove(element, e.type, callback)
return callback.apply(this, arguments)
}
// 添加事件委托处理函数
if (selector) delegator = function(e){
var evt, match = $(e.target).closest(selector, element).get(0)
if (match && match !== element) {
evt = $.extend(createProxy(e), { currentTarget: match, liveFired: element })
return (autoRemove || callback).apply(match, [evt].concat(slice.call(arguments, 1)))
}
}
// 使用add内部函数真正去给选中的元素注册事件
add(element, event, callback, data, selector, delegator || autoRemove)
})
}
直接看到这么一大坨的代码不易于理解,我们分段进行阅读。
第一段
var autoRemove, delegator, $this = this
if (event && !isString(event)) {
$.each(event, function(type, fn){
$this.on(type, selector, data, fn, one)
})
return $this
}
这段代码主要是为了处理下面这种调用形式。
$('.list li').on({
click: function(){
console.log($(this).html())
},
mouseover: function(){
$(this).css('backgroundColor', 'red')
},
mouseout: function(){
$(this).css('backgroundColor', 'green')
}
})
这种写法我们平时写的比较少一点,但是确实是支持的。而zepto的处理方式则是循环调用 on 方法,以 key 为事件名, val 为事件处理函数。
在开始第二段代码阅读前,我们先回顾一下,平时经常使用 on 来注册事件的写法一般有哪些
// 这种我们使用的也许最多了
on(type, function(e){ ... })
// 可以预先添加数据data,然后在回调函数中使用e.data来使用添加的数据
on(type, data, function(e){ ... })
// 事件代理形式
on(type, [selector], function(e){ ... })
// 当然事件代理的形式也可以预先添加data
on(type, [selector], data, function(e){ ... })
// 当然也可以只让事件只有一次起效
on(type, [selector], data, function(e){ ... }, true)
还会有其他的写法,但是常见的可能就是这些,第二段代码就是处理这些参数以让后续的事件正确添加。
第二段
// selector不是字符串形式,callback也不是函数
if (!isString(selector) && !isFunction(callback) && callback !== false)
callback = data, data = selector, selector = undefined
// 处理data没有传或者传了函数
if (callback === undefined || data === false)
callback = data, data = undefined
// callback可以传false值,将其转换为returnFalse函数
if (callback === false) callback = returnFalse
三个if语句很好的处理了多种使用情况的参数处理。也许直接看不能知晓到底是如何做到的,可以试试每种使用情况都代入其中,找寻其是如何兼容的。
接下来我们第三段
这段函数做了非常重要的两件事
- 处理one传入为true,事件只触发一次的场景
- 处理传入了selector,进行事件代理处理函数开发
我们一件件看它如何实现。
if (one) autoRemove = function(e){
remove(element, e.type, callback)
return callback.apply(this, arguments)
}
内部用了一个 remove 函数,这里先不做解析,只要知道他就是移除事件的函数就可以,当移除事件的时候,再执行了传进来的回调函数。进而实现只调用一次的效果。
那么事件代理又是怎么实现咧?
回想一下平常自己是怎么写事件代理的,一般是利用事件冒泡(当然也可以使用事件捕获)的性质,将子元素的事件委托到祖先元素身上,不仅可以实现事件的动态性,还可以减少事件总数,提高性能。
举个例子
我们把原本要添加到li上的事件委托到父元素ul上。
<ulclass="list"> <li>1</li> <li>2</li> <li>3</li> </ul>
let $list = document.querySelector('.list')
$list.addEventListener('click', function(e){
e = e || window.event
let target = e.target || e.srcElement
if (target.tagName.toLowerCase() === 'li') {
target.style.background = 'red'
}
}, false)
以上所述就是小编给大家介绍的《说谁你只是"会用"jQuery?》,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对 码农网 的支持!
猜你喜欢:本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。