这个对象是对事件的基础封装,在实际使用中,我们往往不从这里去引用,而更多的是从Ext.EventManager中引用,这个只是为Ext.EventManager的接口做准备,这里定义:添加监听,删除监听,取消冒泡,阻止事件默认行为,事件目标,事件类型,事件源,以及鼠标事件的客户区坐标位置,屏幕坐标位置,键码,字符编码等方面对浏览器兼容性进行了处理,事件是兼容性最差的一块,所以再写自己应用的时候一定要多查相关资料,还是Nicholas C. Zakas《javascript 高级程序设计》第二版 第12章,可以说EXT源码中涉及到的,这本书里面都有说到!所以我这里也没有每个方法都去解释一遍
Ext.lib.Event = function() { //一堆的变量/常量的定义,有些我们可能觉得是没必要的,但Ext就是这么做,如SCROLLLEFT 这样的,但win和doc我是觉得完全有必要的,这样在压缩的时候能减少字节数,同时也能减少对象查找,这里定义后查找的函数内的局部变量,但不定义查询的是全局变量,所以EXT这么做都是又其道理的 var loadComplete = false, unloadListeners = {}, retryCount = 0, onAvailStack = [], _interval, locked = false, win = window, doc = document, // constants POLL_RETRYS = 200, POLL_INTERVAL = 20, EL = 0, TYPE = 0, FN = 1, WFN = 2, OBJ = 2, ADJ_SCOPE = 3, SCROLLLEFT = 'scrollLeft', SCROLLTOP = 'scrollTop', UNLOAD = 'unload', MOUSEOVER = 'mouseover', MOUSEOUT = 'mouseout', // private //事件监听的简单封装 doAdd = function() { var ret; if (win.addEventListener) { ret = function(el, eventName, fn, capture) { if (eventName == 'mouseenter') {//ie专有的事件,只有第一次才被激活,这里通过MOUSEOVER模拟mouseenter事件,这里的做法是通过拦截器实现 fn = fn.createInterceptor(checkRelatedTarget); el.addEventListener(MOUSEOVER, fn, (capture)); } else if (eventName == 'mouseleave') {//ie专有的事件,只有第一次才被激活 fn = fn.createInterceptor(checkRelatedTarget); el.addEventListener(MOUSEOUT, fn, (capture)); } else { el.addEventListener(eventName, fn, (capture)); } return fn; }; } else if (win.attachEvent) { ret = function(el, eventName, fn, capture) { el.attachEvent("on" + eventName, fn); return fn; }; } else { ret = function(){}; } return ret; }(), // private doRemove = function(){ var ret; if (win.removeEventListener) { ret = function (el, eventName, fn, capture) { if (eventName == 'mouseenter') {//因为添加是由MOUSEOVER模拟的所以这里删除就只要删除MOUSEOVER就行了 eventName = MOUSEOVER; } else if (eventName == 'mouseleave') { eventName = MOUSEOUT; } el.removeEventListener(eventName, fn, (capture)); }; } else if (win.detachEvent) { ret = function (el, eventName, fn) { el.detachEvent("on" + eventName, fn); }; } else { ret = function(){}; } return ret; }(); function checkRelatedTarget(e) { return !elContains(e.currentTarget, pub.getRelatedTarget(e)); } //这里为什么这么写,我又点迷惑,为什么不像 Ext.lib.Dom 中 isAncestor一样写,估计是EXT开发人员自己忽视了 function elContains(parent, child) { if(parent && parent.firstChild){ while(child) { if(child === parent) { return true; } child = child.parentNode; if(child && (child.nodeType != 1)) { child = null; } } } return false; } // private function _tryPreloadAttach() { var ret = false, notAvail = [], element, i, len, v, tryAgain = !loadComplete || (retryCount > 0); if (!locked) { locked = true; for (i = 0, len = onAvailStack.length; i < len; i++) { v = onAvailStack[i]; if(v && (element = doc.getElementById(v.id))){ if(!v.checkReady || loadComplete || element.nextSibling || (doc && doc.body)) { element = v.override ? (v.override === true ? v.obj : v.override) : element; v.fn.call(element, v.obj); onAvailStack.remove(v); } else { notAvail.push(v); } } } retryCount = (notAvail.length === 0) ? 0 : retryCount - 1; if (tryAgain) { startInterval(); } else { clearInterval(_interval); _interval = null; } ret = !(locked = false); } return ret; } // private function startInterval() { if(!_interval){ var callback = function() { _tryPreloadAttach(); }; _interval = setInterval(callback, POLL_INTERVAL); } } // private function getScroll() { var dd = doc.documentElement, db = doc.body; if(dd && (dd[SCROLLTOP] || dd[SCROLLLEFT])){ return [dd[SCROLLLEFT], dd[SCROLLTOP]]; }else if(db){ return [db[SCROLLLEFT], db[SCROLLTOP]]; }else{ return [0, 0]; } } // private function getPageCoord (ev, xy) { ev = ev.browserEvent || ev; var coord = ev['page' + xy]; if (!coord && coord !== 0) { coord = ev['client' + xy] || 0; if (Ext.isIE) { coord += getScroll()[xy == "X" ? 0 : 1]; } } return coord; } var pub = { extAdapter: true, onAvailable : function(p_id, p_fn, p_obj, p_override) { onAvailStack.push({ id: p_id, fn: p_fn, obj: p_obj, override: p_override, checkReady: false }); retryCount = POLL_RETRYS; startInterval(); }, //这个接口一般用户不直接调用,而是调用Ext.EventManager // This function should ALWAYS be called from Ext.EventManager addListener: function(el, eventName, fn) { el = Ext.getDom(el); if (el && fn) { if (eventName == UNLOAD) { if (unloadListeners[el.id] === undefined) { unloadListeners[el.id] = []; } unloadListeners[el.id].push([eventName, fn]); return fn; } return doAdd(el, eventName, fn, false); } return false; }, // This function should ALWAYS be called from Ext.EventManager removeListener: function(el, eventName, fn) { el = Ext.getDom(el); var i, len, li, lis; if (el && fn) { if(eventName == UNLOAD){ if((lis = unloadListeners[el.id]) !== undefined){ for(i = 0, len = lis.length; i < len; i++){ if((li = lis[i]) && li[TYPE] == eventName && li[FN] == fn){ unloadListeners[id].splice(i, 1); } } } return; } doRemove(el, eventName, fn, false); } }, //返回Target getTarget : function(ev) { ev = ev.browserEvent || ev; return this.resolveTextNode(ev.target || ev.srcElement); }, resolveTextNode : Ext.isGecko ? function(node){ if(!node){ return; } // work around firefox bug, https://bugzilla.mozilla.org/show_bug.cgi?id=101197 var s = HTMLElement.prototype.toString.call(node); if(s == '[xpconnect wrapped native prototype]' || s == '[object XULElement]'){ return; } return node.nodeType == 3 ? node.parentNode : node; } : function(node){ return node && node.nodeType == 3 ? node.parentNode : node; }, //返回相关/临近的Target getRelatedTarget : function(ev) { ev = ev.browserEvent || ev; return this.resolveTextNode(ev.relatedTarget || (ev.type == MOUSEOUT ? ev.toElement : ev.type == MOUSEOVER ? ev.fromElement : null)); }, getPageX : function(ev) { return getPageCoord(ev, "X"); }, getPageY : function(ev) { return getPageCoord(ev, "Y"); }, getXY : function(ev) { return [this.getPageX(ev), this.getPageY(ev)]; }, stopEvent : function(ev) { this.stopPropagation(ev); this.preventDefault(ev); }, stopPropagation : function(ev) { ev = ev.browserEvent || ev; if (ev.stopPropagation) { ev.stopPropagation(); } else { ev.cancelBubble = true; } }, preventDefault : function(ev) { ev = ev.browserEvent || ev; if (ev.preventDefault) { ev.preventDefault(); } else { ev.returnValue = false; } }, getEvent : function(e) { e = e || win.event; if (!e) { var c = this.getEvent.caller; while (c) { e = c.arguments[0]; if (e && Event == e.constructor) { break; } c = c.caller; } } return e; }, getCharCode : function(ev) { ev = ev.browserEvent || ev; return ev.charCode || ev.keyCode || 0; }, //clearCache: function() {}, // deprecated, call from EventManager getListeners : function(el, eventName) { Ext.EventManager.getListeners(el, eventName); }, // deprecated, call from EventManager purgeElement : function(el, recurse, eventName) { Ext.EventManager.purgeElement(el, recurse, eventName); }, _load : function(e) { loadComplete = true; var EU = Ext.lib.Event; if (Ext.isIE && e !== true) { // IE8 complains that _load is null or not an object // so lets remove self via arguments.callee doRemove(win, "load", arguments.callee); } }, _unload : function(e) { var EU = Ext.lib.Event, i, j, l, v, ul, id, len, index, scope; for (id in unloadListeners) { ul = unloadListeners[id]; for (i = 0, len = ul.length; i < len; i++) { v = ul[i]; if (v) { try{ scope = v[ADJ_SCOPE] ? (v[ADJ_SCOPE] === true ? v[OBJ] : v[ADJ_SCOPE]) : win; v[FN].call(scope, EU.getEvent(e), v[OBJ]); }catch(ex){} } } }; unloadListeners = null; Ext.EventManager._unload(); doRemove(win, UNLOAD, EU._unload); } }; // Initialize stuff. pub.on = pub.addListener; pub.un = pub.removeListener; if (doc && doc.body) { pub._load(true); } else { doAdd(win, "load", pub._load); } doAdd(win, UNLOAD, pub._unload); _tryPreloadAttach(); return pub; }();