在上一篇的分析中,最后$.on方法返回了一个add方法函数的执行,在这里先看一下其代码:

function add(element, events, fn, data, selector, delegator, capture){
var id = zid(element), set = (handlers[id] || (handlers[id] = []))
events.split(/\s/).forEach(function(event){
if (event == 'ready') return $(document).ready(fn)
var handler = parse(event)
handler.fn = fn
handler.sel = selector
console.log(handler.e)
// emulate mouseenter, mouseleave
if (handler.e in hover) fn = function(e){
var related = e.relatedTarget
if (!related || (related !== this && !$.contains(this, related)))
return handler.fn.apply(this, arguments)
}
handler.del = delegator
var callback = delegator || fn
handler.proxy = function(e){
e = compatible(e)
if (e.isImmediatePropagationStopped()) return
e.data = data
var result = callback.apply(element, e._args == undefined ? [e] : [e].concat(e._args))
if (result === false) e.preventDefault(), e.stopPropagation()
return result
}
handler.i = set.length
set.push(handler)
if ('addEventListener' in element)
element.addEventListener(realEvent(handler.e), handler.proxy, eventCapture(handler, capture))
})
}

在这里先跳过id以及handler部分(handler.e的值即为事件的type属性),该函数主要作用就是将事件添加进元素的事件队列里。

首先是对events进行分割成多个事件,并通过each函数逐个绑定。先来看一下函数里面的条件判定:

hover = { mouseenter: 'mouseover', mouseleave: 'mouseout' }
if (handler.e in hover) fn = function(e){
var related = e.relatedTarget
if (!related || (related !== this && !$.contains(this, related)))
return handler.fn.apply(this, arguments)
}

如果事件的type属性是mouseenter或者mouseleave,那么需要重新定义绑定函数fn,什么是relatedTarget呢?

relatedTarget 属性用于在一个事件中查找另外一个元素。有些事件比如 mouseover 通常侧重处理一个特定的目标,而有些有也可能会涉及到第二目标,比如当目标退出第一目标的 mouseover 事件.

获得relatedTarget元素后,zepto对其进行判断,if (!related || (related !== this && !$.contains(this, related))),假设我们有两个div。

<div class="out">
<div class="in"></div>
</div>

我们对div.out定义mouseenter事件后,当我们鼠标从div.in移出来,但鼠标仍然在div.out,那么鼠标自始至终都在div.out中,但还是触发了mouseenter事件,这可能与预期中不同,所以zepto在这里加了判断,如果relatedTarget元素不存在,或者relatedTarget元素不是我们想要触发的元素本身,并且不包含在其中,即返回一个调用原来的fn函数。

接下来就是handler.proxy函数了,该函数的作用就是通过apply调用绑定的事件函数,并返回结果。

最后通过原生addEvent来绑定函数。

最后,总结一下zepto中绑定事件的思路:

1.找到目标元素。 即match = $(e.target).closest(selector, element).get(0),如果没有selector即跳过。

2.事件委托。如果存在selector,即重新生成一个事件,并通过compitable,createProxy对其进行修改,并改变其currentTarget属性,如果不存在selector,即对要绑定的dom执行compitable,添加eventMethod中的方法。

3.事件绑定,当存在selector触发时,实际触发的是$对象中的dom元素,只是通过事件委托使得像在符合selector条件元素中执行一样。

ps:当存在selector时,假设绑定了click事件,但点击的是$对象中的dom元素并且区域不在selector中,同样触发了事件对象,只是上一篇中的delegator函数里,如果找不到selector,那么跳过,不执行我们绑定的函数。$中的dom元素绑定的是handler.proxy函数,handler.proxy返回的是delegator的执行结果,而delegator如果找不到selector,那么就不执行我们绑定的函数。

