EXT学习资料

无论你是Ext库的新手,抑或是想了解Ext的人,本篇文章的内容都适合你。本文将简单地介绍Ext的几个基本概念,和如何快速地做出一个动态的页面并运行起来,假设读者已具备了一些JavaScript经验和初级了解HTML Dom。 
目錄 
? 1 下载Ext 
? 2 开始! 
? 3 Element:Ext的核心 
? 4 获取多个DOM的节点 
? 5 响应事件 
? 6 使用Widgets 
o 6.1 MessageBox 
o 6.2 Grid 
o 6.3 还有更多的.. 
? 7 使用Ajax 
o 7.1 PHP 
o 7.2 ASP.Net 
o 7.3 Cold Fusion 

下载Ext 
如果你未曾下载过,那应从这里下载最新版本的Ext http://extjs.com/downloads 。 
针对你的下载需求,有几个不同的弹性选项。通常地,最稳定的版本,是较多人的选择。下载解包后,那个example文件夹便是一个探索Ext的好地方!
开始! 
Download Example File 
? IntroToExt.zip 

我们将使用Ext,来完成一些JavaScript任务。 
Zip文件包括三个文件:ExtStart.html, ExtStart.js和ExtStart.css。解包这三个文件到Ext的安装目录中(例如,Ext是在“C:/code/Ext/v1.0”中,那应该在"v1.0"里面新建目录“tutorial”。双击ExtStart.htm,接着你的浏览器打开启动页面,应该会有一条消息告诉你配置已完毕。如果是一个Javascript错误,请按照页面上的指引操作。 
在你常用的IDE中或文本编辑器中,打开ExtStart.js看看: 
Ext.onReady可能是你接触的第一个方法。这个方法是指当前DOM加载完毕后,保证页面内的所有元素能被Script引用(reference)。你可删除alert()那行,加入一些实际用途的代码试试: 
Ext.onReady(function() { 
alert("Congratulations! You have Ext configured correctly!"); 
}); 
Element:Ext的核心 
大多数的JavaScript操作都需要先获取页面上的某个元素(reference),好让你来做些实质性的事情。传统的JavaScript方法,是通过ID获取Dom节点的: 
var myDiv = document.getElementById('myDiv'); 
这毫无问题,不过这样单单返回一个对象(DOM节点),用起来并不是太实用和方便。为了要用那节点干点事情,你还将要手工编写不少的代码;另外,对于不同类型浏览器之间的差异,要你处理起来可真头大了。 
进入Ext.element 对象。元素(element)的的确确是Ext的心脏地带,--无论是访问元素(elements)还是完成一些其他动作,都要涉及它。Element的 API是整个Ext库的基础,如果你时间不多,只是想了解Ext中的一两个类的话,Element一定是首选! 
由ID获取一个Ext Element如下(首页ExtStart.htm包含一个div,ID名字为“myDiv”,然后,在ExtStart.js中加入下列语句): 
Ext.onReady(function() {var myDiv = Ext.get('myDiv');}); 
再回头看看Element对象,发现什么有趣的东东呢? 
? Element包含了常见的DOM方法和属性,提供一个快捷的、统一的、跨浏览器的接口(若使用Element.dom的话,就可以直接访问底层DOM的节点。); 
? Element.get()方法内置缓存处理(Cache),多次访问同一对象效率上有极大优势; 
? 内置常用的DOM节点的动作,并且是跨浏览器的定位的位置、大小、动画、拖放等等(add/remove CSS classes, add/remove event handlers, positioning, sizing, animation, drag/drop)。 
这意味着你可用少量的代码来做各种各样的事情,这里仅仅是一个简单的例子(完整的列表在ElementAPI中)。 
继续在ExtStart.js中,在刚才我们获取好myDiv的位置中加入: 
myDiv.highlight(); //黄色高亮显示然后渐退 
myDiv.addClass('red'); // 添加自定义CSS类 (在ExtStart.css定义) 
myDiv.center(); //在视图中将元素居中 
myDiv.setOpacity(.25); // 使元素半透明 
获取多个DOM的节点 
通常情况下,想获取多个DOM的节点,难以依靠ID的方式来获取。有可能因为没设置ID,或者你不知道ID,又或者直接用ID方式引用有太多元素了。这种情况下,你就会不用ID来作为获取元素的依据,可能会用属性(attribute)或CSS Classname代替。基于以上的原因,Ext引入了一个功能异常强大的Dom Selector库,叫做DomQuery。 
DomQuery可作为单独的库使用,但常用于Ext,你可以在上下文环境中(Context)获取多个元素,然后通过Element接口调用。令人欣喜的是,Element对象本身便有Element.selcect的方法来实现查询,即内部调用DomQuery选取元素。这个简单的例子中, ExtStart.htm包含若干段落( 
标签),没有一个是有ID的,而你想轻松地通过一次操作马上获取每一段,全体执行它们的动作,可以这样做: 
// 每段高亮显示 
Ext.select('p').highlight(); 
DomQuery的选取参数是一段较长的数组,其中包括W3C CSS3 Dom选取器、基本XPatch、HTML属性和更多,请参阅DomQuery API文档以了解这功能强大的库个中细节。 
响应事件 
到这范例为止,我们所写的代码都是放在onReady中,即当页面加载后总会立即执行,功能较单一——这样的话,你便知道,如何响应某个动作或事件来执行你希望做的事情,做法是,先分配一个function,再定义一个event handler事件处理器来响应。我们由这个简单的范例开始,打开ExtStart.js,编辑下列的代码: 
Ext.onReady(function() { 
Ext.get('myButton').on('click', function(){ 
alert("You clicked the button"); 
}); 
}); 
加载好页面,代码依然会执行,不过区别是,包含alert()的function是已定义好的,但它不会立即地被执行,是分配到按钮的单击事件中。用浅显的文字解释,就是:获取ID为'myDottom'元素的引用,监听任何发生这个元素上被单击的情况,并分配一个function,以准备任何单击元素的情况。 
正路来说,Element.select也能做同样的事情,即作用在获取一组元素上。下一例中,演示了页面中的某一段落被单击后,便有弹出窗口: 
Ext.onReady(function() { 
Ext.select('p').on('click', function() { 
alert("You clicked a paragraph"); 
}); 
}); 
这两个例子中,事件处理的function均是简单几句,没有函数的名称,这种类型函数称为“匿名函数(anonymous function)”,即是没有名的的函数。你也可以分配一个有名字的event handler,这对于代码的重用或多个事件很有用。下一例等效于上一例: 
Ext.onReady(function() { 
var paragraphClicked = function() { 
alert("You clicked a paragraph"); 
Ext.select('p').on('click', paragraphClicked); 
}); 
到目前为止,我们已经知道如何执行某个动作。但当事件触发时,我们如何得知这个event handler执行时是作用在哪一个特定的元素上呢?要明确这一点非常简单,Element.on方法传入到even handler的function中(我们这里先讨论第一个参数,不过你应该浏览API文档以了解even handler更多的细节)。在我们之前的例子中,function是忽略这些参数的,到这里可有少许的改变,——我们在功能上提供了更深层次的控制。必须先说明的是,这实际上是Ext的事件对象(event object),一个跨浏览器和拥有更多控制的事件的对象。例如,可以用下列的语句,得到这个事件响应所在的DOM节点: 
Ext.onReady(function() { 
var paragraphClicked = function(e) { 
Ext.get(e.target).highlight(); 
Ext.select('p').on('click', paragraphClicked); 
}); 
注意得到的e.target是DOM节点,所以我们首先将其转换成为EXT的Elemnet元素,然后执行欲完成的事件,这个例子中,我们看见段落是高亮显示的。 
使用Widgets 
(Widget原意为“小器件”,现指页面中UI控件) 
除了我们已经讨论过的核心JavaScript库,当前的Ext亦包括了一系列的最前端的JavaScirptUI组件库。文本以一个常用的widget为例子,作简单的介绍。 
MessageBox 
比起略为沉闷的“HelloWolrd”消息窗口,我们做少许变化,前面我们写的代码是,单击某个段落便会高亮显示,现在是单击段落,在消息窗口中显示段落内容出来。 
在上面的paragraphClicked的function中,将这行代码: 
Ext.get(e.target).highlight(); 
替换为: 
var paragraph = Ext.get(e.target); 
paragraph.highlight(); 
Ext.MessageBox.show({ 
title: 'Paragraph Clicked', 
msg: paragraph.dom.innerHTML, 
width:400, 
buttons: Ext.MessageBox.OK, 
animEl: paragraph 
}); 
这里有些新的概念需要讨论一下。在第一行中我们创建了一个局部变量(Local Variable)来保存某个元素的引用,即被单击的那个DOM节点(本例中,DOM节点指的是段落paragrah,事因我们已经定义该事件与< p>标签发生关联的了)。为什么要这样做呢?嗯...观察上面的代码,我们需要引用同一元素来高亮显示,在MessageBox中也是引用同一元素作为参数使用。 
一般来说,多次重复使用同一值(Value)或对象,是一个不好的方式,所以,作为一个具备良好OO思维的开发者,应该是将其分配到一个局部变量中,反复使用这变量! 
现在,为了我们接下来阐述新概念的演示,请观察MessageBox的调用。乍一看,这像一连串的参数传入到方法中,但仔细看,这是一个非常特别的语法。实际上,传入到MessageBox.show的只有一个参数:一个Object literal,包含一组属性和属性值。在Javascript中,Object Literal是动态的,你可在任何时候用{和}创建一个典型的对象(object)。其中的字符由一系列的name/value组成的属性,属性的格式是[property name]:[property value]。在整个Ext中,你将会经常遇到这种语法,因此你应该马上消化并吸收这个知识点! 
使用Object Literal的原因是什么呢?主要的原因是“可伸缩性(flexibility)”的考虑",随时可新增、删除属性,亦可不管顺序地插入。而方法不需要改变。这也是多个参数的情况下,为最终开发者带来不少的方便(本例中的MessageBox.show())。例如,我们说这儿的foo.action方法,有四个参数,而只有一个是你必须传入的。本例中,你想像中的代码可能会是这样的foo.action(null, null, null, 'hello').,若果那方法用Object Literal来写,却是这样, foo.action({ param4: 'hello' }),这更易用和易读。 
Grid 
Grid是Ext中人们最想先睹为快的和最为流行Widgets之一。好,让我们看看怎么轻松地创建一个Grid并运行。用下列代码替换ExtStart.js中全部语句: 
Ext.onReady(function() { 
var myData = [ 
['Apple',29.89,0.24,0.81,'9/1 12:00am'], 
['Ext',83.81,0.28,0.34,'9/12 12:00am'], 
['Google',71.72,0.02,0.03,'10/1 12:00am'], 
['Microsoft',52.55,0.01,0.02,'7/4 12:00am'], 
['Yahoo!',29.01,0.42,1.47,'5/22 12:00am'] 
]; 

var ds = new Ext.data.Store({ 
proxy: new Ext.data.MemoryProxy(myData), 
reader: new Ext.data.ArrayReader({id: 0}, [ 
{name: 'company'}, 
{name: 'price', type: 'float'}, 
{name: 'change', type: 'float'}, 
{name: 'pctChange', type: 'float'}, 
{name: 'lastChange', type: 'date', dateFormat: 'n/j h:ia'} 
]) 
}); 
ds.load(); 

var colModel = new Ext.grid.ColumnModel([ 
{header: "Company", width: 120, sortable: true, dataIndex: 'company'}, 
{header: "Price", width: 90, sortable: true, dataIndex: 'price'}, 
{header: "Change", width: 90, sortable: true, dataIndex: 'change'}, 
{header: "% Change", width: 90, sortable: true, dataIndex: 'pctChange'}, 
{header: "Last Updated", width: 120, sortable: true, 
renderer: Ext.util.Format.dateRenderer('m/d/Y'), dataIndex: 'lastChange'} 
]); 

var grid = new Ext.grid.Grid('grid-example', {ds: ds, cm: colModel}); 
grid.render(); 
grid.getSelectionModel().selectFirstRow(); 
}); 
这看上去很复杂,但实际上加起来,只有七行代码。第一行创建数组并作为数据源。实际案例中,你很可能从数据库、或者WebService那里得到动态的数据。接着,我们创建并加载data store, data store将会告诉Ext的底层库接手处理和格式化这些数据。接着,我们定义一个column模型,用来轻松地调配Grid的每一列参数。最后我们生成这个Grid,传入data store和column模型两个对象,进行渲染并选好第一行。不是太困难吧?如果一切顺利,完成之后你会看到像这样的: 

当然,你可能未掌握这段代码的某些细节(像MemoryProxy究竟是什么?)但先不要紧,这个例子的目的是告诉你,用少量的代码,创建一个富界面的多功能的UI组件而已——这是完全可能的,只要读者您有兴趣学习。这儿有许多学习Grid的资源。Ext Grid教程、交叉Gird演示和Grid API文档。 
还有更多的.. 
这只是冰山一角。还有一打的UI Widgets可以供调用,如 layouts, tabs, menus, toolbars, dialogs, tree view等等。请参阅API文档中范例演示。 
使用Ajax 
在弄好一些页面后,你已经懂得在页面和脚本之间的交互原理(interact)。接下来,你应该掌握的是,怎样与远程服务器(remote server)交换数据,常见的是从数据库加载数据(load)或是保存数据(save)到数据库中。通过JavaScript异步无刷新交换数据的这种方式,就是所谓的Ajax。Ext内建卓越的Ajax支持,例如,一个普遍的用户操作就是,异步发送一些东西到服务器,然后,UI元素根据回应(Response)作出更新。这是一个包含text input的表单,一个div用于显示消息(注意,你可以在ExtStart.html中加入下列代码,但这必须要访问服务器): 
<div id="msg" style="visibility: hidden"></div> 

Name: <input type="text" id="name" /><br /> 

<input type="button" id="oKButton" value="OK" /> 
接着,我们加入这些处理交换数据的JavaScript代码到文件ExtStart.js中(用下面的代码覆盖): 
Ext.onReady(function(){ 
Ext.get('oKButton').on('click', function(){ 
var msg = Ext.get('msg'); 
msg.load({ 
url: [server url], //换成你的URL 
params: 'name=' + Ext.get('name').dom.value, 
text: 'Updating...' 

}); 
msg.show(); 
}); 
}); 
这种模式看起来已经比较熟悉了吧!先获取按钮元素,加入单击事件的监听。在事件处理器中(event handler),我们使用一个负责处理Ajax请求、接受响应(Response)和更新另一个元素的Ext内建类,称作UpdateManager。 UpdateManager可以直接使用,或者和我们现在的做法一样,通过Element的load方法来引用(本例中该元素是id为“msg“的 div)。当使用Element.load方法,请求(request)会在加工处理后发送,等待服务器的响应(Response),来自动替换元素的 innerHTML。简单传入服务器url地址,加上字符串参数,便可以处理这个请求(本例中,参数值来自“name”元素的value),而text值是请求发送时提示的文本,完毕后显示那个msg的div(因为开始时默认隐藏)。当然,和大多数Ext组件一样,UpdateManager有许多的参数可选,不同的Ajax请求有不同的方案。而这里仅演示最简单的那种。 
PHP 
<? if(isset($_GET['name'])) { 
echo 'From Server: '.$_GET['name']; 
?> 
ASP.Net 
protected void Page_Load(object sender, EventArgs e) 
if (Request["name"] != null) 
Response.Write("From Server: " + Request["name"]); 
Response.End(); 
Cold Fusion 
<cfif StructKeyExists(url, "name")> 
<cfoutput>From Server: #url.name#</cfoutput> 
</cfif> 
最后一个关于Ajax隐晦的地方就是,服务器实际处理请求和返回(Resposne)是具体过程。这个过程会是一个服务端页面,一个Servlet,一个 Http调度过程,一个WebService,甚至是Perl或CGI脚本,即不指定一个服务器都可以处理的http请求。让人无法预料的是,服务器返回什么是服务器的事情,无法给一个标准的例子来覆盖阐述所有的可能性。(这段代码输出刚才我们传入'name'的那个值到客户端,即发送什么,返回什么)。 
使用Ajax的真正挑战,是需要进行适当的手工编码,并相应格式化为服务端可用接受的数据结构。有几种格式供人们选择(最常用为JSON/XML)。正因 Ext是一种与服务器语言免依赖的机制,使得其它特定语言的库亦可用于Ext处理Ajax服务。只要页面接受到结果是EXT能处理的数据格式,Ext绝不会干涉服务器其他的事情!要全面讨论这个问题,已超出本文的范围。推荐正在Ajax环境下开发的您,继续深入阅读Ext Ajax教程。 

下一步是? 
现在你已经一睹Ext其芳容,知道她大概能做些什么了。下面的资源有助您进一步深入了解: 
EXT源码概述 
揭示源代码 
Javascript是一门解释型的语言,意味着在运行之前代码是没有经过编译的。按照这种理论,在你网站上所发播的Ext代码是我们看的懂的(human-readible)。我这里说“理论上”,是因为实际情况中,很多源代码是经过某些自动化步骤的处理,生成很小几行的文件最终发布的,通过剔除空白符号和注释,或混淆等的方法,以减小文件大小。 
仔细看看EXT标准源码ext-core.js,你会发现这是一段超长的源码。这是刚才提及的自动化步骤生成的结果--对浏览器来说不错!可是对于我们是难以阅读的。 
ext-core.js 
/* 
* Ext JS Library 1.1 
* Copyright(c) 2006-2007, Ext JS, LLC. 
* licensing@extjs.com 
* http://www.extjs.com/license 
*/ 

Ext.DomHelper=function(){var _1=null;var _2=/^(?:br|frame... 

Ext.Template=function(_1){if(_1 instanceof Array){_1... 
... 
接着看下去的是ext-core-debug.js (注意在文件名后面加上-debug的JS文件), 我会发现是全部已格式化好的源代码。这个文件是配合调时器所使用的,像Firebug的工具能够可以让你一步一步地、一行一行地调试代码。你也会发现文件的体积将近大了一倍之多,这便是没有压缩或混淆的缘故。 
ext-core-debug.js 
/* 
* Ext JS Library 1.1 
* Copyright(c) 2006-2007, Ext JS, LLC. 
* licensing@extjs.com 
* http://www.extjs.com/license 
*/ 

Ext.DomHelper = function(){ 
var tempTableEl = null; 
var emptyTags = /^(?:br|frame|hr|img|input|link|meta|range|spacer|wbr|area|param|col)$/i; 
var tableRe = /^table|tbody|tr|td$/i; 
... 
该调试版本可以在调试阶段方便地检查EXT库运行到哪一步,但是你还是会错过一个有价值的...代码注释!要完整地看到代码,就要阅读真正的原始代码! 
发布Ext源码时的一些细节 
你在download得到的压缩文档,包含在这些文件夹之中的,有一source的子目录。在这个文件夹里面,正如所料,是全部的EXT的源文件,遵从Lesser GNU (LGPL) 开源的协议。对于EXT开发者来说应该非常适合。 
用你日常使用文本编辑器打开源代码的任意一个文件(推荐有高亮显示的编辑器,或是在这里full-featured IDE看看),便可以开始我们的探险! 
我应该从哪里开始? 
Ext代码库里面包含了许多各种各样的文件,甚至令人觉得有点望而生畏。好在,Ext是一个通过充分考虑后而设计的JavaScript库,--在底层的代码为各项应用提供稳健的基础如跨浏览器的各种DOM操控,使得在上层的类classes运行于一个较高级的抽象层面(class 一术语与我们已习惯的Java和C++语言稍微有所不同,但一些概念如继承则可是如此类推去理解的--有关面向对象的JavaScript的更多资料,请参见Introduction to object-oriented (OO) JavaScript)。 
这意味着,当浏览源码的时候,采取“自顶向下(bottom-up)”还是“自下向顶(top-down)”的方式,都是无关紧要的。你所熟悉API里面的代码已经是属于最高的抽象层面的范畴,你可以根据你的兴趣,顺着这些你熟悉的API逐步深入。但是你若赞同我的看法,并打算深入了解其个中原理,最理想的地方是从底层代码开始。 
适配器Adapters 
浏览器读取第一个源文件,当中的一个任务就是创建Ext对象本身。 Ext.js 
Ext = {}; 
Ext成型于YahooUI的Javascript库的扩展。在当时,Ext须依赖YUI的底层代码来处理跨浏览器的问题。现在ExtJS已经是独立、免依赖的库了(standalone ),你可将YUI替换为另外你所选择javascript库,如prototype、jQuery、或者是这些之中的最佳选择,-Ext自带的底层库。负责将这些库(包括Ext自带的底层库)映射为Ext底层库的这部分代码,我们称之为适配器(Adapters)。这部分源码位于source/adapter的子目录。当项目引入Ext的时候便需要选择好你准备使用的适配器。 
核心Core 
source/core中的文件是构建于适配器API之上的“相对”最底层的源码。有些的源码甚至“底层”到直接为独立库的代码直接使用。这意味着应先了解和学习这整个库,再学习剩余的部分也不迟。要了解Ext的各种“Magic”和核心层面,就应该把重点放在source/core 目录下的各个源代码。 

Javascript中的作用域(scope) 
事前准备 
学习本教程的最佳方法是随手准备好Firefox中的工具Firebug。这样使得您可以即刻测试教程的例子。 
如果机子上还没有FireFox和FireBug,就应该尽快安装一套来用。 
定义 
作用域scope 
1.(名词)某样事物执行、操作、拥有控制权的那么一个区域 [1] 
2. (名词) 编写程序时,程序之中变量的可见度;例如,一个函数能否使用另外一个函数所创建的变量。[2] 
可是这能够说明什么问题呢? 每当有人在说“这是作用域的问题”或“作用域搞错了”的时候,那就是说某个函数运行起来的时候,找不到正确变量的位置。这样我们便知道应该从哪一方面入手,查找出问题所在。 
正式开始 
实际上每一个你定义的函数都是某个对象的方法。甚至是这样的写法: 
function fn() { 
alert(11); 
老兄你不是故弄玄虚吧~。做一个这样的演示可真得是简单得要命。没错!本例不需要任何Javascript文件,服务器或html。你只要打开 firefox,弹出firebug,点击console tab。在Firefox状态栏上面看到有>>>提示的地方就可以输入了。 
输入: 
function fn() { alert(11); }; 
然后回车。一切安然...你刚才做的实际上是定义了一个函数fn。接着试试: 
fn(); 
然后回车。得到11的警告窗口?还不错吧?接着试试: 
window.fn(); 
this.fn(); 
得到一样的结果吧?这是因为函数fn是window对象的一个方法,在第二行的"this"的作用域实际指向了windows对象。不过多数情况中你不需要像这样window.myFunction(...)地调用函数,这样太麻烦了,程序员工作起来会很不方便。 
window对象 
window 对象总是存在的,你可理解其为一个浏览器窗口对象。它包含了其它所有的对象如document 和所有的全局变量。 
你可以打开Firebug,切换到 Script 页面并在Firebug右侧的New watch expression... 里面输入 window。观察window对象究竟有什么在里面。 
接着,尝试找出我们之前定义过的fn函数。 
另外,每个frame或iframe拥有其自身的window对象,其自身的全局空间。 
理解作用域 
接下的内容开始有点复杂了。切换到Firebug Console标签页然后输入: 
var o1 = {testvar:22, fun:function() { alert('o1: ' + this.testvar); }}; 
var o2 = {testvar:33, fun:function() { alert('o2: ' + this.testvar); }}; 
结果是什么?你声明了o1 和 o2两个对象,分别都有一些属性和方法,但值不同。 

接着试试: 
fun(); 
window.fun(); 
this.fun(); 
出错了,是吧?因为window对象(等价于this)并没有fun的方法。试一试下面的: 
o1.fun(); 
o2.fun(); 
22和33出来了?非常好! 
接下来这部分的内容最复杂啦。基于这个原始的函数,如果对象的数量多的话,你必须为每个对象加上这个函数-明显是重复劳动了。这样说吧,o1.fun写得非常清晰的而且为了搞掂它已经占用了我一个星期的开发时间。想象一下代码到处散布着this变量,怎么能不头疼?如果要将调用(执行)的o1.fun方法但this会执行o2,应该怎么实现呢?试一试下面的: 
o1.fun.call(o2); 
明白了吗?当执行o1的fun方法时你强行将变量this指向到o2这个对象,换句话说,更加严谨地说:o1.fun的方法在对象o2的作用域下运行。 
当运行一个函数,一个对象的方法时,你可将作用域当作this值的变量。 
变量的可见度 
变量的可见度和作用域的关系非常密切。我们已经了解到,可在任何对象的外部,声明变量,或在全局的函数(函数也是变量的一种)也可以,更严格说,它们是全局对象window的属性。 全局变量在任何地方都可见;无论函数的内部还是外部。如果你在某一个函数内修改了一个全局变量,其它函数也会得知这个值是修改过的。 
对象可以有它自己的属性(像上面的testvar),这些属性允许从内部或是外部均是可见的。试: 
alert(o1.testvar); // 从外部访问o1的属性testvar 
从内部访问的演示可在两个测试对象的fun方法找到。 
用关键字var在内部声明,相当于声明局部变量(局部声明也是在一条链上,即Scope Chain 作用域链上,Frank注): 
i = 44; 
function fn2() { 
var i = 55; 
alert(i); 
fn2(); 
将得到什么?对了,55。声明在函数fn2的变量i是一个本地变量(局部变量),和等于44的全局变量i 44没什么关系。 But: 
alert(i); 
这会访问全局变量i,显示44。 
希望本文能帮助读者彻底理解作用域变量可见性的含义。 
EXT程序规划入门 
事前准备 
本教程假设你已经安装好ExtJS库。安装的目录是extjs 并位于你程序的上一级目录。如果安装在其它地方你必须更改路径,更改示例文件中script标签的src的属性。 
需要些什么? 
除ExtJS库本身外,我们还需要两个文件: 
? applayout.html 
? applayout.js 

先看看一份html文档,比较精简。并附有详细说明: 
applayout.html 
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" 
"http://www.w3.org/TR/html4/loose.dtd"> 
<html> 
<head> 
<meta http-equiv="Content-Type" content="text/html; charset=utf-8"> 
<link rel="stylesheet" type="text/css" href="../extjs/resources/css/ext-all.css"> 
<script type="text/javascript" src="../extjs/adapter/ext/ext-base.js"></script> 
<script type="text/javascript" src="../extjs/ext-all-debug.js"></script> 
<script type="text/javascript" src="applayout.js"></script> 
<!-- 本地化的脚本引用在这里 --> 
<script type="text/javascript"> 
Ext.onReady(myNameSpace.app.init, myNameSpace.app); 
</script> 
<title>Application Layout Tutorial</title> 
</head> 
<body> 
</body> 
</html> 
开头的两行声明了文档的类型。程序可以不用doctype,但是这样的话浏览器可能默认其为Quirks怪僻类型,会导致处理跨浏览器这一问题上出现差异。 

我们采用HTML 4.01 Transitional的文档类型,该类型在主流浏览器上支持得不错。当然,你也可以根据你的需求采用其它类型的doctype,但是记住别忘了要加上doctype。 
接着指定HTML头部的Content-Type。做这些事情其实很琐碎,但总是有益处。 
然后引入EXT的样式,适配器和EXTJS本身。有两种版本的ExtJS: 
? ext-all.js - 不能直接阅读,加载时更快,用于产品发布 
? ext-all-debug.js - 便于阅读,开发阶段使用, 
开发阶段的时候便需要引入debug的版本。 

applayout.js这个文件就是我们的程序,紧跟着的是本地化的脚本,这里可以换成Extjs翻译好的版本 
跟着我们开始分配事件句柄(event handler),使得在文档全部加载完毕后,程序就可以初始化(运行)。 
下面的这一行: 
Ext.onReady(myNameSpace.app.init, myNameSpace.app); 
可这样说:当文档全部加载完毕,就运行myNameSpace.app的init方法,规定的作用域是myNameSpace.app。 
然后是标题,头部的结尾,body(当前空)和结束标签。 
文档的解说就说到这儿了。 
applayout.js 
/** 
* Application Layout 
* by Jozef Sakalos, aka Saki 
* http://extjs.com/learn/Tutorial:Application_Layout_for_Beginners_(Chinese) 
*/ 

// 填充图片的本地引用 
Ext.BLANK_IMAGE_URL = '../extjs/resources/images/default/s.gif'; 

// 创建命名空间 
Ext.namespace('myNameSpace'); 

// 创建应用程序 
myNameSpace.app = function() { 
// 元素还没创建,未能访问 

// 私有变量 

// 私有函数 

// 公共空间 
return { 
// 公共的属性,如,要转换的字符串 
// 公共方法 
init: function() { 
alert('应用程序初始化成功。'); 
}; 
}(); // 程序底部 

// 文件底部 
文件最开始的几行是注释,说明该文件的主要内容,作者,作者相关的资讯。没有任何注释的程序也可以正常的运行,-但请记住:每次写的程序要容易给人看得懂的 Always write your application as if you would write it for another.当你回头看你几个月前写的代码,发现你根本不记得自己写过什么的时候,就会明白这个道理,并养成编码的好习惯。接着是要指向你服务器的填充图片,如不指定则默认extjs.com。每次运行程序的时候都访问extjs.com,不推荐这样,应该先修改这个常量值指向到本地。 

现在自定义命名空间。将所有变量和方法都划分到一个全局对象下管理,这样的好处是避免了变量名冲突和由不同函数干扰了全局变量的值。名字(namespace)可按自己的方案选择。 

整段代码的重点是,我们创建了 myNameSpace对象的属性app,其值是一个函数立刻运行之后的返回值。 
如果运行我们的代码: 
var o = function() { 
return {p1:11, p2:22}; 
}(); 
实际上我们创建了一个匿名函数(没有名字的函数),经过解释(预编译?)之后让它立刻运行(注意函数后面的())。最后将函数返回的对象(注意此时是一个object变量)分配到变量o。我们的程序便是这种思路去写的。 
你可以把私有变量和私有函数直接定义在function和return这两个声明之间,但是请切记:此时不能访问任何html页面中的元素,那会导致错误,因为这段代码在加载时页面的head中就运行了,而这时候html页面中的其它元素还没有被加载进来。 
另外一方面,函数init,是由匿名函数返回的对象的一个方法而已。它会在文档全部加载后才运行。换言之整个DOM树已经是可用的了。 
一切都还好吧~如果能正确运行http://yourserver.com/applayout/applayout.html,不出现什么错误的话将出现一个警告。 
接下来是利用这个空白的模板,讨论本文的重点。 
公开Public、私有Private、特权的Privileged? 
让我们加入一些有趣内容到程序中去吧。在页面applayout.html的body标签中加入: 
<div id="btn1-ct"></div> 
空白的div会当作按钮的容器来使用。然后在applayout.js加入下来代码: 
/** 
* Application Layout 
* by Jozef Sakalos, aka Saki 
* http://extjs.com/learn/Tutorial:Application_Layout_for_Beginners_(Chinese) 
*/ 

// 填充图片的本地引用 
Ext.BLANK_IMAGE_URL = '../extjs/resources/images/default/s.gif'; 

// 创建命名空间 
Ext.namespace('myNameSpace'); 

// 创建应用程序 
myNameSpace.app = function() { 
// 元素还没创建,未能访问 

// 私有变量 
var btn1; 
var privVar1 = 11; 

// 私有函数 
var btn1Handler = function(button, event) { 
alert('privVar1=' + privVar1); 
alert('this.btn1Text=' + this.btn1Text); 
}; 

// 公共空间 
return { 
// 公共的属性,如,要转译的字符串 
btn1Text: 'Button 1' 

// 公共方法 
, init: function() { 
btn1 = new Ext.Button('btn1-ct', { 
text: this.btn1Text 
, handler: btn1Handler 
}); 
}; 
}(); //程序底部 

// 文件底部 
变量btn1 和privVar1 是私有的。 那意味着在程序外部他们是不能够被访问的,而且也不可见。私有函数btn1Handler也是同样道理。 
另外一个方面,btn1Text是公共变量所以可以被程序外部访问或者是修改(我们稍后将会演示)。 
函数init是特权的,即是私有变量和公共变量两者都可以被它访问到。在本例中,公共变量this.btn1Text和私有函数btn1Handler都能够被特权函数init所访问。同时,相对外界来说,它也属于公共成员的一种。 
Ok,运行看看。能看到左上角的按钮吗?很好,点击它。得到一个privVar1=11的警告。这说明私有函数能访问私有变量。 
但是在第二个警告中遇到了this.btn1Text=undefined的问题,这好像不应该这样。个中原因是因为位于事件句柄(event handler)中的变量this没有正确指向到我们的程序。你需要用scope:this 指明这个作用域(这里的this关键字所指示的scope应该是btn1变量): 
btn1 = new Ext.Button('btn1-ct', { 
text: this.btn1Text 
, handler: btn1Handler 
, scope: this 
}); 
刷新一下,可以了吧? 
重写公共变量 
在applayout.js底部加入下列代码: 
Ext.apply(myNameSpace.app, { 
btn1Text:'Taste 1' 
}); 

// 文件底部 
这代码是用来干什么的呢?首先它创建了我们的程序对象然后改变(重写)公共变量btn1Text的值。运行后将看到按钮上文字的变化。 

把文本都放到公共变量,以便于以后的国际化翻译工作,而不需要修改程序的内部代码。 
当然你也可以将其它的值例如尺寸、样式、等等的总之是可自定义的选项都放到公共变量中。免于在数千行代码之中为查找某个颜色而费劲。 
重写(Overriding)公共函数 
接着更改一下代码,让它读起来是这样的: 
Ext.apply(myNameSpace.app, { 
btn1Text:'Taste 1' 
, init: function() { 
try { 
btn1 = new Ext.Button('btn1-ct', { 
text: this.btn1Text 
, handler: btn1Handler 
, scope: this 
}); 
catch(e) { 
alert('错误: "' + e.message + '" 发生在行: ' + e.lineNumber); 
}); 

// end of file 
我们这里将init重写了一篇,主要是在原来代码的基础上加入异常控制,以便能够捕获异常。试运行一下看还有没有其它的变化? 
嗯 是的,出现了btn1Handler 未定义的错误。这是因为新的函数虽然尝试访问私有变量但它实际是不允许的。正如所见,原init是特权函数可访问私有空间,但新的init却不能。之所以不能访问私有空间,是因为:禁止外部代码No code from outside,哪怕是尝试重写特权方法。 
DomQuery基础 
本教程旨在为读者了解怎样利用单例对象Ext.DomQuery,浏览穿梭于DOM树之中和获取对象,提供一个起点。 
DomQuery基础 
DomQuery的select函数有两个参数。第一个是选择符字符(selector string )而第二个是欲生成查询的标签ID(TAG ID)。 
本文中我准备使用函数“Ext.query”但读者须谨记它是“Ext.DomQuery.select()”的简写方式。 
这是要入手的html: 
<html> 
<head> 
<script type="text/javascript" src="../js/firebug/firebug.js"></script> 
</head> 
<body> 
<script type="text/javascript" src="../ext/ext-base.js"></script> 
<script type="text/javascript" src="../ext/ext-core.js"></script> 
<div id="bar" class="foo"> 
I'm a div ==> my id: bar, my class: foo 
<span class="bar">I'm a span within the div with a foo class</span> 
<a href="http://www.extjs.com" target="_blank">An ExtJs link</a> 
</div> 
<div id="foo" class="bar"> 
my id: foo, my class: bar 
<p>I'm a P tag within the foo div</p> 
<span class="bar">I'm a span within the div with a bar class</span> 
<a href="#">An internal link</a> 
</div> 
</body> 
</html> 

第一部分:元素选择符Selector 
假设我想获取文档内所有的“span”标签: 
// 这个查询会返回有两个元素的数组因为查询选中对整个文档的所有span标签。 
Ext.query("span"); 
// 这个查询会返回有一个元素的数组因为查询顾及到了foo这个id。 
Ext.query("span", "foo"); 
注意刚才怎么传入一个普通的字符串作为第一个参数。 
按id获取标签,你需要加上“#”的前缀: 
// 这个查询会返回包含我们foo div一个元素的数组! 
Ext.query("#foo"); 
按class name获取标签,你需要加上“.”的前缀: 
/*这个查询会返回有一个元素的数组, 
包含与之前例子一样的div但是我们使用了class name来获取*/ 
Ext.query(".foo"); 
你也可以使用关键字“*”来获取所有的元素: 
// 这会返回一个数组,包含文档的所有元素。 
Ext.query("*"); 
要获取子标签,我们只须在两个选择符之间插入一个空格: 
// 这会返回有一个元素的数组,包含p标签的div标签 
Ext.query("div p"); 
// 这会返回有两个元素的数组,包含span标签的div标签 
Ext.query("div span"); 

还有三个的元素选择符,待后续的教程会叙述。 "" 
如果朋友你觉得这里说得太简单的话,你可以选择到DomQuery 文档看看,可能会有不少收获:) 
第二部分:属性选择符Attributes selectors 
这些选择符可让你得到基于一些属性值的元素。属性指的是html元素中的href, id 或 class。 
// 我们检查出任何存在有class属性的元素。 
// 这个查询会返回5个元素的数组。 
Ext.query("*[class]"); // 结果: [body#ext-gen2.ext-gecko, div#bar.foo, span.bar, div#foo.bar, span.bar] 
现在我们针对特定的class属性进行搜索。 
// 这会得到class等于“bar”的所有元素 
Ext.query("*[class=bar]"); 

// 这会得到class不等于“bar”的所有元素 
Ext.query("*[class!=bar]"); 

// 这会得到class从“b”字头开始的所有元素 
Ext.query("*[class^=b]"); 

//这会得到class由“r”结尾的所有元素 
Ext.query("*[class$=r]"); 

//这会得到在class中抽出“a”字符的所有元素 
Ext.query("*[class*=a]"); 
第三部分: CSS值元素选择符 
这些选择符会匹配DOM元素的style属性。尝试在那份html中加上一些颜色: 
<html> 
<head> 
<script type="text/javascript" src="../js/firebug/firebug.js"></script> 
</head> 
<body> 
<script type="text/javascript" src="../ext/ext-base.js"></script> 
<script type="text/javascript" src="../ext/ext-core.js"></script> 
<div id="bar" class="foo" style="color:red;"> 
我是一个div ==> 我的id是: bar, 我的class: foo 
<span class="bar" style="color:pink;">I'm a span within the div with a foo class</span> 
<a href="http://www.extjs.com" target="_blank" style="color:yellow;">An ExtJs link with a blank target!</a> 
</div> 
<div id="foo" class="bar" style="color:fushia;"> 
my id: foo, my class: bar 
<p>I'm a P tag within the foo div</p> 
<span class="bar" style="color:brown;">I'm a span within the div with a bar class</span> 
<a href="#" style="color:green;">An internal link</a> 
</div> 
</body> 
</html> 
基于这个CSS的颜色值我们不会作任何查询,但可以是其它的内容。它的格式规定是这样的: 
元素{属性 操作符 值} 
注意我在这里是怎么插入一个不同的括号。 
所以,操作符(operators)和属性选择符(attribute selectors)是一样的。 
// 获取所以红色的元素 
Ext.query("*{color=red}"); // [div#bar.foo] 

// 获取所有粉红颜色的并且是有红色子元素的元素 
Ext.query("*{color=red} *{color=pink}"); // [span.bar] 

// 获取所有不是红色文字的元素 
Ext.query("*{color!=red}"); 
//[html, head, script firebug.js, link, body#ext-gen2.ext-gecko, 
// script ext-base.js, script ext-core.js, span.bar, 
//a www.extjs.com, div#foo.bar, p, span.bar, a test.html#] 

// 获取所有颜色属性是从“yel”开始的元素 
Ext.query("*{color^=yel}"); // [a www.extjs.com] 

// 获取所有颜色属性是以“ow”结束的元素 
Ext.query("*{color$=ow}"); // [a www.extjs.com] 

// 获取所有颜色属性包含“ow”字符的元素 
Ext.query("*{color*=ow}"); // [a www.extjs.com, span.bar] 

第四部分:伪类选择符Pseudo Classes selectors 
仍然是刚才的网页,但是有所不同的只是新加上了一个UL元素、一个TABLE元素和一个FORM元素,以便我们可以使用不同的伪类选择符,来获取节点。 
<html> 
<head> 
<script type="text/javascript" src="../js/firebug/firebug.js"></script> 
</head> 
<body> 
<script type="text/javascript" src="../ext/ext-base.js"></script> 
<script type="text/javascript" src="../ext/ext-core.js"></script> 
<div id="bar" class="foo" style="color:red; border: 2px dotted red; margin:5px; padding:5px;"> 
I'm a div ==> my id: bar, my class: foo 
<span class="bar" style="color:pink;">I'm a span within the div with a foo class</span> 
<a href="http://www.extjs.com" target="_blank" style="color:yellow;">An ExtJs link with a blank target!</a> 
</div> 
<div id="foo" class="bar" style="color:fushia; border: 2px dotted black; margin:5px; padding:5px;"> 
my id: foo, my class: bar 
<p>I'm a P tag within the foo div</p> 
<span class="bar" style="color:brown;">I'm a span within the div with a bar class</span> 
<a href="#" style="color:green;">An internal link</a> 
</div> 
<div style="border:2px dotted pink; margin:5px; padding:5px;"> 
<ul> 
<li>Some choice #1</li> 
<li>Some choice #2</li> 
<li>Some choice #3</li> 
<li>Some choice #4 with a <a href="#">link</a></li> 
</ul> 
<table style="border:1px dotted black;"> 
<tr style="color:pink"> 
<td>1st row, 1st column</td> 
<td>1st row, 2nd column</td> 
</tr> 
<tr style="color:brown"> 
<td colspan="2">2nd row, colspanned! </td> 
</tr> 
<tr> 
<td>3rd row, 1st column</td> 
<td>3rd row, 2nd column</td> 
</tr> 
</table> 
</div> 
<div style="border:2px dotted red; margin:5px; padding:5px;"> 
<form> 
<input id="chked" type="checkbox" checked/><label for="chked">I'm checked</label> 
<br /><br /> 
<input id="notChked" type="checkbox" /><label for="notChked">not me brotha!</label> 
</form> 
</div> 
</body> 
</html> 

接着: 
/* 
this one gives us the first SPAN child of its parent 
*/ 
Ext.query("span:first-child"); // [span.bar] 

/* 
this one gives us the last A child of its parent 
*/ 
Ext.query("a:last-child") // [a www.extjs.com, a test.html#] 

/* 
this one gives us the second SPAN child of its parent 
*/ 
Ext.query("span:nth-child(2)") // [span.bar] 

/* 
this one gives us ODD TR of its parents 
*/ 
Ext.query("tr:nth-child(odd)") // [tr, tr] 

/* 
this one gives us even LI of its parents 
*/ 
Ext.query("li:nth-child(even)") // [li, li] 

/* 
this one gives us A that are the only child of its parents 
*/ 

Ext.query("a:only-child") // [a test.html#] 

/* 
this one gives us the checked INPUT 
*/ 
Ext.query("input:checked") // [input#chked on] 

/* 
this one gives us the first TR 
*/ 
Ext.query("tr:first") // [tr] 

/* 
this one gives us the last INPUT 
*/ 
Ext.query("input:last") // [input#notChked on] 

/* 
this one gives us the 2nd TD 
*/ 
Ext.query("td:nth(2)") // [td] 

/* 
this one gives us every DIV that has the "within" string 
*/ 
Ext.query("div:contains(within)") // [div#bar.foo, div#foo.bar] 

/* 
this one gives us every DIV that doesn't have a FORM child 
*/ 
Ext.query("div:not(form)") [div#bar.foo, div#foo.bar, div] 

/* 
This one gives use every DIV that has an A child 
*/ 
Ext.query("div:has(a)") // [div#bar.foo, div#foo.bar, div] 

/* 
this one gives us every TD that is followed by another TD. 
obviously, the one that has a colspan property is ignored. 
*/ 
Ext.query("td:next(td)") // [td, td] 

/* 
this one gives us every LABEL that is preceded by an INPUT 
*/ 
Ext.query("label:prev(input)") //[label, label] 
扩展EXT组件 
要创建的扩展是一个在文字前面能够显示图标的这么一个Ext.form.Combobox。将其中一个功能举例来说,就是要在一块选择里,国家名称连同国旗一并出现。 
我们先给扩展起个名字,就叫Ext.ux.IconCombo。 
文件的创建 
首要的步骤是准备好开发中将会使用的文件。需下列文件: 
? iconcombo.html: 新扩展将会使用的 html markup 
? iconcombo.js: 程序javascript代码 
? Ext.ux.IconCombo.js: 扩展的javascript文件 
? Ext.ux.IconCombo.css: 扩展样式表 
iconcombo.html 
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" 
"http://www.w3.org/TR/html4/loose.dtd"> 
<html> 
<head> 
<meta http-equiv="Content-Type" content="text/html; charset=utf-8"> 
<link rel="stylesheet" type="text/css" href="../extjs/resources/css/ext-all.css"> 
<link rel="stylesheet" type="text/css" href="Ext.ux.IconCombo.css"> 
<script type="text/javascript" src="../extjs/adapter/ext/ext-base.js"></script> 
<script type="text/javascript" src="../extjs/ext-all-debug.js"></script> 
<script type="text/javascript" src="Ext.ux.IconCombo.js"></script> 
<script type="text/javascript" src="iconcombo.js"></script> 
<!-- A Localization Script File comes here --> 
<script type="text/javascript">Ext.onReady(iconcombo.init, iconcombo);</script> 
<title>Ext.ux.IconCombo Tutorial</title> 
</head> 
<body> 
<div style="position:relative;width:300px;top:24px;left:64px;font-size:11px"> 
<div>Icon combo:</div> 
<div id="combo-ct"></div> 
</div> 
</body> 
</html> 
该文件来自教程Ext程序规划入门 的轻微修改。 
iconcombo.js 
/** 
* Ext.ux.IconCombo Tutorial 
* by Jozef Sakalos, aka Saki 
* http://extjs.com/learn/Tutorial:Extending_Ext_Class 
*/ 

// 引用本地空白文件 
Ext.BLANK_IMAGE_URL = '../extjs/resources/images/default/s.gif'; 

// 创建程序 
iconcombo = function() { 

// 公共空间 
return { 
// public properties, e.g. strings to translate 

// public methods 
init: function() { 
var icnCombo = new Ext.ux.IconCombo({ 
store: new Ext.data.SimpleStore({ 
fields: ['countryCode', 'countryName', 'countryFlag'], 
data: [ 
['US', 'United States', 'x-flag-us'], 
['DE', 'Germany', 'x-flag-de'], 
['FR', 'France', 'x-flag-fr'] 
}), 
valueField: 'countryCode', 
displayField: 'countryName', 
iconClsField: 'countryFlag', 
triggerAction: 'all', 
mode: 'local', 
width: 160 
}); 
icnCombo.render('combo-ct'); 
icnCombo.setValue('DE'); 
}; 
}(); // end of app 

// end of file 

我们在这个文件中创建IconCombo,以便可以进行扩展和测试。 
Ext.ux.IconCombo.js 
// Create创建用户的扩展(User eXtensions namespace (Ext.ux)) 
Ext.namespace('Ext.ux'); 

/** 
* Ext.ux.IconCombo 扩展类 
* @author Jozef Sakalos, aka Saki 
* @version 1.0 
* @class Ext.ux.IconCombo 
* @extends Ext.form.ComboBox 
* @constructor 
* @param {Object} config 配置项参数 
*/ 
Ext.ux.IconCombo = function(config) { 

// 调用父类的构建函数 
Ext.ux.IconCombo.superclass.constructor.call(this, config); 

} // Ext.ux.IconCombo构建器的底部 

// 进行扩展 
Ext.extend(Ext.ux.IconCombo, Ext.form.ComboBox, { 

}); // 扩展完毕 

// 文件底部 
运行到这一步,实际这是一个没有对Ext.form.ComboBox新加任何东西的空扩展。我们正是需要这个完成好的空扩展,再继续下一步。 
Ext.ux.IconCombo.css 
.x-flag-us { 
background-image: url(../img/flags/us.png); 
.x-flag-de { 
background-image: url(../img/flags/de.png); 
.x-flag-fr { 
background-image: url(../img/flags/fr.png); 
路径可能根据你所在的国旗放置目录有所不同。国旗的资源可在here下载。 
Let's go 
So far so good!如果你浏览iconcombo.html应该会发现一个包含三个选项的标准combo,而德国的那个是选中的...是吧?不过还没有图标... 
现在正是开始工作。在调用父类构建器之后加入下列行: 
this.tpl = config.tpl || 
'<div class="x-combo-list-item">' 
+ '<table><tbody><tr>' 
+ '<td>' 
+ '<div class="{' + this.iconClsField + '} x-icon-combo-icon"></div></td>' 
+ '<td>{' + this.displayField + '}</td>' 
+ '</tr></tbody></table>' 
+ '</div>' 
在这一步,我们将默认combox box的模版重写为iconClsField模版。 
现在加入Ext.ux.IconCombo.css中的样式文件: 
.x-icon-combo-icon { 
background-repeat: no-repeat; 
background-position: 0 50%; 
width: 18px; 
height: 14px; 
不错!可以测试一下了,刷新的页面,还好吧!?嗯,列表展开时那些漂亮的图标就出来了。。还有。。我们不是要在关闭时也出现图标的吗? 
在构建器中加入创建模版的过程: 
this.on({ 
render:{scope:this, fn:function() { 
var wrap = this.el.up('div.x-form-field-wrap'); 
this.wrap.applyStyles({position:'relative'}); 
this.el.addClass('x-icon-combo-input'); 
this.flag = Ext.DomHelper.append(wrap, { 
tag: 'div', style:'position:absolute' 
}); 
}} 
}); 
加入 事件render的侦听器,用于调整元素样式和创建国旗的div容器。如后按照下列方式进行扩展: 
// 进行扩展 
Ext.extend(Ext.ux.IconCombo, Ext.form.ComboBox, { 

setIconCls: function() { 
var rec = this.store.query(this.valueField, this.getValue()).itemAt(0); 
if(rec) { 
this.flag.className = 'x-icon-combo-icon ' + rec.get(this.iconClsField); 
}, 

setValue: function(value) { 
Ext.ux.IconCombo.superclass.setValue.call(this, value); 
this.setIconCls(); 

}); // 扩展完毕 
新增 setIconCls函数并重写setValue函数。我们还是需要父类的setValue的方法来调用一下,接着再调用setIconCls的函数。最后,我们应该在文件Ext.ux.IconCombo.css加入下列代码: 
.x-icon-combo-input { 
padding-left: 26px; 
.x-form-field-wrap .x-icon-combo-icon { 
top: 3px; 
left: 6px; 
完成 
最后再刷新一下,如果一切顺利,那这个就是新的Ext.ux.IconCombo扩展! 希望你能在此基础上扩展更多的组件! 
谢谢Brian Moeskau提醒,使得能进一步精简Ext.ux.IconCombo 代码,才称得上最终版本。最终代码和CSS为: 
Ext.ux.IconCombo.js 
// Create user extensions namespace (Ext.ux) 
Ext.namespace('Ext.ux'); 

/** 
* Ext.ux.IconCombo Extension Class 
* @author Jozef Sakalos 
* @version 1.0 
* @class Ext.ux.IconCombo 
* @extends Ext.form.ComboBox 
* @constructor 
* @param {Object} config Configuration options 
*/ 
Ext.ux.IconCombo = function(config) { 

// call parent constructor 
Ext.ux.IconCombo.superclass.constructor.call(this, config); 

this.tpl = config.tpl || 
{' 
+ this.displayField 
+ '} 

this.on({ 
render:{scope:this, fn:function() { 
var wrap = this.el.up('div.x-form-field-wrap'); 
this.wrap.applyStyles({position:'relative'}); 
this.el.addClass('x-icon-combo-input'); 
this.flag = Ext.DomHelper.append(wrap, { 
tag: 'div', style:'position:absolute' 
}); 
}} 
}); 
} // end of Ext.ux.IconCombo constructor 

// extend 
Ext.extend(Ext.ux.IconCombo, Ext.form.ComboBox, { 

setIconCls: function() { 
var rec = this.store.query(this.valueField, this.getValue()).itemAt(0); 
if(rec) { 
this.flag.className = 'x-icon-combo-icon ' + rec.get(this.iconClsField); 
}, 

setValue: function(value) { 
Ext.ux.IconCombo.superclass.setValue.call(this, value); 
this.setIconCls(); 

}); // end of extend 

// end of file 
Ext.ux.IconCombo.css 
css 
/* application specific styles */ 
.x-flag-us { 
background-image:url(../img/flags/us.png); 
.x-flag-de { 
background-image:url(../img/flags/de.png); 
.x-flag-fr { 
background-image:url(../img/flags/fr.png); 

/* Ext.ux.IconCombo mandatory styles */ 
.x-icon-combo-icon { 
background-repeat: no-repeat; 
background-position: 0 50%; 
width: 18px; 
height: 14px; 
.x-icon-combo-input { 
padding-left: 25px; 
.x-form-field-wrap .x-icon-combo-icon { 
top: 3px; 
left: 5px; 
.x-icon-combo-item { 
background-repeat: no-repeat; 
background-position: 3px 50%; 
padding-left: 24px; 
EXT的布局(Layout) 
Ext的layout布局对于建立WEB程序尤为有用。关于布局引擎(layout engine),区域管理器(region manager)的教程将分为几部分,本文是第一篇,为您介绍如何创建区域,如何增加版面到这些区域。 
布局引擎(layout engine)这一功能早已在EXT前个ALPHA实现了。 Jack Slocum对于怎样环绕某一区域,给与指定区域管理的策略,和建立界面的问题,在他的第一、第二篇关于跨浏览器的WEB2.0布局功能的博客中,进行过讨论。定义一个DOM元素的边界(edge),使之一个布局的边框(border)--这种做法使得创建“富界面”客户端UI的开发更进一大步。 
布局管理器(layout manager)负责管理这些区域。布局管理的主要的用户组件是BorderLayout类。该类为EXT开发富界面的程序提供了一个切入点。Layout的含意是划分好一些预定的区域。可用的区域分别有south, east, west, north,和center。每一个BorderLayout对象都提供这些区域但只有center要求必须使用的。如果你在单独一个区域中包含多个面板,你可通过NestedLayoutPanel 类套嵌到BorderLayout 实例中。 
注意事项:本教程的每个文件都是.html和.js格式的。教程每一步都有演示,你也可以下载这些文件在编辑器(zip格式提供在这里)中看看发生什么事。 
面板(Panel)是区域管理(region management)的另外一个组件。面板提供了这么一个地方,可为您的EXT器件(widget)、加载的HTML,嵌入的IFrames、或者是你日常在HTML页面上摆放的随便一样东西。NestedLayoutPanel也是一个面板,只不过用于链接多个BorderLayout的区域,其它的面板包括内容面板 ContentPanel,Grid面板 GridPanel,和Tree面板 TreePanel。 
简单的例子 
下面的layout包含 north, south, east, west,和center的区域,而且每个区域包含一个ContentPanel,各区域之间使用得了分隔条分割开。 

var mainLayout = new Ext.BorderLayout(document.body, 
north: { 
split: true, initialSize: 50 
}, 
south: { 
split: true, initialSize: 50 
}, 
east: { 
split: true, initialSize: 100 
}, 
west: { 
split: true, initialSize: 100 
}, 
center: { 
}); 
这是一个非常基本的layout,只是分配了东南西北中间的区域、分隔条、设置一下初始尺寸,并最迟定义中间区域。本例中, BorderLayout被绑定到"document.body"这个DOM元素,其实BorderLayout还可以绑定到任何一个封闭的DOM元素。定义好BorderLayout之后,我们加入ContentPanel对象(基于本例)。 
mainLayout.beginUpdate(); 
mainLayout.add('north', new Ext.ContentPanel('north-div', { 
fitToFrame: true, closable: false 
})); 
mainLayout.add('south', new Ext.ContentPanel('south-div', { 
fitToFrame: true, closable: false 
})); 
mainLayout.add('east', new Ext.ContentPanel('east-div', { 
fitToFrame: true, closable: false 
})); 
mainLayout.add('west', new Ext.ContentPanel('west-div', { 
fitToFrame: true, closable: false 
})); 
mainLayout.add('center', new Ext.ContentPanel('center-div', { 
fitToFrame: true 
})); 
mainLayout.endUpdate(); 
当前的例子是将ContentPanel加入到所有区域中。由调用mainLayout.beginUpdate()开始。beginUpdate ()告诉BorderLayout对象在执行endUpate()方法之前,先不要对加入的对象排版布局。这样的好处是避免了ContentPanel有对象加入时,导致UI的刷新,改进了整体的用户体验。执行beginUpdate()之后,加入五个ContentPanel对象到区域。所有的 ContentPanel对象(除中间的那个外),都设置是可关闭的(closbale)。所有的ContentPanel对象也都设置为自动适配它们的父元素。最后执行endUpdate()渲染layout。 
InternetExploer注意事项:BorderLayout所容纳的元素必须有一个SIZE以便正确渲染。典型地你无须为document.body 指明size,因为document.body通常是有size的了(大多数情况,-除非你在浏览器上什么也看不到)。但是如果你将layout连同容器放到现有的web页面上(‘可能是DIV),那么DIV的size应该先指明以便正确渲染。如下列显示正常: 
好,让我们趁热打铁,看看完整的layout是怎样的。假设ext是一子目录叫做ext-1.0,父目录下面的代码。 
simple.html: 

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值