读Ext之十三(Ext元素)
终于越来越接近Ext的核心了。这篇开始Ext.Element,这里说的Ext元素指的是Ext.Element类的实例。
任何前端库都会涉及到对HTMLElement的操作,JQuery更是以其为中心,一个$()函数调用后将DOM元素以索引方式存在 JQuery对象 中。
Ext则使用了一个称为 Ext.Element 的类,如 Ext.get/Ext.fly 返回的都是该类的实例对象。许多操作如样式,添加事件等都封装到该类里。
Ext.Element的定义大概框架如下
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 | Ext.Element = function (element, forceNew){ var dom = typeof element == "string" ? DOC.getElementById(element) : element, id; if (!dom) return null ; id = dom.id; if (!forceNew && id && Ext.elCache[id]){ // element object already exists return Ext.elCache[id].el; } this .dom = dom; this .id = id || Ext.id(dom); }; var D = Ext.lib.Dom, DH = Ext.DomHelper, E = Ext.lib.Event, A = Ext.lib.Anim, El = Ext.Element, EC = Ext.elCache; El.prototype = { ... } |
可以看到,采用了的是很传统的JS写类方式:构造器+原型 方式。构造器内执行大概如下
1,如果参数element是字符串则使用document.getElementById获取,否则直接返回element(通常是HTMLElement)。
2,给 变量 id赋值,来自dom.id
3,forceNew不传或传false情况下,优先从缓存中取。
4,给this挂上dom和id属性。
3处有点绕人,
return Ext.elCache[id].el;
我们知道这是在定义一个类,使用new操作符。如
var ele = new Ext.Element('head');
既然是new,里面怎么又return了呢?一般是函数调用(),里面有return。构造器里一般都是定义一些字段。其实当构造器中return的是对象类型时是允许的,如果return的是基本类型,又使用new调用函数则会出现非预期的效果。对于这点感兴趣的详见:具名函数的四种调用方式 系列 。
构造器内其实很少代码,比较简单。随后是定义了一些变量如D,DH,这些多数是之前篇幅中提到的。Ext采用这种简单的设计模式:组合。Bruce Eckel在《Think in Java》中所言,避免滥用继承,多数情况下组合已足以。
接着看挂在prototype上的第一个方法 set
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 | set : function (o, useSet){ var el = this .dom, attr, val, useSet = (useSet !== false ) && !!el.setAttribute; for (attr in o){ if (o.hasOwnProperty(attr)) { val = o[attr]; if (attr == 'style' ) { DH.applyStyles(el, val); } else if (attr == 'cls' ) { el.className = val; } else if (useSet) { el.setAttribute(attr, val); } else { el[attr] = val; } } } return this ; }, |
该方法用来设置HTMLElement(如div)的属性如id、style、class等。第一个参数是个对象(Hash)如{id:'uname'},第二个参数布尔类型,顾名思义useSet为true即使用setAttribute来设置属性。
至于什么使用setAttribute,什么时候使用点操作符(.)给元素设置属性。这里 给出了详尽的解释。
该方法内部使用for in来遍历对象,我们知道默认情况下 for in 不会去遍历原形链的属性 的。
对象的hasOwnProperty方法对继承于原形链的属性是 检测不到 的,即返回false。因此这里hasOwnProperty(attr) 这个判断完全是多余的。
接着看,又是多个if分支
style,调用DH.applyStyles。但DH.applyStyles方法有bug 。
cls,设置类名(class)
useSet为true,使用setAttribute设置
最后使用中括号操作符(等价于点操作符)
看下一个方法 is
1 2 3 | is : function (simpleSelector){ return Ext.DomQuery.is( this .dom, simpleSelector); }, |
该方法用来检测dom元素是否具有给定的css选择器。
如对于<div id="d1" class="container"></div>,Ext.get('d1').is('.container')返回的是true。注意字符串container前得有个点。因为是css类选择器。
该方法内部调用Ext.DomQuery.is,暂不提。
接下来是 focus 方法,
1 2 3 4 5 6 7 8 9 10 11 12 | focus : function (defer, /* private */ dom) { var me = this , dom = dom || me.dom; try { if (Number(defer)){ me.focus.defer(defer, null , [ null , dom]); } else { dom.focus(); } } catch (e){} return me; }, |
focus方法用来获取焦点,defer为数字,即延迟函数调用的时间,单位是毫秒。
参数dom对于使用Ext库的客户端程序员来说是用不到的,仅提供给库内部使用,即递归调用。
1 | me.focus.defer(defer, null , [ null , dom]); |
这句较难理解,即递归调用。me是this,this是Ext.Element对象自身。在focus内部又调用自己。
me.focus.defer 中的 defer哪来的? 还记得第三篇 原型扩展 中提到的为Function.prototype扩展的defer方法吗?没错,就是它。
如果没有传defer,那么直接调用dom.focus。这里使用了try catch,是因为某些情况下会出异常。
接下来是 blur 方法,很简单就不说了。往下看 getValue 方法
1 2 3 4 | getValue : function (asNumber){ var val = this .dom.value; return asNumber ? parseInt(val, 10) : val; }, |
获取元素的value,一般是form内元素如input,select,textarea等。这里很巧妙,如果asNumber为true则偷偷的将其 转换为数字类型 了。
接下来是 addListener / removeListener / removeAllListeners / purgeAllListeners ,它们内部使用的是Ext.EventManager。第九篇 提到了,不说了。
往下是 addUnits 方法,该方法是内部用的。暂不提。
接下来是 load 方法
1 2 3 4 5 6 7 8 9 10 | load : function (url, params, cb){ Ext.Ajax.request(Ext.apply({ params: params, url: url.url || url, callback: cb, el: this .dom, indicatorText: url.indicatorText || '' }, Ext.isObject(url) ? url : {})); return this ; }, |
该方法把请求返回的html片段作为该元素的innerHTML填充。内部调用的是Ext.Ajax.request。
接着是isBorderBox 方法
1 2 3 | isBorderBox : function (){ return noBoxAdjust[( this .dom.tagName || "" ).toLowerCase()] || Ext.isBorderBox; }, |
标准模式中对于具有border的元素如select会返回1,怪异模式中对于input,select,textarea元素返回1。如果不是这些元素会直接返回Ext.isBorderBox。
1 | isBorderBox = isIE && !isStrict, |
接下来是 remove 方法,
1 2 3 4 5 6 7 8 9 | remove : function (){ var me = this , dom = me.dom; if (dom) { delete me.dom; Ext.removeNode(dom); } }, |
该方法会删除该dom元素,包括dom文档中的,ext缓存中的,及dom元素上的事件handler。
往下是 hover 方法,
1 2 3 4 5 6 | hover : function (overFn, outFn, scope, options){ var me = this ; me.on( 'mouseenter' , overFn, scope || me.dom, options); me.on( 'mouseleave' , outFn, scope || me.dom, options); return me; }, |
意义及用法就不提了,内部使用me.on,on是addListener的简写。mouseenter和mouseleave 的实现在 第四篇 提到 了。
接着是 contains 方法,
1 2 3 | contains : function (el){ return !el ? false : Ext.lib.Dom.isAncestor( this .dom, el.dom ? el.dom : el); }, |
判断是否包含el元素,内部实现也在第四篇中提到。
再看 getAttribute 方法,
1 2 3 4 5 6 7 8 9 10 11 12 | getAttribute : Ext.isIE ? function (name, ns){ var d = this .dom, type = typeof d[ns + ":" + name]; if ([ 'undefined' , 'unknown' ].indexOf(type) == -1){ return d[ns + ":" + name]; } return d[name]; } : function (name, ns){ var d = this .dom; return d.getAttributeNS(ns, name) || d.getAttribute(ns + ":" + name) || d.getAttribute(name) || d[name]; }, |
该方法用来获取元素的属性值。可以看到对于IE和非IE有两个版本,IE中对undefined,unknow类型做了处理。其它浏览器则是标准的实现。
最后一个是 update 方法,
1 2 3 4 5 6 | update : function (html) { if ( this .dom) { this .dom.innerHTML = html; } return this ; } |
该方法使用innerHTML来更新元素内容。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· go语言实现终端里的倒计时
· 如何编写易于单元测试的代码
· 10年+ .NET Coder 心语,封装的思维:从隐藏、稳定开始理解其本质意义
· .NET Core 中如何实现缓存的预热?
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
· 周边上新:园子的第一款马克杯温暖上架
· 分享 3 个 .NET 开源的文件压缩处理库,助力快速实现文件压缩解压功能!
· Ollama——大语言模型本地部署的极速利器
· DeepSeek如何颠覆传统软件测试?测试工程师会被淘汰吗?
· 使用C#创建一个MCP客户端