zepto 事件分析3(add函数)的更多相关文章

  1. zepto 事件分析2($.on)

    这里主要分析zepto事件中的$.on函数,先看一下该函数的代码 $.fn.on = function(event, selector, data, callback, one){ var autoR ...

  2. zepto 事件分析4(事件队列)

    前面分析了zepto的事件绑定,接下来分析事件解绑,先看一下zepto中解绑的off方法: $.fn.off = function(event, selector, callback){ var $t ...

  3. zepto 事件分析1($.Event)

    先看一下zepto事件的函数,在这里,zepto是把zepto对象作为一个立即执行函数的参数传进去的. (function($){ ... ... })(Zepto) 在zepto事件函数中,主要为$ ...

  4. Zepto事件模块源码分析

    Zepto事件模块源码分析 一.保存事件数据的handlers 我们知道js原生api中要移除事件,需要传入绑定时的回调函数.而Zepto则可以不传入回调函数,直接移除对应类型的所有事件.原因就在于Z ...

  5. jQuery源码解读-事件分析

    最原始的事件注册 addEventListener方法大家应该都很熟悉,它是Html元素注册事件最原始的方法.先看下addEventListener方法签名: element.addEventList ...

  6. 移动web app开发必备 - zepto事件问题

    问题描述: 项目在祖先元素上绑定了 touchstart,touchmove,touchend事件,用来处理全局性的事件,比如滑动翻页 正常状态下: 用户在子元素上有交互动作时,默认状态下都是会冒泡到 ...

  7. 源码分析MySQL mysql_real_query函数

    目录 目录 1 1. 前言 1 2. 调用路径 2 3. MAX_PACKET_LENGTH宏 2 4. DBUG_RETURN宏 3 5. COM_QUERY枚举值 3 6. mysql_query ...

  8. Zepto源代码分析一~核心方法

    今天抽出时间复习了一下Zepto的源代码,依照自己的理解进行凝视. 欢迎大家拍砖. 源代码版本号:v1.1.4 源代码下载地址:http://zeptojs.com/ 分析总体代码之后,整理出架构图: ...

  9. Zepto源代码分析之二~三个API

    因为时间关系:本次仅仅对这三个API($.camelCase.$.contains.$.each)方法进行分析 第一个方法变量转驼峰:$.camelCase('hello-world-welcome' ...

随机推荐

  1. 计算机网络六:无线局域网、IEEE 802.11、WIFI和蓝牙

    无线局域网.IEEE 802.11.WIFI和蓝牙 ㈠无线局域网 1.定义       无线局域网络(Wireless Local Area Networks),简称WLAN.它是相当便利的数据传输系 ...

  2. C++的IO处理中的头文件以及类理解(2)<sstream>头文件

    C++的IO处理中的头文件以及类理解(2)<sstream>头文件 头文件<sstream>中定义的类型都继承iostream头文件中定义的类型.除了继承得来的操作,sstre ...

  3. FPGA中带优先级的if else if与不带优先级的case的探讨

    我们知道在书本上都说让我们尽量使用不带优先级的的数据选择器,今天我们就来探讨一下二者的区别. 例子1:带优先级的的数据选择器,综合成功,且没有任何警告. module detection_prio # ...

  4. 设置npm的registry

    .原npm地址 npm config set registry http://registry.npmjs.org .设置国内镜像 a.通过config命令 npm config set regist ...

  5. Oracle数据库用EF操作的示例

    Using EF Oracle Sample Provider with EDM Designer  (from msdn) Many people are asking if it is possi ...

  6. Android 视频播放器 (一):使用VideoView播放视频

    一.简介 作为Android开发,我们不可避免的会接触到视频播放,VideoView做为最简单的播放器,我们是不应该不会的. 下面简单介绍一下VideoView: VideoView是使用MediaP ...

  7. 第85节:Java中的JavaScript

    第85节:Java中的JavaScript 复习一下css: 选择器的格式: 元素选择器:元素的名称{} 类选择器:. 开头 ID选择器:# ID选择器 后代选择器: 选择器1 选择器2 子元素选择器 ...

  8. 关键字New,如阴魂不散

    本文是溪源翻译的第一篇技术文章,查看原文,因时间仓促,也许翻译过程措辞不当,还请见谅. 当你使用类似于C#或VisualBasic这种强类型语言,在实例化对象时,你往往会使用New这个关键字.我敢保证 ...

  9. JavaScript状态模式及状态机模型

    这是一篇,我自己都看不完的文章... 文章大体就两部分: 状态模式的介绍 状态机模型的函数库javascript-state-machine的用法和源码解析 场景及问题背景: 我们平时开发时本质上就是 ...

  10. Go语言之Interface(二)

    使用指针接收器和值接收器实现接口 type Describer interface { Describe() } type Person struct { name string age int } ...