说谁你只是"会用"jQuery?

栏目: Node.js · 发布时间: 6年前

内容简介:说谁你只是"会用"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语句很好的处理了多种使用情况的参数处理。也许直接看不能知晓到底是如何做到的,可以试试每种使用情况都代入其中,找寻其是如何兼容的。

接下来我们第三段

这段函数做了非常重要的两件事

  1. 处理one传入为true,事件只触发一次的场景
  2. 处理传入了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?》,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对 码农网 的支持!

查看所有标签

猜你喜欢:

本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们

计算理论导论

计算理论导论

塞普斯 / 机械工业出版社 / 2002-8 / 39.0

This book——by a noted authority and educator in the field——presents computer science theory from a uniquely intuitive,“big picture”perspective.The author grounds his clear and interesting study on ......一起来看看 《计算理论导论》 这本书的介绍吧!

JS 压缩/解压工具
JS 压缩/解压工具

在线压缩/解压 JS 代码

在线进制转换器
在线进制转换器

各进制数互转换器

RGB CMYK 转换工具
RGB CMYK 转换工具

RGB CMYK 互转工具