在ExtJs源码分析与学习—ExtJs事件机制(一)中分析了ExtJs对原生浏览器事件的封装。这篇进一步分析ExtJs对事件的封装和扩充。ExtJs会对浏览器本身的事件进行转换,是通过类Ext.EventObject来实现的,该类中通过自执行匿名函数返回Ext.EventObjectImpl对象,该对象用到了Ext.lib.Event(对原生浏览器事件的扩展)。
- Ext.EventObject = function(){
- var E = Ext.lib.Event,
- …
- Ext.EventObjectImpl = function(e){
- if(e){
- this.setEvent(e.browserEvent || e);
- }
- };
-
- Ext.EventObjectImpl.prototype = {
- …
- };
-
- return new Ext.EventObjectImpl();
- }();
下面看Ext.EventObject中代码的实现
-
- safariKeys = {
- 3 : 13,
- 63234 : 37,
- 63235 : 39,
- 63232 : 38,
- 63233 : 40,
- 63276 : 33,
- 63277 : 34,
- 63272 : 46,
- 63273 : 36,
- 63275 : 35
- },
该对象是为了兼容旧版本safari浏览器对部分键的按键事件返回值的处理,与其他浏览器的统一。
-
- btnMap = Ext.isIE ? {1:0,4:1,2:2} : {0:0,1:1,2:2};
由于IE浏览器与其他浏览器的按键值(e.button)不同,所以定义该对象是为了实现所有浏览器的兼容。兼容后,0为左键,1为中键,2为右键。
接下来是类Ext.EventObjectImpl的定义
- Ext.EventObjectImpl = function(e){
- if(e){
- this.setEvent(e.browserEvent || e);
- }
- };
该类中对浏览器原生事件e进行了包装,调用了private method setEvent,setEvent是定义在Ext.EventObjectImpl.prototype下的
-
- setEvent : function(e){
- var me = this;
- if(e == me || (e && e.browserEvent)){
- return e;
- }
- me.browserEvent = e;
- if(e){
-
-
- me.button = e.button ? btnMap[e.button] : (e.which ? e.which - 1 : -1);
- if(clickRe.test(e.type) && me.button == -1){
- me.button = 0;
- }
- me.type = e.type;
-
- me.shiftKey = e.shiftKey;
-
- me.ctrlKey = e.ctrlKey || e.metaKey || false;
- me.altKey = e.altKey;
-
-
-
- me.keyCode = e.keyCode;
- me.charCode = e.charCode;
-
-
-
- me.target = E.getTarget(e);
-
- me.xy = E.getXY(e);
- }else{
- me.button = -1;
- me.shiftKey = false;
- me.ctrlKey = false;
- me.altKey = false;
- me.keyCode = 0;
- me.charCode = 0;
- me.target = null;
- me.xy = [0, 0];
- }
- return me;
- },
该方法首先判断事件是否包装过,如已经包装了,则直接返回该包装的事件。
me.browserEvent = e;//把浏览器的原始事件保存到browserEvent中
me.button = e.button ? btnMap[e.button] : (e.which ? e.which - 1 : -1);//e.which原本是键盘事件
if(clickRe.test(e.type) && me.button == -1){
me.button = 0;
}
该段代码处理了不同按键返回不同的e.button
其他代码的功能,可参见代码注释
接着看stopEvent方法
- stopEvent : function(){
- var me = this;
- if(me.browserEvent){
- if(me.browserEvent.type == 'mousedown'){
- Ext.EventManager.stoppedMouseDownEvent.fire(me);
- }
- E.stopEvent(me.browserEvent);
- }
- },
停止事件(preventDefault和stopPropagation)。用来停止事件冒泡,阻止元素默认行为。需要说明的是对mousedown事件做了特殊处理Ext.EventManager.stoppedMouseDownEvent 实际是Ext.util.Event类的一个实例对象。如pub.stoppedMouseDownEvent = new Ext.util.Event();后续讲到Ext.EventManager时,会讲fire方法。
-
-
-
- preventDefault : function(){
- if(this.browserEvent){
- E.preventDefault(this.browserEvent);
- }
- },
-
-
-
-
- stopPropagation : function(){
- var me = this;
- if(me.browserEvent){
- if(me.browserEvent.type == 'mousedown'){
- Ext.EventManager.stoppedMouseDownEvent.fire(me);
- }
- E.stopPropagation(me.browserEvent);
- }
- },
对preventDefault(阻止浏览器默认行为处理事件)和stopPropagation(取消事件冒泡)在该类中的实现,实际上还是调用Ext.lib.Event中的原生方法
接着看其他代码
-
-
-
-
-
- getCharCode : function(){
- return this.charCode || this.keyCode;
- },
-
-
-
-
-
-
- getKey : function(){
- var k = this.keyCode || this.charCode;
- return Ext.isSafari ? (safariKeys[k] || k) : k;
- },
-
-
-
-
-
-
- getPageX : function(){
- return this.xy[0];
- },
-
-
-
-
-
-
- getPageY : function(){
- return this.xy[1];
- },
-
-
-
-
-
-
- getTime : function(){
- if(this.browserEvent){
- return E.getTime(this.browserEvent);
- }
- return null;
- },
-
-
-
-
-
-
- getXY : function(){
- return this.xy;
- },
-
-
-
-
-
-
-
-
-
- getTarget : function(selector, maxDepth, returnEl){
- return selector ? Ext.fly(this.target).findParent(selector, maxDepth, returnEl) : (returnEl ? Ext.get(this.target) : this.target);
- },
-
-
-
-
-
-
-
- getRelatedTarget : function(){
- return this.browserEvent ? E.getRelatedTarget(this.browserEvent) : null;
- },
-
-
-
-
-
-
-
-
- getWheelDelta : function(){
- var e = this.browserEvent;
- var delta = 0;
- if(e.wheelDelta){
- delta = e.wheelDelta/120;
- }else if(e.detail){
- delta = -e.detail/3;
- }
- return delta;
- },
getTarget 方法用到了Ext.fly,一个关键的方法,利用共享模式实现的,后续待讲
下面看方法within
- within : function(el, related, allowEl){
- if(el){
- var t = this[related ? "getRelatedTarget" : "getTarget"]();
- return t && ((allowEl ? (t == Ext.getDom(el)) : false) || Ext.fly(el).contains(t));
- }
- return false;
- }
如果该事件的目标对象是el的子元素,则返回true;如果allowEl设为true时,该事件的目标对象等于el也返回true。关于这个方法的理解,请看例子:
- <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
- <html>
- <head>
- <title>Ext.EventObject.within method demo</title>
- <meta http-equiv="content-type" content="text/html; charset=UTF-8">
- <link rel="stylesheet" type="text/css"
- href="../ext-3.3.1/resources/css/ext-all.css" />
- <style type="text/css">
- div {
- border: 1px solid blue;
- padding: 40px;
- margin: 10px;
- }
- </style>
-
- <script type="text/javascript"
- src="../ext-3.3.1/adapter/ext/ext-base-debug.js"></script>
- <script type="text/javascript"
- src="../ext-3.3.1/ext-all-debug-w-comments.js"></script>
- <script type="text/javascript"
- src="../ext-3.3.1/src/locale/ext-lang-zh_CN.js"></script>
- <script type="text/javascript" src="../ext-3.3.1/src/debug.js"></script>
-
- <script type="text/javascript">
- Ext.onReady(function() {
- Ext.BLANK_IMAGE_URL = '../ext-3.3.1/resources/images/default/s.gif';
- Ext.QuickTips.init();
- Ext.getBody().on('click', function(e){
- if(e.within('some-el')){//如果该事件的目标对象是some-el的子元素,则返回true
- alert('是在some-el的孩子节点上点击的!');
- }else{
- alert('你的点击不是some-el的孩子节点');
- }
- });
-
- Ext.getBody().on('click', function(e,t){
- if((t.id == 'some-el') && !e.within(t, true)){//如果allowEl设为true时,该事件的目标对象等于el也返回true
- alert('直接在some-el上点击了鼠标!');
- }
- });
- });
-
- </script>
- </head>
-
- <body>
- <div id="some-el">
- <div id="some-el2">
- Ext.EventObject.within demo
- </div>
- </div>
- <br/>
- <div id="some-el3">
- <div id="some-el4">
- 不是some-el节点
- </div>
- </div>
- </body>
- </html>
以上介绍了Ext事件 Ext.EventObject 对浏览器原生事件的包装和扩展,实现了个浏览器之间的兼容。