内容简介:说谁你只是"会用"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?》,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对 码农网 的支持!
猜你喜欢:本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。