Event in Zepto

你有想过没,当你监听某个DOM元素的一个事件时,其事件处理函数是如何和该DOM元素关联起来的呢:

1 var wp=document.getElementById(‘wrapper’);
2 wp.addEventListener(‘click’,function(){
3       // event handler
4 });

你又想过没,当你监听某个对象上的自定义事件时,其事件处理函数是如何和该对象关联起来的, 事件是如何被触发的,这背后的库,又做了什么呢:

1 var  obj={}
2 $(obj).on(‘fire’,function(){
3     // event handler
4 })

带着这些问题,我们以zepto库为原型,从代码实现的角度来一窥究竟:

首先,我们构造一个mini库,以$记之吧.为简单起见,它只做两件事:id选择器,each方法:

 1 <script>
 2     $ = (function () {
 3         function typestr(o) {
 4             var s = Object.prototype.toString.call(o);
 5             if (s == "[object Object]") return “object”;
 6             else if (s == "[object String]") return “string”;
 7             else;
 8         }
 9         var _$ = function (node) {
10             if (typestr(node) == “string”) node = document.getElementById(node.slice(1));
11             var rev = [node];
12             rev.__proto__ = _$.fn;
13
14             return rev;
15         }
16         _$.fn = {
17             each: function (callback) {
18                 for (var i = 0; i < this.length; i++) {
19                     callback(this[i], i);
20                 }
21             }
22         };
23         return _$;
24     })();</script>

下面是核心部分,我们先完成准备工作,声明一个计数器和一个对象来维护事件,然后给出基本骨架:

 1 <script>
 2  (function($){
 3     var handlers = {};
 4     var _zid = 1;
 5     function zid(element) {
 6         return element._zid || (element._zid = _zid++);
 7 }
 8 function add(element,event,callback){
 9     // 内部添加事件
10 }
11 function remove(element,event,callback){
12    //内部删除事件
13 }
14 $.fn.on=function(event,callback,one){
15    //对外公开监听事件方法
16    //add
17 }
18 $.fn.off=function(event,callback){
19    //对外公开移除事件的方法
20    //remove
21 }
22 $.fn.one=function(event,callback){
23 }
24 $.fn.trigger=function(event){
25      //对外公开触发事件的方法
26 }
27 })($);
28 </script>

add方法:

 1 function add(element, event, callback) {
 2         var id = zid(element), set = (handlers[id] || (handlers[id] = [])), handler = {};
 3         handler.en = event;
 4         handler.ev = callback;
 5         handler.i=set.length;
 6         set.push(handler);
 7         if (‘addEventListener‘ in element) {
 8             element.addEventListener(handler.en,callback, false);
 9         }
10 }

如果某元素注册过事件,就通过它的_zid值去handlers中找到事件队列,将新的事件对象添加进队列;如果该元素没注册过事件,则在handlers中开辟一个以_zid关联的新队列,再将事件对象添加进队列. 事件队列的长度正好是新添加事件对象在事件队列中的位置,记录该位置,可方便后面从事件队列中删除该事件对象.

这里的’元素’指的是对象,因为DOM元素上的事件是用addEventListener方法来通知浏览器,让浏览器来为我们来作类似的事情.

remove方法:

 1 function remove(element,event,callback){
 2       var id=zid(element);
 3       var set=handlers[id]||[];
 4       set.forEach(function(handler){
 5         if(handler.en==event){
 6            delete set[handler.i];
 7            if("removeEventListener" in element){
 8              element.removeEventListener(event,callback,false);
 9             }
10        }
11       });
12 }

和add方法作相反的事情,对象和DOM元素也是分别对待:

两个核心方法讲完,看看对外公开的几个方法:

on/one/off方法:

 1     $.fn.on = function (event, callback,one) {
 2         var cbx=callback;
 3         this.each(function (elem, index) {
 4             if(one){
 5                 cbx=function(){
 6                     callback();
 7                     remove(elem,event,cbx);
 8
 9                 };
10             }
11                 add(elem, event,cbx);
12         });
13
14     };
15     $.fn.one=function(event,callback){
16        this.on(event,callback,1);
17     };
18
19     $.fn.off=function(event,callback){
20         this.each(function (elem, index) {
21             remove(elem, event, callback);
22         });
23     };

在on方法里给one方法预留了一个判断 ,在执行callback一次后,就remove掉该事件,该事件就不会再次被触发;

