- 浏览: 321443 次
- 性别:
文章分类
最新评论
-
i042416:
分析在哪?
angular分析 -
何盆盆:
你好,请问您这是Extjs3还是Extjs4
ExtJs源码分析与学习—ExtJs事件机制(一) -
124753561:
引用引用引用引用引用[u][u][u][u][i][i][i] ...
Subvision SVN 服务端与客户端的安装 -
谷超:
请问一下例子中itext是什么版本的?
利用iText生成word文档例子参考 -
geosmart:
正好要用到执行字符串中方法,学习了
java中利用反射机制实现调用给定为字符串的方法名
前面讲了ExtJs核心代码以及扩展后,今天来说说ExtJs的事件机制,要想弄明白ExtJs的事件机制,就必须先知道浏览器的事件机制,这里给出了浏览器事件机制与自定义事件的实现 。
首先看源码 ext-base-event.js 关于浏览器本身事件的封装。代码中实现了各主要浏览器的兼容,以及对一些事件进行了扩展。该代码中首先定义了类Ext.lib.Event,该类(函数)是一个匿名函数自执行,执行后返回对象pub,pub赋值给Ext.lib.Event。
Ext.lib.Event = function() { var loadComplete = false, unloadListeners = {},//用来存放el的unload事件 … var pub = { … }; return pub; }();
既然该类是围绕pub来实现的,我们首先来看pub的定义,pub中定义了许多关于事件处理的方法
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(); },
该方法中调用了startInterval()和_tryPreloadAttach()。_tryPreloadAttach() 、 onAvailable() 、startInterval() 这三个函数的执行机制大致是这样的:在文档还没有加载完成之前,可以通过 onAvailable() 方法给某个对象注册某类事件的监听器, onAvailable() 方法会调用 startInterval() 方法来启动一个轮询来执行 _tryPreloadAttach() ,轮询的周期是 POLL_INTERVAL (默认是 20ms ),轮询次数是 POLL_RETRYS ( 200 次)(这么来看的话,这种依靠不断轮询来尝试文档是否已经加载完成的方法最长是 20ms*200=4 秒钟)。 _tryPreloadAttach() 方法里面会判断文档是不是已经加载完成,如果加载完成,执行注册的监听器,并把定时器清除掉。_tryPreloadAttach() 方法还有一个 tryAgain 标志用来说明是不是要进行再次尝试。
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; },
该方法为元素添加注册事件,el为添加事件的元素,eventName为事件名称(如click),fn为响应函数(hanlder)。对“unload”事件做了单独处理,内部调用了私有的doAdd函数。
doAdd = function() { var ret; if (win.addEventListener) {//标准浏览器 ret = function(el, eventName, fn, capture) { if (eventName == 'mouseenter') { fn = fn.createInterceptor(checkRelatedTarget); el.addEventListener(MOUSEOVER, fn, (capture)); } else if (eventName == 'mouseleave') { fn = fn.createInterceptor(checkRelatedTarget); el.addEventListener(MOUSEOUT, fn, (capture)); } else { el.addEventListener(eventName, fn, (capture)); } return fn; }; } else if (win.attachEvent) {//ie浏览器,ie9中会同时支持这两种方式win.attachEvent和win.addEventListener, ret = function(el, eventName, fn, capture) { el.attachEvent("on" + eventName, fn); return fn; }; } else { ret = function(){}; } return ret; }(),
该函数为闭包函数,即自执行函数,会返回ret对应的函数。并且添加了其他浏览器(IE除外)不支持的事件mouseenter和mouseleave ,为非IE浏览器间接实现了这两个事件,需要另两个函数的辅助。这两个辅助函数的实现也可以用到其他没有引入ExtJs的项目中。
function checkRelatedTarget(e) { return !elContains(e.currentTarget, pub.getRelatedTarget(e)); } //判断某个元素child是否是parent的子元素,是则返回true,否则false。 function elContains(parent, child) { if(parent && parent.firstChild){ while(child) { if(child === parent) { return true; } child = child.parentNode; //nodeType 属性返回被选节点的节点类型。等于1为节点Element,当child不是parent的孩子节点时,会一直执行,直到child.nodeType 为 9 document时 if(child && (child.nodeType != 1)) { child = null; } } } return false; }
elContains 两个参数parent,child判断某个元素child是否是parent的子元素,是则返回true,否则false。
checkRelatedTarget 会作为一个拦截器,这里e.currentTarget是指添加事件的元素本身。pub.getRelatedTarget(e)返回的是跟该事件相关的元素标准浏览器用relatedTarget IE中用fromElement,toElement。在Ext.lib.Dom中也有个实现判断父子元素的方法isAncestor,后续会讲到。不知道ExtJs为什么要写两个实现,个人推测这两段代码可能是两个人实现的,并且实现的原理有些不同,故保留了两个。
下面看getRelatedTarget的实现
getRelatedTarget : function(ev) { ev = ev.browserEvent || ev; return this.resolveTextNode(ev.relatedTarget || (/(mouseout|mouseleave)/.test(ev.type) ? ev.toElement : /(mouseover|mouseenter)/.test(ev.type) ? ev.fromElement : null)); },
这里有几个浏览器事件对象属性需说明一下
target 指事件源对象,点击嵌套元素最里层的某元素,该元素就是target。IE6/7/8对应的是srcElement。
currentTarget 指添加事件handler的元素本身,如el.addEventListener中el就是currentTarget。IE6/7/8没有对应属性,可在handler内使用this来替代如evt.currentTarget = this。
relativeTarget 指事件相关的元素,一般用在mouseover,mouseout事件中。IE6/7/8中对应的是fromElement,toElement。
getRelatedTarget 方法返回相关的元素结点,在该方法中resolveTextNode辅助函数又判断了火狐和其他浏览器的不同。
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; },
下面看与addListener对应的方法removeListener
/** * 删除 el 事件 */ 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[el.id].splice(i, 1); } } } return; } doRemove(el, eventName, fn, false); } },
该方法与addListener对应,用来删除由addListener注册的事件,实际调用是利用Ext.EventManager中的方法来调用,该类对这两个方法又进行了更进一步的封装,后续待分析。该方法中用到了辅助函数doRemove
/** * 返回一个符合当前浏览器的函数用来注销事件, 对于非ie下的浏览器也让其可以支持mouseleave/mouseenter */ doRemove = function(){ var ret; if (win.removeEventListener) { ret = function (el, eventName, fn, capture) { if (eventName == 'mouseenter') { 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; }();
除了注册和删除事件,Ext还对其他原生的事件方法(属性)进行了封装,看下面
getTarget : function(ev) { ev = ev.browserEvent || ev; return this.resolveTextNode(ev.target || ev.srcElement); },
获取当前事件源对象
getPageX : function(ev) { return getPageCoord(ev, "X"); }, getPageY : function(ev) { return getPageCoord(ev, "Y"); }, getXY : function(ev) { return [this.getPageX(ev), this.getPageY(ev)]; },
这三个函数用来获取鼠标事件源的坐标(水平,垂直)坐标,其中调用了辅助函数getPageCoord和getScroll,该方法中解决了不同浏览器之间的不同,实现了兼容。
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; }
私有的getPageCoord方法用来获取鼠标事件时相对于文档的坐标(水平,垂直)。
Firefox引入了pageX / Y ,IE9/Safari/Chrome/Opera虽然支持但仅在文档(document)内而非页面(page)。
Safari/Chrome/Opera可以使用标准的clientX/Y获取,IE下可通过clientX/Y与scrollLeft/scrollTop计算得到。
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]; } }
私有的getScroll方法返回文档的scrollTop和scrollLeft值,由于浏览器差异,该实现上先从document.documentElement取,为0后再从document.body上取。都没有的话返回[0,0]。
下面看另外三个方法
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; } },
preventDefault 为阻止事件的默认行为。W3C标准使用 preventDefault 方法,IE6/7/8则是设置 returnValue 为false。Safari/Chrome/Opera同时支持IE6/7/8方式。Firefox仅支持标准的preventDefault。IE9两者都支持。
stopPropagation 用来停止事件冒泡,阻止事件进一步向下传播。关于事件传播的三个阶段:捕捉阶段、到达目标对象阶段、起泡阶段的详细描述可参阅《 JavaScript 权威指南》
stopEvent 则同时阻止默认行为和事件冒泡
下面几个方法只把源码贴出来,功能可以看注释。注意的是getListeners和purgeElement在Ext.EventManager中会实现,等讲到哪里再详细的分析
/** * 获取事件对象 */ 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; }, /** * 获取按键码,注意在keypress 事件中使用。 */ 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); }, /** * 如果文档已经加载完成,如果是IE,则将注册在 window 的 onload 事件上的监听器全部清除掉 */ _load : function(e) { loadComplete = true; 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); } }, /** * 在文档卸载之前,把注册在 window 的 unload 事件上的所有监听函数执行一遍,然后从缓存数组中清除掉。 */ _unload : function(e) { var EU = Ext.lib.Event, i, v, ul, id, len, 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){} } } }; Ext.EventManager._unload(); doRemove(win, UNLOAD, EU._unload); }
另外在Ext.lib.Event的开头定义了一些变量
var loadComplete = false, unloadListeners = {},//用来存放el的unload事件 retryCount = 0, onAvailStack = [], _interval, locked = false, win = window, doc = document, // constants POLL_RETRYS = 200, POLL_INTERVAL = 20, TYPE = 0, FN = 1, OBJ = 2, ADJ_SCOPE = 3, SCROLLLEFT = 'scrollLeft', SCROLLTOP = 'scrollTop', UNLOAD = 'unload', MOUSEOVER = 'mouseover', MOUSEOUT = 'mouseout',
将window、document等外部(非函数作用域内)变量存于本地变量中,这样JS压缩程序就能对它们进行名称替换,获得更高的压缩率。
以上代码为ExtJs对浏览器原生事件的简单封装,而对浏览器本身的事件进行转换和调用,是通过类Ext.EventObject和Ext.EventManager实现的,请看后续讲解。
发表评论
-
ExtJs源码分析与学习—Ext.ToolTip应用
2011-12-29 14:18 1840源码待分析,先粘一例子备用 // ... -
ExtJs源码分析与学习—遮罩层
2011-09-06 16:49 4876类Ext.LoadMask ... -
ExtJs源码分析与学习—Ext.UpdateManager
2011-09-06 16:12 2180如果想让html页面中任意元素自动局部刷新,而不是整 ... -
ExtJs源码分析与学习—ExtJs元素Element(六)
2011-06-08 10:41 1794批量操作元素Ext.CompositeElementLite ... -
ExtJs源码分析与学习—ExtJs元素Element(五)
2011-06-07 17:35 1931元素的查询Ext.DomQuery 该 ... -
ExtJs源码分析与学习—ExtJs元素Element(四)
2011-06-07 17:17 3029元素的操作Ext.DomHelper ... -
ExtJs源码分析与学习—ExtJs元素Element(三)
2011-06-07 17:06 1049Ext.Element扩展 通过Ex ... -
ExtJs源码分析与学习—ExtJs元素Element(二)
2011-06-07 16:42 2638元素操作核心类Ext.Elemen ... -
ExtJs源码分析与学习—ExtJs元素Element(一)
2011-06-07 15:47 2127从这一节开始 ... -
ExtJs源码分析与学习—工具类Ext.util.TextMetrics
2011-06-07 15:11 2037Ext提供了一个 ... -
ExtJs源码分析与学习—ExtJs事件机制(六)
2011-06-07 11:25 1481最后把涉及到Ext事件的相关类图贴到这里 ... -
ExtJs源码分析与学习—ExtJs事件机制(五)
2011-06-07 11:15 3056最近一直忙着做产品,所以好久没有写文章了,下面接着 ... -
Ext 组件操作总结——会不断完善
2011-05-10 16:36 26911、Ext.Component 该组件在渲染的时候会默认的 ... -
ExtJs表单几种验证与扩展
2011-05-03 16:26 4897首先说明一下表单验证与其相关的一些设置 Ext ... -
ExtJs源码分析与学习—ExtJs事件机制(四)
2011-03-19 23:37 5305ExtJs组件事件——ExtJs自定义事件的处理 ... -
ExtJs源码分析与学习—ExtJs事件机制(三)
2011-03-15 20:20 3254这篇讲ExtJs对事件的管理和调用 Ext ... -
ExtJs源码分析与学习—ExtJs事件机制(二)
2011-03-15 20:15 2613在ExtJs源码分析与学习—ExtJs事件机制( ... -
ExtJs源码分析与学习—ExtJs核心代码扩展
2011-01-20 19:16 2082前面三篇文章参考作者snandy总结了Ext中核心 ... -
ExtJs源码分析与学习—ExtJs核心代码(三)
2011-01-17 17:29 2176Ext为javascript的一些对象进行了扩展,主要有Str ... -
ExtJs源码分析与学习—ExtJs核心代码(二)
2011-01-17 16:23 1855接上篇 下面介绍Ext.js ...
相关推荐
在《EXTJS源码分析与开发实例宝典》这本书中,读者可以深入理解EXTJS的内部机制,学习如何利用EXTJS进行高效开发。 标题"EXTJS源码分析与开发实例宝典-开发的效果图.rar"暗示了书中的内容可能包含了EXTJS的源码解析...
《Extjs源码分析与开发实例宝典》全面介绍了ExtJS的技术细节和开发实践,是学习和掌握ExtJS不可或缺的资源。通过对本书的深入阅读,开发者不仅可以理解ExtJS的工作原理,还能学会如何运用ExtJS构建高效、美观的Web...
6. **图表组件**:EXTJS 2.0内置了各种图表类型,如柱状图、饼图、线图等,适用于数据可视化需求,源码分析有助于定制高级图表功能。 7. **Ajax交互**:EXTJS 2.0通过Ajax技术实现与服务器的异步通信,提供了强大的...
**Django整合ExtJS源码解析** 在Web开发领域,Django作为一个强大的Python Web框架,以其高效、安全和可扩展性而备受青睐。与此同时,ExtJS是一个JavaScript库,用于构建富客户端应用程序,提供了丰富的组件和数据...
- **调试工具**:利用浏览器的开发者工具,可以追踪ExtJS源码中的错误和性能瓶颈。 - **主题定制**:ExtJS允许自定义皮肤,通过修改CSS文件,可以调整应用的视觉样式。 6. **与其他技术集成** - **PHP, ASP.NET,...
最后,结合源码分析,提升你的编程水平,能够更好地应对复杂的应用需求。 总之,ExtJs是一个强大的JavaScript框架,通过学习提供的实例和中文教程,结合具体的代码实践,你将能够开发出功能丰富、用户体验优秀的Web...
核心技术部分深入讲解Ext JS的核心基础知识,包括JS原生对象的扩展、事件机制、 模板模型、数据模型,包括一个机制、两个模型及六个扩展。基于元素的开发部分讲解了在DOM元素基 础上扩展的Ext JS元素,包括元素操作...
源码分析: 在`ext-4.0.0`目录中,你将找到ExtJS 4.0的核心源代码。这些文件主要分为以下几个部分: 1. `src`目录:这是ExtJS的核心源码存放处,包含了所有组件、类和工具函数。每个主要的组件或功能都有自己的子...
康海涛的"掏钱学ExtJs完全版"可能是一份全面的学习资料,涵盖了ExtJs的基础到高级应用,包括源码分析,旨在帮助学习者深入理解和掌握这一技术。 首先,ExtJs的核心是其组件模型。它提供了大量的预定义组件,如表格...
通过下载并研究"ext-3.1.0"这个压缩包中的源码和示例,开发者不仅可以学习到ExtJS的基本用法,还能深入理解其内部机制,这对于提升JavaScript开发技能和扩展自定义功能非常有帮助。不过需要注意,随着技术的发展,...
这个资料包“ExtJs源码以及文档相关资料”显然包含了ExtJS 4.1.1版本的源代码和相关的文档,这对于深入理解ExtJS的工作原理和学习如何使用它是极为宝贵的资源。 首先,ExtJS 4.1.1是该框架的一个稳定版本,发布于...
EXTJS的源码学习可以帮助开发者深入理解其内部机制,如数据管理、事件系统、布局管理等。通过分析源码,开发者可以学习到如何自定义组件,优化性能,甚至开发自己的EXTJS插件。此外,熟悉EXTJS的源码也有助于解决在...
在"ExtJs框架系列之filetree 源码"中,我们关注的是FileTree组件,它是一个可交互的文件系统树形视图,允许用户浏览、操作目录和文件。这个组件在Web应用中尤其常见,用于模拟桌面操作系统中的文件管理器。 File...
作者采用了"core→element→component"的主线,将整个ExtJS源码结构串联起来,确保读者能逐步深入学习,理解每一层的细节。 "Introduction"章节为初学者提供了入门指导,涵盖了ExtJS的基本概念和安装步骤。"Core...
本系统采用EXTJS进行前端开发,EXTJS是一款强大的JavaScript组件库,以其丰富的UI组件和数据绑定机制,为开发高质量的Web应用提供了便利。 EXTJS的核心特性包括: 1. **组件化设计**:EXTJS以组件为中心,提供了...
该随书源码包含三个不同版本的ExtJS源码:ext-3.0.0、ext-3.1.1和ext-3.2.0。这涵盖了ExtJS 3.x的主要迭代,每个版本都可能包含新的特性和改进。通过学习这些源码,读者可以了解到ExtJS的历史演变,以及在不同版本间...
"深入浅出ExtJS随书源码--EXTJS2.0"是针对ExtJS 2.0版本的学习资源,通常与一本相关书籍配套,帮助读者通过实际代码加深对ExtJS的理解。 源码分析: 1. **组件系统**:ExtJS的核心是其组件模型,其中包括各种可...
**项目实战源码分析** 这个项目源码中,你将看到以下几个部分: 1. **前端部分**:主要使用ExtJs编写,包括各种页面布局、组件、交互逻辑。你可以在`js`目录下找到相关的JavaScript文件,这些文件定义了ExtJs组件和...
在"ExtJS5学习之Grid与Grid之间的数据拖拽"这个主题中,我们将深入探讨如何实现这种交互功能,以及背后的机制和重要知识点。 首先,我们要了解ExtJS的Grid组件。Grid是一种可配置的表格视图,它可以显示大量的结构...
本文将深入探讨使用ASP.NET与EXTJS技术构建的点卡在线销售系统的开发知识,帮助读者理解并掌握这一系统的实现原理。 首先,ASP.NET是微软公司推出的Web应用程序框架,基于.NET Framework,提供了一种高效、安全且...