源码注释

//     Zepto.js
//     (c) 2010-2015 Thomas Fuchs
//     Zepto.js may be freely distributed under the MIT license.

;(function($){var _zid = 1, undefined,slice = Array.prototype.slice,isFunction = $.isFunction,isString = function(obj){ return typeof obj == 'string' },handlers = {},//_zid: events    事件缓存池specialEvents={},focusinSupported = 'onfocusin' in window,      //是否支持即将获取焦点时触发函数   onfocusin focus不支持冒泡focus = { focus: 'focusin', blur: 'focusout' },    //焦点修正hover = { mouseenter: 'mouseover', mouseleave: 'mouseout' }    // mouseenter  mouseleave不冒泡的修正 ,mouseover mouseout功能一样且支持冒泡//此处标准浏览器,click、mousedown、mouseup、mousemove抛出的就是MouseEvents,应该也是对低版本IE等某些浏览器的修正specialEvents.click = specialEvents.mousedown = specialEvents.mouseup = specialEvents.mousemove = 'MouseEvents'/*** 取元素标识符,没有设置一个返回* @param element* @returns {*|number}*/function zid(element) {return element._zid || (element._zid = _zid++)}/***  查找元素上事件响应函数集合* @param element* @param event* @param fn* @param selector* @returns {Array}*/function findHandlers(element, event, fn, selector) {//解析命名空间事件名event = parse(event)//
    if (event.ns) var matcher = matcherFor(event.ns)//找到响应函数集合return (handlers[zid(element)] || []).filter(function(handler) {return handler&& (!event.e  || handler.e == event.e) //判断事件类型是否相同&& (!event.ns || matcher.test(handler.ns)) //判断事件命名空间是否相同 RegExp.prototype.test = function(String) {};&& (!fn       || zid(handler.fn) === zid(fn))  // zid(handler.fn)返回handler.fn的标识,没有加一个,判断fn标识符是否相同&& (!selector || handler.sel == selector)  //返回 handler,  判断selector是否相同
    })}/*** 解析事件类型* @param event  'click'* @returns {{e: * 事件类型 , ns: string 命名空间}}*/function parse(event) {//如果有.分隔,证明有命名空间var parts = ('' + event).split('.')return {e: parts[0], ns: parts.slice(1).sort().join(' ')}}/*** 生成命名空间的正则对象* @param ns* @returns {RegExp}*/function matcherFor(ns) {return new RegExp('(?:^| )' + ns.replace(' ', ' .* ?') + '(?: |$)')}/*** 事件捕获* 对focus和blur事件且浏览器不支持focusin focusout,通过设置捕获来模拟冒泡* @param handler* @param captureSetting* @returns {*|boolean|boolean}*/function eventCapture(handler, captureSetting) {//如果是focus和blur事件且浏览器不支持focusin focusout时,//设置为可捕获,间接达到冒泡的目的return handler.del &&(!focusinSupported && (handler.e in focus)) ||!!captureSetting}/***  修正事件类型 focus->focusIn blur->focusOut mouseenter->mouseover  mouseleave->mouseout* @param type   事件类型* @returns {*|boolean|*|*}*/function realEvent(type) {//hover[type] mouseenter和mouseleave 转换成   mouseover和mouseout// focus[type]  focus blur  修正为  focusin  focusoutreturn hover[type] || (focusinSupported && focus[type]) || type}/*** 增加事件底层方法* @param element* @param events  字符串 如‘click'* @param fn* @param data* @param selector* @param delegator* @param capture*/function add(element, events, fn, data, selector, delegator, capture){//zid Zepto会在elemnt上扩展一个标识属性_zid// 读取元素上已绑定的事件处理函数var id = zid(element), set = (handlers[id] || (handlers[id] = []))// \s 匹配空格events.split(/\s/).forEach(function(event){//如果是ready事件if (event == 'ready') return $(document).ready(fn)//解析事件   {e: * 事件类型 , ns: string 命名空间}var handler   = parse(event)//保存fn,下面为了处理mouseenter, mouseleave时,对fn进行了修改//存储fn响应函数//存储selectorhandler.fn    = fnhandler.sel   = selector// emulate mouseenter, mouseleave// 模仿 mouseenter, mouseleave//如果事件是mouseenter, mouseleave,模拟mouseover mouseout事件处理if (handler.e in hover) fn = function(e){
//          relatedTarget 事件属性返回与事件的目标节点相关的节点。
//            对于 mouseover 事件来说,该属性是鼠标指针移到目标节点上时所离开的那个节点。
//            对于 mouseout 事件来说,该属性是离开目标时,鼠标指针进入的节点。
//            对于其他类型的事件来说,这个属性没有用。var related = e.relatedTarget//不存在,表明不是mouseover、mouseout事件,//related !== this && !$.contains(this, related))  当related不在事件对象event内   表示事件已触发完成,不是在move过程中,需要执行响应函数if (!related || (related !== this && !$.contains(this, related)))//执行响应函数return handler.fn.apply(this, arguments)}//事件委托handler.del   = delegatorvar callback  = delegator || fnhandler.proxy = function(e){//修正evente = compatible(e)//如果是阻止所有事件触发if (e.isImmediatePropagationStopped()) returne.data = data //缓存数据//执行回调函数,context:element,arguments:event,e._args(默认是undefind,trigger()时传递的参数)var result = callback.apply(element, e._args == undefined ? [e] : [e].concat(e._args))//当事件响应函数返回false时,阻止浏览器默认操作和冒泡if (result === false) e.preventDefault(), e.stopPropagation()return result}//设置事件响应函数的索引,删除事件时,根据它来删除  delete handlers[id][handler.i]handler.i = set.length//缓存到handlers[id]里    set = handlers[id]set.push(handler)//元素支持DOM2级事件绑定if ('addEventListener' in element)//绑定事件//DOM源码
//         @param {string} type
//        @param {EventListener|Function} listener
//        @param {boolean} [useCapture]     是否使用捕捉,默认 false
//        EventTarget.prototype.addEventListener = function(type,listener,useCapture) {};//realEvent(handler.e)  修正后的事件类型//handler.proxy 修正为代理上下文的事件响应函数// eventCapture(handler, capture)
        element.addEventListener(realEvent(handler.e), handler.proxy, eventCapture(handler, capture))})}/***  删除事件,   对应add* @param element* @param events* @param fn* @param selector* @param capture  是否捕获*/function remove(element, events, fn, selector, capture){var id = zid(element)  //找到元素标识;(events || '').split(/\s/).forEach(function(event){ //events多个以空格分隔//遍历事件响应函数集合findHandlers(element, event, fn, selector).forEach(function(handler){delete handlers[id][handler.i]      //删除缓存在handlers的响应函数if ('removeEventListener' in element)//调用DOM原生方法删除事件//DOM源代码
//          /**
//           @param {string} type
//           @param {EventListener|Function} listener
//           @param {boolean} [useCapture]
//           */
//          EventTarget.prototype.removeEventListener = function(type,listener,useCapture) {};//realEvent(handler.e) 修正事件类型     handler.proxy  代理的事件响应函数     eventCapture(handler, capture)修正的是否捕获//与增加事件底层函数 add最后一行    element.addEventListener(realEvent(handler.e), handler.proxy, eventCapture(handler, capture))  呼应
        element.removeEventListener(realEvent(handler.e), handler.proxy, eventCapture(handler, capture))})})}//此处不清楚要干嘛,将事件两个核心底层方法封装到event对象里,方便做Zepto插件事件扩展?$.event = { add: add, remove: remove }/*** 代理* (function,context),(context,name)* @param fn* @param context* @returns {*}*/$.proxy = function(fn, context) {var args = (2 in arguments) && slice.call(arguments, 2)   //如果传了第3个参数,取到第3个参数以后(包含第3个参数)所有的参数数组,挺好的判断技巧if (isFunction(fn)) {   //fn是函数//采用闭包,以context调用函数。// args.concat(slice.call(arguments)) 将传参挪到前面  如传递给$.proxy(fn,context,3,4);  转变成  fn.apply(context,[3,4,fn,context,3,4])var proxyFn = function(){ return fn.apply(context, args ? args.concat(slice.call(arguments)) : arguments) }// 标记函数proxyFn._zid = zid(fn)return proxyFn} else if (isString(context)) {  //context是字符串, 实际传参(context,name)if (args) {                           //修正传参,再以$.proxy调用args.unshift(fn[context], fn)   // unshift  往数组开头添加新的项return $.proxy.apply(null, args)} else {return $.proxy(fn[context], fn)}} else {throw new TypeError("expected function")   //抛出异常:要求的函数类型错误
    }}/*** 绑定事件,应直接采用on* 源自1.9版本前jquery的绑定事件的区分:bind()是直接绑定在元素上.live()则是通过冒泡的方式来绑定到元素上的。更适合列表类型的,绑定到document DOM节点上。和.bind()的优势是支持动态数据。.delegate()则是更精确的小范围使用事件代理,性能优于.live().on()则是1.9版本整合了之前的三种方式的新事件绑定机制* @param event* @param data* @param callback* @returns {*}*/$.fn.bind = function(event, data, callback){return this.on(event, data, callback)}/***  解绑事件,应直接用off* @param event* @param callback* @returns {*}*/$.fn.unbind = function(event, callback){return this.off(event, callback)}/*** 绑定一次性事件* @param event* @param selector* @param data* @param callback* @returns {*}*/$.fn.one = function(event, selector, data, callback){return this.on(event, selector, data, callback, 1)}var returnTrue = function(){return true},returnFalse = function(){return false},ignoreProperties = /^([A-Z]|returnValue$|layer[XY]$)/,//匹配 大写字母A-Z开头/returnValue/layerX/layerY用于createProxy(),过滤event对象的属性eventMethods = {preventDefault: 'isDefaultPrevented',//是否已调用preventDefault()    preventDefault      阻止浏览器的默认动作stopImmediatePropagation: 'isImmediatePropagationStopped', //是否已调用stopImmediatePropagation(),stopImmediatePropagation DOM3提出的阻止任何事件触发stopPropagation: 'isPropagationStopped' //是否已调用stopPropagation()  stopPropagation阻止冒泡
      }/*** 修正event对象* @param event   代理的event对象 原生event对象* @param source  原生event对象* @returns {*}*/function compatible(event, source) {//event.isDefaultPrevented   是否已调用了preventDefault方法////event是代理事件对象时,赋值给sourceif (source || !event.isDefaultPrevented) {source || (source = event)//遍历,代理preventDefault  stopImmediatePropagation   stopPropagation等方法
      $.each(eventMethods, function(name, predicate) {var sourceMethod = source[name]event[name] = function(){          //扩展event对象,代理preventDefault  stopImmediatePropagation   stopPropagation方法 ,兼容浏览器不支持,同时做其他事情this[predicate] = returnTrue     //如果执行了3方法,原生事件对象isDefaultPrevented  isImmediatePropagationStopped  isPropagationStopped 三方法标记truereturn sourceMethod && sourceMethod.apply(source, arguments)  //且调用原生方法
        }event[predicate] = returnFalse   //扩展原生事件对象  isDefaultPrevented  isImmediatePropagationStopped  isPropagationStopped三方法,默认返回false。
      })//如果浏览器支持  defaultPrevented DOM3 EVENT提出的能否取消默认行为if (source.defaultPrevented !== undefined ? source.defaultPrevented :'returnValue' in source ? source.returnValue === false :source.getPreventDefault && source.getPreventDefault())event.isDefaultPrevented = returnTrue    //默认可以取消
    }//返回修正对象return event}/*** 创建事件代理* @param event Event对象* @returns {*}*/function createProxy(event) {var key, proxy = { originalEvent: event } //存储原始eventfor (key in event)//复制event属性至proxy,ignoreProperties里包含的属性除外if (!ignoreProperties.test(key) && event[key] !== undefined) proxy[key] = event[key]//
    return compatible(proxy, event)}/*** 小范围冒泡绑定事件,应直接采用on*/$.fn.delegate = function(selector, event, callback){return this.on(event, selector, callback)}/***  解绑事件,应直接用off*/$.fn.undelegate = function(selector, event, callback){return this.off(event, selector, callback)}/***  冒泡到document.body绑定事件,应直接采用on* @param event* @param callback* @returns {*}*/$.fn.live = function(event, callback){$(document.body).delegate(this.selector, event, callback)return this}/***  在doument.body解绑事件,应直接用off*/$.fn.die = function(event, callback){$(document.body).undelegate(this.selector, event, callback)return this}/*** 扩展Zepto on监听事件方法* 元素上绑定一个或多个事件的事件处理函数* 注意: 方法参数不应超过5个,超过5个,应该用arguments。5个是惯例。if或for或闭包嵌套层也不应超过5层* @param event 事件集 字符串/* @param selector 子选择器* @param data  event.data* @param callback          事件响应函数* @param one        内部用, $.fn.one用。标记一次性事件* @returns {*}*/$.fn.on = function(event, selector, data, callback, one){var autoRemove, delegator, $this = this//event是对象{click:fn},支持这种方式我觉得没多大用if (event && !isString(event)) {$.each(event, function(type, fn){$this.on(type, selector, data, fn, one)})return $this}//选择器非字符串  callback非方法//未传data    on('click','.ss',function(){})if (!isString(selector) && !isFunction(callback) && callback !== false)callback = data, data = selector, selector = undefined//data传了function    或未传if (callback === undefined || data === false)callback = data, data = undefined//callback传了false,转换成false函数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){//以element元素为容器,以事件源为起点,往上冒泡找到匹配selector的元素// match  响应函数对应的事件源var evt, match = $(e.target).closest(selector, element).get(0)//    selector能找到,且不是容器,即不是绑定事件的上下文,即$('.parent').on('click','.son',fn)形式。开始处理委托。if (match && match !== element) {//createProxy(e) 创建event代理对象  currentTarget指向selector元素,liveFired指向绑定事件的容器elementevt = $.extend(createProxy(e), {currentTarget: match, liveFired: element})//执行事件响应函数//autoRemove触发一次事件响应函数后自动销毁。 callback触发事件响应函数// [evt].concat(slice.call(arguments, 1))响应函数的参数数组return (autoRemove || callback).apply(match, [evt].concat(slice.call(arguments, 1)))}}add(element, event, callback, data, selector, delegator || autoRemove)})}/*** 移除事件响应函数* @param event* @param selector* @param callback* @returns {*}*/$.fn.off = function(event, selector, callback){var $this = this//是对象,遍历移除if (event && !isString(event)) {$.each(event, function(type, fn){$this.off(type, selector, fn)})return $this}// 是函数if (!isString(selector) && !isFunction(callback) && callback !== false)callback = selector, selector = undefinedif (callback === false) callback = returnFalsereturn $this.each(function(){//元素遍历移除remove(this, event, callback, selector)})}/*** 触发事件* @param event 事件类型* @param args* @returns {*}*/$.fn.trigger = function(event, args){//修正event为事件对象event = (isString(event) || $.isPlainObject(event)) ? $.Event(event) : compatible(event)//传参event._args = argsreturn this.each(function(){// handle focus(), blur() by calling them directly//如果事件是focus blurif (event.type in focus && typeof this[event.type] == "function") this[event.type]()// items in the collection might not be DOM elements// 支持浏览器原生触发事件API//DOM源码
//        /**
//         @param {Event} event
//         @return {boolean}
//         */
//        EventTarget.prototype.dispatchEvent = function(event) {};else if ('dispatchEvent' in this) this.dispatchEvent(event)//模拟触发事件else $(this).triggerHandler(event, args)})}// triggers event handlers on current element just as if an event occurred,// doesn't trigger an actual event, doesn't bubble/*** 触发事件,不能冒泡* @param event  event对象* @param args 传参* @returns {*}*/$.fn.triggerHandler = function(event, args){var e, resultthis.each(function(i, element){//修正事件对象e = createProxy(isString(event) ? $.Event(event) : event)e._args = argse.target = element//找到此元素上此事件类型上的事件响应函数集,遍历,触发$.each(findHandlers(element, event.type || event), function(i, handler){//调用 handler.proxy执行事件result = handler.proxy(e)//如果event调用了immediatePropagationStopped(),终止后续事件的响应if (e.isImmediatePropagationStopped()) return false})})return result}// shortcut methods for `.bind(event, fn)` for each event type/*** 给常用事件生成便捷方法* @param event* @param args* @returns {*}*/;('focusin focusout focus blur load resize scroll unload click dblclick '+'mousedown mouseup mousemove mouseover mouseout mouseenter mouseleave '+'change select keydown keypress keyup error').split(' ').forEach(function(event) {$.fn[event] = function(callback) {return (0 in arguments) ?//有callback回调,是绑定事件,否则,触发事件  ,// 不用on?on才通用啊 ,bind也是调用on//$.fn.bind = function(event, data, callback){
//             return this.on(event, data, callback)
//           }this.bind(event, callback) :this.trigger(event)}})/***  创建Event对象* @param type* @param props 扩展到Event对象上的属性* @returns {*}* @constructor*/$.Event = function(type, props) {//当type是个对象时if (!isString(type)) props = type, type = props.type//对应 specialEvents.click = specialEvents.mousedown = specialEvents.mouseup = specialEvents.mousemove = 'MouseEvents'//创建event对象,如果是click,mousedown,mouseup mousemove,创建为MouseEvent对象,bubbles设为冒泡//TODO: 为什么要把这些事件单独拎出来?var event = document.createEvent(specialEvents[type] || 'Events'), bubbles = true// (name == 'bubbles') ? (bubbles = !!props[name])如果是冒泡,确保是true/false    浏览器只识别true、false, !!props[name]明确进行类型转换// event[name] = props[name] props属性扩展到event对象上if (props) for (var name in props) (name == 'bubbles') ? (bubbles = !!props[name]) : (event[name] = props[name])//初始化event对象,type为事件类型,如click,bubbles为是否冒泡,第三个参数表示是否可以用preventDefault方法来取消默认操作//初始化event对象,type:事件类型   如click  bubbles能否  true:   能否使用preventDefault取消浏览器默认操作//附上DOM源码/*@browser Gecko@param {string} eventTypeArg@param {boolean} canBubbleArg@param {boolean} cancelableArg*///Event.prototype.initEvent = function(eventTypeArg,canBubbleArg,cancelableArg) {};event.initEvent(type, bubbles, true)//添加isDefaultPrevented方法,event.defaultPrevented返回一个布尔值,表明当前事件的默认动作是否被取消,也就是是否执行了 event.preventDefault()方法.return compatible(event)}})(Zepto)

三大核心方法on/off/trigger流程解析

浏览器原生支持自定义事件举例(Zepto自定义事件/浏览器事件原理)

        var element =  document.body;   //绑定事件元素var handler = function(e,a,b){     //事件响应函数alert('监听成功 a:' +a+'  b:'+b +' e.data:'+ JSON.stringify(e.data));}//监听事件element.addEventListener('add',function(e){e.data = {wo:1};  //对监听事件进行传参handler.apply(element, e._args == undefined ? [e] : [e].concat(e._args));},false);//抛出自定义事件var addEvent = document.createEvent('Events');addEvent.initEvent('add',true,true);addEvent._args = [2,3];  //传参element.dispatchEvent(addEvent);

方法图 

转载于:https://www.cnblogs.com/mominger/p/4384692.html

Zepto源码分析-event模块相关推荐

  1. jQuery源码分析--Event模块(1)

    jQuery的Event模块提供了强大的功能:事件代理,自定义事件,自定义数据等.今天记录一下它实现的原理. 我们都知道,在js的原生事件中,有事件对象和回调函数这两样东西.但是事件对象是只读的,所以 ...

  2. 读Zepto源码之Deferred模块

    Deferred 模块也不是必备的模块,但是 ajax 模块中,要用到 promise 风格,必需引入 Deferred 模块.Deferred 也用到了上一篇文章<读Zepto源码之Callb ...

  3. 读Zepto源码之Ajax模块 1

    Ajax 模块也是经常会用到的模块,Ajax 模块中包含了 jsonp 的现实,和 XMLHttpRequest 的封装. 读 Zepto 源码系列文章已经放到了github上,欢迎star: rea ...

  4. zepto ajax php实例,读Zepto源码之Ajax模块

    Ajax 模块也是经常会用到的模块,Ajax 模块中包含了 jsonp 的现实,和 XMLHttpRequest 的封装. 读 Zepto 源码系列文章已经放到了github上,欢迎star: rea ...

  5. nginx源码分析之模块初始化

    在nginx启动过程中,模块的初始化是整个启动过程中的重要部分,而且了解了模块初始化的过程对应后面具体分析各个模块会有事半功倍的效果.在我看来,分析源码来了解模块的初始化是最直接不过的了,所以下面主要 ...

  6. Fabric源码分析-共识模块

    正好这些天要有一个需求要帮客户魔改Fabric-v0.6,把一些hyperchain的高级特性移植过去,借此机会把之前看过的源码在梳理一下. 下面就是对Fabric共识模块的源码分析和梳理,代码都是以 ...

  7. koa源码分析-co模块以及thunk

    Thunk以及CO模块 co4.0之前都是返回的thunk函数 之后的都是返回promise thunk thunk:在 JavaScript 语言中,Thunk 函数替换的是将多参数函数,替换成单参 ...

  8. Python3.5源码分析-sys模块及site模块导入

    Python3源码分析 本文环境python3.5.2. 参考书籍<<Python源码剖析>> python官网 Python3的sys模块初始化 根据分析完成builtins ...

  9. [Abp vNext 源码分析] - 2. 模块系统的变化

    一.简要说明 本篇文章主要分析 Abp vNext 当中的模块系统,从类型构造层面上来看,Abp vNext 当中不再只是单纯的通过 AbpModuleManager 来管理其他的模块,它现在则是 I ...

最新文章

  1. 如何把使用到android res文件夹下面资源(R.xx.xx)的工程打包成jar文件,供其它项目使用...
  2. libc++abi.dylib: terminate_handler unexpectedly
  3. Python--12 内嵌函数和闭包
  4. 数据库开发——MySQL——多表查询
  5. shell写入文件,后面会覆盖前面
  6. java对象赋值优雅写法_看看人家写的API,那才叫优雅!
  7. Linux内核使用的字符串转整形数和16进制数
  8. sqlserver 登录名迁移
  9. sql 分组 行列转换
  10. 关于改进邮政编码的建议 2018-03-08
  11. 车架号校验 php代码,VIN车辆识别代码查询
  12. Java实现控制台版CS
  13. Nginx配置SSL支持htpps访问
  14. iReport制作EXCEL、PDF或者HTML文件
  15. 如何将点云asc文件转换为pcd文件
  16. 国外lead教程—EMU之dating类offer分析
  17. Java卸载删除(2023最强版)
  18. 超简单的_ps抠图_在线抠图工具_智能抠图_速抠图
  19. linux下discus/apache服务器,局域网内无法访问--解决办法
  20. Android 改造无线电,浅谈Android平台的物联网网关方案设计

热门文章

  1. qchart画完以后删除_画错了,重新画一幅吧!”这句话对学画画的孩子来说,伤害有多大?...
  2. linux 内存 实例,内存管理与使用实例
  3. php的const,php中const入门
  4. 德赛西威与智驾科技MAXIEYE发布“九逵计划”,部署商用车自动驾驶技术及服务...
  5. 我在亦庄见了说中文的爱因斯坦,会比心的熊猫,还有一场意念打字大赛
  6. 索尼AI CEO:我们要让AI在30年内拿到诺贝尔奖
  7. ICLR 2020将采用远程会议,首次在非洲办会可能就这样泡汤了
  8. 真AI用钱表达:这家少年班毕业生创办的AI公司三年就盈利,增速300%
  9. Lyft开源L5自动驾驶数据集:55000个人工标注的3D注释框架,还有高清空间语义地图...
  10. ubuntu16.04忘了root密码