读Ext之十五(操作批量元素)
前两篇读了Ext.Element,这篇介绍的Ext.CompositeElementLite类是对集合(Ext.Element)的操作。
Ext.select / Ext.element.select 依赖于Ext.CompositeElementLite。
类的大概结构如下(省略了挂在prototype上的很多方法),
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 | Ext.CompositeElementLite = function (els, root){ this .elements = []; this .add(els, root); this .el = new Ext.Element.Flyweight(); }; Ext.CompositeElementLite.prototype = { isComposite: true , getElement : function (el){ // Set the shared flyweight dom property to the current element var e = this .el; e.dom = el; e.id = el.id; return e; }, ... }; |
构造器内定义了两个个字段
this.elements 存放的是HTMLElement
this.el 是Ext.Element.Flyweight 对象
new 该类时
var cel = new Ext.Ext.CompositeElementLite(els,root);
构造器调用了this.add,该方法如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 | add : function (els, root){ var me = this , elements = me.elements; if (!els){ return this ; } if (Ext.isString(els)){ els = Ext.Element.selectorFunction(els, root); } else if (els.isComposite){ els = els.elements; } else if (!Ext.isIterable(els)){ els = [els]; } for ( var i = 0, len = els.length; i < len; ++i){ elements.push(me.transformElement(els[i])); } return me; }, |
els有三种分支:
分支1,els为字符串(css选择器如.cls)
调用Ext.Element.selectorFunction,返回数组。该函数是Ext.DomQuery.select的别名。Ext.DomQuery.select暂不提,只需了解调用后会返回一个数组,数组中存放的是HTMLElement。
分支2,els.isComposite为true
即els本身就是Ext.CompositeElementLite的实例对象,那么直接取该对象的elements赋值给els。
分支3,els不可迭代,即不是数组,也不是对象。
如传一个div元素,那么将其包装成数组。
for循环将数组中的元素一一添加到elements中。for中使用了transformElement方法,
1 2 3 4 | // private transformElement : function (el){ return Ext.getDom(el); }, |
这个方法用来将el转成HTMLElement,下面依次看原型上的其它属性,方法
1 | isComposite: true , |
isComposite 用来表示对象是Ext.CompositeElementLite类的对象。仅在类内部使用。在上面提到的add方法中用到了。
接着是 getElement 方法,也仅在该类内部使用
1 2 3 4 5 6 7 | getElement : function (el){ // Set the shared flyweight dom property to the current element var e = this .el; e.dom = el; e.id = el.id; return e; }, |
用来获取构造器中定义的字段el(Ext.Element.Flyweight),Ext.Element.Flyweight基本上可以当成是Ext.Element。
这个方法设计很巧妙,它并没有每次都new一个Ext.Element.Flyweight,然后赋值给this.el。而是继续使用已有的this.el,只是更新了this.el.dom和this.el.id。
往下是 getCount方法,
1 2 3 | getCount : function (){ return this .elements.length; }, |
该方法返回集合this.elements的长度。
接下来是 invoke 方法,
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | invoke : function (fn, args){ var me = this , els = me.elements, len = els.length, e, i; for (i = 0; i < len; i++) { e = els[i]; if (e){ Ext.Element.prototype[fn].apply(me.getElement(e), args); } } return me; }, |
简单讲就是将Ext.Element.prototype的函数fn在指定的上下文(me.getElement(e))上执行。
嗯,还是没明白。实际上这个函数不是提供给客户端程序员的,它是用来扩展Ext.CompositeElementLite类的。
即将Ext.Element原型上的所有方法都复制给Ext.CompositeElementLite的原型。以下代码就是干这个事的
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 | ( function (){ var fnName, ElProto = Ext.Element.prototype, CelProto = Ext.CompositeElementLite.prototype; for (fnName in ElProto){ if (Ext.isFunction(ElProto[fnName])){ ( function (fnName){ CelProto[fnName] = CelProto[fnName] || function (){ return this .invoke(fnName, arguments); }; }).call(CelProto, fnName); } } })(); |
这段代码对于初学JS的程序员理解起来较困难。慢慢看吧..
接下来是 item 方法,
1 2 3 4 5 6 7 8 9 10 | item : function (index){ var me = this , el = me.elements[index], out = null ; if (el){ out = me.getElement(el); } return out; }, |
它返回集合中指定的元素,如cel.item(1)返回集合中的第二个元素。
往下是 addListener 方法,
1 2 3 4 5 6 7 8 9 10 11 12 13 | addListener : function (eventName, handler, scope, opt){ var els = this .elements, len = els.length, i, e; for (i = 0; i<len; i++) { e = els[i]; if (e) { Ext.EventManager.on(e, eventName, handler, scope || e, opt); } } return this ; }, |
用来给集合中的所有元素添加事件,内部使用前面提到的 Ext.EventManager.on方法。
接下来是 each 方法,
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 | each : function (fn, scope){ var me = this , els = me.elements, len = els.length, i, e; for (i = 0; i<len; i++) { e = els[i]; if (e){ e = this .getElement(e); if (fn.call(scope || e, e, me, i)){ break ; } } } return me; }, |
用来迭代集合中的元素,使用fn来调用,scope指定fn的上下文。与JQuery的 $().each 类似。
接着是 fill 方法,
1 2 3 4 5 6 | fill : function (els){ var me = this ; me.elements = []; me.add(els); return me; }, |
该方法会用新的元素集合填充elements。会清空之前的elements。
接下来是 filter 方法,
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 | filter : function (selector){ var els = [], me = this , elements = me.elements, fn = Ext.isFunction(selector) ? selector : function (el){ return el.is(selector); }; me.each( function (el, self, i){ if (fn(el, i) !== false ){ els[els.length] = me.transformElement(el); } }); me.elements = els; return me; }, |
该方法用来过滤集合元素,条件selector可以是函数,也可以是css选择器。把符合条件的存放到字段elements中。
内部使用了is方法,该方法在上一篇已经提到。
再往下是 indexof 方法,
1 2 3 | indexOf : function (el){ return this .elements.indexOf( this .transformElement(el)); }, |
很熟悉吧,和String和Array的indexOf类似,如果存在返回其索引,否则返回-1。内部实际使用的就是数组的indexOf方法。
indexOf是ECMAScript 5中数组新加的。IE6等老浏览器不支持,Ext给Array的原型添加了该方法,见 读Ext之三(原型扩展) 。
接着看 replaceElement 方法,
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | replaceElement : function (el, replacement, domReplace){ var index = !isNaN(el) ? el : this .indexOf(el), d; if (index > -1){ replacement = Ext.getDom(replacement); if (domReplace){ d = this .elements[index]; d.parentNode.insertBefore(replacement, d); Ext.removeNode(d); } this .elements.splice(index, 1, replacement); } return this ; }, |
该方法会把集合中指定的元素(el)用新的元素(replacement)替换。domReplace为true则将文档中的也替换,否则只替换集合elements中的。
最后一个方法是 clear,
1 2 3 | clear : function (){ this .elements = []; } |
顾名思义,用来 清空数组 。与 Prototype库 不同的Ext是重新给数组this.elements赋值为空数组。
Prototype库则是将数组的length赋值为0以实现清空。注意JS中数组的length是可写的。其它语言如Java则是只读的。
在篇头我提到了Ext.select / Ext.Element.select,且说过该方法的实现依赖于Ext.CompositeElementLite。下面揭开面纱看看该方法源码,
1 2 3 4 5 6 7 8 9 10 11 12 | Ext.Element.select = function (selector, root){ var els; if ( typeof selector == "string" ){ els = Ext.Element.selectorFunction(selector, root); } else if (selector.length !== undefined){ els = selector; } else { throw "Invalid selector" ; } return new Ext.CompositeElementLite(els); }; Ext.select = Ext.Element.select; |
可以看到Ext.select是Ext.Element.select的别名,它更简短。Ext.select 在日常开发中经常使用到。
它相当于JQuery的选择器,即往往是先定位到某(或某些)元素(或元素集合)后再进行一系列操作(方法调用)。不知注意到没有,Ext.CompositeElementLite的方法中都会返回自身(this)。
因此可以实现 链式调用 的语法效果。如
1 2 3 | Ext.select( 'p' ) .addClass( '.cls' ) .on( 'click' , function (){alert( this )}); |
选择页面中的段落p元素,为其添加类cls,再为其添加点击事件。只要愿意你可以一直点操作下去。
好了,介绍了怎么使用。看看它是如何实现的,
参数selector可以是css选择器(字符串),可以是HTMLELement集合。将其作为参数传给new Ext.CompositeElementLite。返回的是Ext.CompositeElementLite的实例对象。
很简单,其实内部仅仅是new了个Ext.CompositeElementLite,了解了该类。其实就了解了Ext.select。
【推荐】国内首个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客户端
2011-05-18 仅IE不支持setTimeout/setInterval函数的第三个以上参数