trigger方法:

 1  $.fn.trigger = function (event, args) {
 2         var elem = this[0],set = handlers[zid(element)],len=set.length,handler;
 3         for (var i = 0; i <len; i++) { //forEach
 4             handler=set[i];
 5             if(handler.en==event){
 6                 if(‘dispatchEvent‘ in elem) elem.dispatchEvent(event)
 7                 handler.ev.call(this,args);
 8             }
 9         }
10  };

通过计数器去查看元素的_zid值,然后去handlers中查找事件队列,循环事件队列,执行相应处理函数,如果是DOM元素,用dispatchEvent方法来告知浏览器触发事件.

就上面看来,大部分代码是用来解决如何通过维护事件队列来监听,移除,触发一个对象上的事件.对于DOM元素上的事件来说,我们只是通过addEventListener方法告知浏览器,要注册事件.通过removeEventListener方法告知浏览器,要移除事件了,但浏览器是如何维护它的事件队列的,对于我们来讲,是透明的.另外事件的触发也靠浏览器的自身的机制去完成的,例如,浏览器如果检测到一个DOM元素被单击了,它会去触发click事件以执行相应的处理函数.也就是说,浏览器形为和事件之间是有对应或契约关系的.我们常见的DOM元素上面一些默认的事件,都是以这种方式来处理的.

上面的代码为了做到尽可能的简单,很多地方做了简化,这里要提一点的是,自定义事件的用法:

 1 // Create the event.
 2 var event = document.createEvent(‘Event‘);
 3 // Define that the event name is ‘build‘.
 4 event.initEvent(‘build‘, true, true);
 5 // Listen for the event.
 6 document.addEventListener(‘build‘, function (e) {
 7   // e.target matches document from above
 8 }, false);
 9 // target can be any Element or other EventTarget.
10 document.dispatchEvent(event);

相关内容参考: https://developer.mozilla.org/en-US/docs/Web/Guide/Events/Creating_and_triggering_events

移动端click事件问题

因为移动端的单击事件会延时,zepto的tap事件据说又很坑爹,所以,果断决定来模拟一个自己的tap事件:

 1 (function (root, $) {
 2            var x, y, target, startTime;
 3            root = $(root);
 4            root.bind(‘touchstart‘, function (e) {
 5                target = $(event.target);
 6                var touch = event.changedTouches[0];
 7                x = touch.pageX;
 8                y = touch.pageY;
 9                startTime = new Date().getTime();
10            }).bind(‘touchend‘, function (e) {
11                var touch = event.changedTouches[0];
12                var tx = (new Date().getTime() - startTime);
13                var cx = touch.pageX;
14                var cy = touch.pageY;
15                if (Math.abs(cx - x) <= 10 && Math.abs(cy - y) <= 10 && tx <= 500) {
16                    var ev = $.Event(‘tap‘);
17                    target.triggerHandler(ev);
18                }
19            });
20 })(document,zepto)

这里$.Event的内部实现其实就用到了上面提到的自定义事件.我们这里已经定义好了tap事件的触发时机,只待事件注册了.

改用tap注册事件,事件执行确实是快了,但是,它却带来了新的问题:

场景   当我们在一个弹出层的关闭按钮上面用tap注册了一个事件,功能是单击后,弹出层消失.

效果:  确实能让对弹出层消失,但是如果关闭按钮下方刚好有个文本框,或是有一个上面已经注册了其他事件的DOM元素,你会发现不被期望的事情发生了.要么是键盘弹出来了,要么是触发了DOM元素上的事件,页面跳转了.更有甚者,是导致页面跳转,触发了下个页面上元素的事件.

执行得太快,也是个错么?

这个问题一度的解决方案是,定义一个白色的透明层,执行tap事件时,立马把整个屏幕罩起来,0.8s后,移除遮罩:

