本节书摘来自异步社区《Ext JS实战》一书中的第2章,第2.2节,作者:【美】Jesus Garcia著,更多章节内容可以访问云栖社区“异步社区”公众号查看
2.2 Ext.Element类
所有使用了JavaScript的Web应用程序都会围绕着一个核心,也就是HTML的Element。JavaScript对DOM节点的访问能力让我们能够随意、灵活地操作DOM,包括增加、删除、美化或者修改文档中的任意节点内容。通过ID引用一个DOM节点的传统方法是:
这个getElementById方法很好用,可以执行一些类似改变innerHTML的内容,或者美化和配置一个CSS类这样的基本任务。不过要是想对该节点做更多的事情,例如管理它的事件,在有鼠标点击时应用某个样式,或者替换一个CSS类?必须自己管理全部代码,还要不断地对代码进行更新,以保证能够全部浏览器的兼容性。老实说,我想不起还有什么事情会比这个更费劲了。幸运的是,Ext都替我们完成了这些任务。
2.2.1 框架的核心
先看一下Ext.Element这个类,Ext JS社区公认这个类是Ext JS的核心,框架中的每个UI部件中都有它的身影,通过getEl()方法或者el属性都可以得到它。
Ext.Element类是一个完整的DOM元素管理包,包含了许多宝贵的工具,正是因为它的存在,才使得框架能够对DOM施展魔法,并提供健壮的UI供我们使用。这个工具集及其全部的功能对于最终的开发人员都是可用的。
按照Ext JS的设计理念,这个类不仅仅是对DOM元素的简单管理,还能处理各种复杂的任务,例如能够很容易地管理大小、对齐以及坐标。也可以很容易地利用Ajax更新一个元素,管理子节点、动画,使用完整的事件管理以及更多的内容。
2.2.2 与Ext.Element的第一次亲密接触
Ext.Element是很容易上手的,而且可以简化一些最困难的任务,为了练习Ext.Element,需要配置一个基本页面。按照第1章介绍的方法,配置一个包含了Ext JavaScript和CSS的页面。接下来,要包含下面的CSS和HTML:
这些只是给我们的示例搭建一个舞台,确保div标签有明确的大小和边框,这样在页面上能够清晰地看到效果。这里用了一个id是'div1'的div,它就是要操作的目标。如果页面设置正确无误,应该可以清楚地看到这个样式化的,如图2-2所示。这幅图片展示的是一个普通的HTML框,下面就用它来练习基本的Ext.Element方法。
注意:
所有的关于Ext.Element的示例代码都会引用刚刚配置的基本页面。如果想真切地看到DOM发生的改变,建议用Firefox内置的多行的Firebug文本编辑器。相反,可以把这些示例放在一般的脚本块中。只是记住要使用Ext.onReady()。
按照这个CSS定义,属于muDiv类的div都会是35个像素高和200个像素宽,看起来有点怪异。我们要做的就是,通过把高度改成200个像素,让这个div变成一个完美的正方形。
上面这两行代码的执行非常重要。第一行使用的是Ext.get,传给这个方法一个字符串'div',返回的结果是一个Ext.Element的实例,返回的实例通过变量myDiv1进行引用。Ext.get使用的也是document.getElementById,只不过是按照Ext的元素管理方法对它进行了包装而已。
得到这个Ext.Element实例myDiv1之后,通过调用它的setHeight方法,并传入一个整数值200,就把这个方框的高度增加到了200个像素。类似地,也可以用setWidth方法改变元素的宽度,不过下面会跳到一些更有趣的内容。
“一个完美的正方形。不错!”好了,再把大小改变一下,这次使用的是setSize。把width和height都设成350个像素。还是利用已经创建好的引用,myDiv1:
执行这行代码会发生什么呢?是不是动起来了,有一种生动的效果?更好了!
实际上,setSize方法是setHeight和setWidth方法的组合。对这个方法,传递的是宽度,高度以及一个带有两个属性的对象,这两个属性是duration和easing。如果定义了第3个属性,会让setSize以动画的效果展现元素大小的改变。如果不在乎动画效果,那就可以忽略第3个参数,这个框的大小立刻会发生改变,就像之前改变高度那样。
设置大小还只是通过Element类管理元素的众多功能之一。Ext.Element更强大的能力体现在轻松地处理元素的CRUD操作(创建、读取、更新和删除)。
2.2.3 创建子节点
JavaScript的好处之一就是操纵DOM的能力,这其中就包括DOM节点的创建。JavaScript中的有很多原生方法也具有这个能力。Ext JS用Ext.Element类很方便地把这些方法包装起来。下面看看如何创建子节点。
要想创建一个子节点,要用的是Element的createChild方法:
这段代码给目标div的innerHTML添加了一个字符串节点。如果想创建一个元素该怎么做呢?很简单:
createChild会给div1的innerHTML追加一个内容是字符串'Element from a string'的子Div。我不喜欢用这种方法追加子元素,因为这种用字符串代表元素的形式太混乱了。Ext通过接收一个配置对象而不是一个字符串帮我们解决了这个问题:
这里是通过配置对象来创建子元素的。将tag属性设成'div',给html属性指定了一个字符串。单从技术的角度来看,这个方法和之前的createChild实现是一样的,不过看起来更清晰,更能说明我们的意图。如果想注入一个嵌套的标签又该怎么做呢?还是通过配置对象的方法,可以很容易地实现:
在这段代码中,创建了最后一个子元素,它有一个id、一些样式以及一个子元素,这个子元素是一个带有更多样式的div。图2-3显示了div所发生的变化。
在图2-3中,可以看到添加到myDiv1的全部内容,以及Firebug所展示的一副生动的DOM视图,这里增加了一个字符串节点和3个子div,其中一个还有它自己的子div。
如果想在列表的顶端插入一个子元素,还可以使用好用的insertFirst方法,例如:
Element.insertFirst总是在位置0插入一个新的元素,即使DOM结构中还没有一个子元素也一样。
如果想在一个特定的位置插入一个子节点,createChild方法也可以完成这个任务。我们所要做的就是把新创建节点的位置传进去,例如:
在这段代码中,我们给createChild传入了两个参数。第一个参数是个配置对象,代表着新创建的DOM元素,第二个参数是目标节点的引用,createChild用它作为新建节点的容身之所。记住你给这个新创建元素指定的id;我们很快就会用到它。
注意,这里用到了myDiv1.dom.childNodes。Ext.Element的dom属性让我们具有了利用通用浏览器元素管理优雅性的机会。
注意:
Element.Dom属性和document.getElementById()返回的是同样的DOM元素引用。
图2-4显示了在页面上看到的插入节点的样子,以及在Firebug的DOM探测工具中看到的DOM层次结构。从图2-4中可以看到,节点插入的结果和我们设想的一样。用insertFirst在列表的顶端插入一个新节点,用createChild在子节点3的上面插入一个节点。记住,对子节点的计数总是从数字0开始的,而不是从1开始的。
作为一名Web开发人员,添加元素对我们来说是家常便饭。毕竟,这是DHTML的一部分。不过删除也同样重要。下面再看看如何用Ext.Element删除一些子元素。
2.2.4 删除子节点
节点的删除看起来要比添加简单一些。我们所需要做的就是通过Ext找到该节点,然后调用节点自己的remove方法就行了。为了练习子节点的删除操作,以一个干净的画板开始。请新建一个页面,然后输入下面的HTML代码:
检查这段HTML代码,看到了一个id是'div1'的父div。它有5个直系的后代,第1个的id是'child1'。第2个和第3个没有id,不过其CSS类分别是'child2'和'child3'。第4个子元素的id是'child4',并且其CSS类是'sameClass'。类似地,它也有一个id是'nestedChild1'的直系后代,并且用的是和父亲相同的CSS类。Div1的最后一个孩子没有id,也没有CSS类。之所以准备这些素材,是因为要先从CSS选择器1开始,然后再到直接利用元素的id。
在添加子节点的例子中,一直是把父div(id='div1')包装成Ext.Element后加以引用,然后用它的create方法创建节点。要想删除一个节点,就要用不同的方法了,因为需要明确地定位到要删除的节点。对于这个新的DOM结构,我们会用几种方法来实现。
要尝试的第一个方法是通过一个已经包装好的DOM元素删除一个子节点。先要创建一个包装div1的Ext.Element实例,然后再用CSS选择器找到它的第一个子节点。
这个例子中,通过Ext.get得到了对div1的引用。然后又调用了Element.down方法,传给这个方法的是一个伪类选择器,这会让Ext沿着这棵DOM树向下查找,一直到找到第一个孩子,它是一个div,然后Ext把它包装成一个Ext.Element的实例,最终得到的就是对第一个子元素的引用,即firstChild的引用。
Element.down方法查找的是给出的Ext.Element的一级DOM节点。找到的结果恰好是一个id是'child1'的div。然后调用firstChild.remove,这个节点就从DOM中删除了。
下面是通过选择器删除列表中的最后一个节点:
这个例子和前一个类似,最大的区别在于用的是选择器'div:last-child',这个选择器找到div1的最后一个childNode,然后再用一个Ext.Element的实例把它封装起来。最后,再调用lastChild.remove,这个节点就没有了。
如果想根据id找到目标元素该怎么办呢?可以让Ext.get完成这个工作。这次,没有必要创建引用,用链(Chaining)完成这个工作就可以了:
执行这行代码会把id是'child4'的子节点删掉,包括该节点的子节点。记住,如果一个节点有子节点,那么删除这个节点的同时,它的所有子节点也都会被删除。
关于Ext.Element要了解的最后一项内容就是执行Ajax请求,从服务器远程加载HTML片段并把它们注入到DOM中。
2.2.5 Ext.Element与Ajax一起使用
Ext.Element类具有执行Ajax的能力,可以取得远程的HTML片段,并把这些片段注入到它自己的innerHTML中。做练习之前,先写一个将要被加载的HTML片段:
这个HTML片段中,只放了一个简单的div,还嵌入了一个script脚本,里面调用的是Ext.getBody,然后又通过链调用它的highlight方法。要想得到对document.body的引用,Ext.getBody非常好用,现在把这个文件保存成htmlFragment.html。
接着,要加载以下内容。
这段代码中,调用的是Ext.getBody所返回的对象的load方法,并给这个方法传入一个配置对象,其中url指定的要加载的目标htmlFragment.html,scripts被设为true。这段代码执行起来会怎么样呢?可以参见图2-5。
这段代码执行后,可以看到文档体通过Ajax请求获取了htmlFragment.html文件。在获取这个文件的过程中,会一直显示一个代表正在加载的指示符,一旦请求完成,这段HTML片段就被插入到DOM中了。接着会看到整个内容区都用黄色高亮显示,这就意味着JavaScript代码得到了执行。现在,应该知道Ext.Element.load方法要比手工调用Ext.Ajax.request方便多了。
看到了吧,用Ext.Element给DOM添加内容或者删除内容就是小菜一碟。Ext还有更简单的办法添加元素,尤其是当要增加的是那种重复的DOM结构。这就要用到Template和XTemplate两个辅助工具类。