1 /*#ng{position:fixed;top:0;left:0;width:100%;height:100%;background:#fff;opacity:0.0;z-index:1999;}*/
2 var ng=$(‘#ng’);
3 ng.show();
4 setTimeout(function(){ng.hide();},800)

后来受叶小钗同学一文的启发,还是用click事件:

1 if (Math.abs(cx - x) <= 10 && Math.abs(cy - y) <= 10 && tx <= 500) {
2    var ev = $.Event(‘click.me‘);
3    target.triggerHandler(ev);
4 }

为了避免click执行两次,在自定义的click事件里,我给加了个.me的别名,用intel XDK找了几款机型测试了下,暂时没发现什么问题,有兴趣的同学可以试试!

Event in Zepto

时间: 2024-10-07 19:05:41

Event in Zepto的相关文章

zepto源码

/** * * ┏┓ ┏┓ * ┏┛┻━━━┛┻┓ * ┃ ┃ * ┃ ━ ┃ * ┃ ┳┛ ┗┳ ┃ * ┃ ┃ * ┃ ┻ ┃ * ┃ ┃ * ┗━┓ ┏━┛Code is far away from bug with the animal protecting * ┃ ┃ 神兽保佑,代码无bug * ┃ ┃ * ┃ ┗━━━┓ * ┃ ┣┓ * ┃ ┏┛ * ┗┓┓┏━┳┓┏┛ * ┃┫┫ ┃┫┫ * ┗┻┛ ┗┻┛ * */ /* Zepto 1.1.4 - zepto event aj

zepto源码分析系列

如果你也开发移动端web,如果你也用zepto,应该值得你看看.有问题请留言. Zepto源码分析-架构 Zepto源码分析-zepto(DOM)模块 Zepto源码分析-callbacks模块 Zepto源码分析-event模块 Zepto源码分析-ajax模块 Zepto源码分析-form模块 Zepto源码分析-deferred模块 Zepto源码分析-动画(fx fx_method)模块 内容一定要200字一定要200字内容一定要200字一定要200字内容一定要200字一定要200字内容

C#学习日记24----事件(event)

事件为类和类的实例提供了向外界发送通知的能力,实现了对象与对象之间的通信,如果定义了一个事件成员,表示该类型具有 1.能够在事件中注册方法 (+=操作符实现). 2.能够在事件中注销方法(-=操作符实现). 3.当事件被触发时注册的方法会被通知(事件内部维护了一个注册方法列表).委托(Delegate)是事件(event)的载体,要定义事件就的要有委托.  有关委托的内容请点击 委托(De... www.mafengwo.cn/event/event.php?iid=4971258www.maf

【移动端兼容问题研究】javascript事件机制详解(涉及移动兼容)--转

前言 javascript事件基础 事件捕获/冒泡 事件对象 事件模拟 移动端响应速度 PC与移动端鼠标事件差异 touch与click响应速度问题 结论 zepto事件机制 注册/注销事件 zepto模拟tap事件 tap事件的问题一览 点透问题 fastclick思想提升点击响应 实现原理 鬼点击 ios与android鼠标事件差异 事件捕获解决鬼点击 结语 前言 这篇博客有点长,如果你是高手请您读一读,能对其中的一些误点提出来,以免我误人子弟,并且帮助我提高 如果你是javascript菜

zepto 事件分析1($.Event)

先看一下zepto事件的函数,在这里,zepto是把zepto对象作为一个立即执行函数的参数传进去的. (function($){ ... ... })(Zepto) 在zepto事件函数中,主要为$.Event,$.on,$.off等,分析事件代码,分析这三个就够了,首先分析$.Event函数,在分析该函数之前,从接口文档中大概知道这是一个创建事件的方法,可以先看一下原生的javascript代码是怎么生成一个事件的. var event = new Event('event'); //老式创

阻止长按复制页面中的内容;zepto中touch中的应用必须先加载event模块之后;

1 <!doctype html> 2 <html lang="en"> 3 <head> 4 <meta charset="UTF-8"> 5 <meta id="viewport" name="viewport" content="width=device-width, initial-scale=1.0, minimum-scale=1.0, maximum

Zepto源码分析-event模块

源码注释 // 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

zepto源码学习-04 event

之前说完$(XXX),然后还有很多零零碎碎的东西需要去分析,结果一看代码,发现zepto的实现都相对简单,没有太多可分析的.直接略过了一些实现,直接研究Event模块,相比JQuery的事件系统,zepto的设计相对简单很多,看起来也就相对轻松,整个event模块也就300行代码. 先看事件的相关接口以及用法 $.Event $.Event(type, [properties]) ⇒ event 创建并初始化一个指定的DOM事件.如果给定properties对象,使用它来扩展出新的事件对象.默认

zepto的touch模块解决click延迟300ms问题以及点透问题的详解

大家都知道移动端的click事件会延迟300ms触发,这时大家可以使用zepto的touch模块,里面定义了一个tap事件,通过绑定tap事件,可以实现点击立即触发的功能. 那么,它的tap事件是怎么实现的呢?这是我们要解决的第一个问题. 第二个问题,大家都知道zepto的tap事件会有点透的问题,那么,点透如何出现,点透为什么会出现,点透问题如何解决等,这是我们要解决的第二个问题. 我们先来看tap事件是如何实现的? 查看touch.js代码,在最后的代码中有以下代码: ;['swipe',