web前端之dojo(用javascript语言实现的开源DHTML工具包)

86 篇文章 0 订阅
15 篇文章 0 订阅

web前端之dojo(用javascript语言实现的开源DHTML工具包)

一、开始Dojo开发

1、Dojo Toolkit 简介
Dojo 于 2004 年创建,使开发 DHTML 和 JavaScript web 应用程序开发流程更为容易,隐藏了很多现代 web 浏览器中普遍存在的跨浏览器矛盾。这使重点放在实现功能上,而不是调整代码使其在每个浏览器上运行。Dojo 属于 Dojo 基金会,该基金会是 Russell 和 Dylan Schiemann 于 2005 年创建的。Dojo 是一个开源软件(OSS),有双重许可,Academic Free License (AFL) 和一个修改的 BSD 许可,可以选择遵守一个。

特性一瞥
Dojo Toolkit 的特性可以分到 4 个不同部分。这种划分使得开发人员可以将库大小保持到最小,确保应用程序性能不受大量 JavaScript 库下载的影响。例如,如果只需要 Ajax 支持性能,您只需要包含 base 包;不需要包含扩展的 Dijit UI 组件,在本系列中稍后您将学习更多关于 Dojo 加载不同模块的方法。

Base 包提供 Dojo Toolkit 的基础,包括一些功能,比如 DOM 使用函数、CSS3 基于选择器的 DOM 查询、事件处理、基本的动画、以及 Dojo 基于类的面向对象特性。本文主要介绍 Base。
Core 包包含一些 Base 中没有包含的附加特性。通常,这些特性不像 Base 中的特性那样频繁使用;因此,它们单独加载减轻 Base 包的负担。从这一点上来讲,Core 包提供一些实际有用的组件,包括高级动画拖放、I/O、数据管理、国际化(i18n)、浏览器历史管理。Core 包不再本文范围之内。
Dijit 包包含 Dojo 小部件和组件的扩展 UI 库。这些小部件的一些示例包括对话框、日历、调色板、工具提示和树。它也包括一些表单控件,这比标准 HTML 表单控件提供更多的功能,还有一些完整的布局管理选项。本系列的第 3 部分将深入介绍 Dijit 特性。
Dojo eXtensions (DojoX) 包含工具箱的各个子项目。位于 DojoX 中的大多数是实验特性,但是也有一些稳定组件和特性。DojoX 将在本系列的第 3 部分做一简要介绍。

2、基础知识
在本小节,将学习一些很有用的 Dojo 功能,使之更易于使用 DOM 和阵列。

(1)DOM 实用函数
DOM 实用函数通过提供根据 ID 寻找条目的能力或者使用 CSS3 选择器,使得使用 DOM 中的元素比较容易。还有一些其他功能,可以创建和销毁元素,以及操作现有元素内容。

dojo.byId
dojo.byId 函数使您可以通过 id 属性选择一个 DOM 节点。该函数是标准 JavaScript
document.getElementById 函数的一个别名,但是显然简短易书写,而且还解决了很多跨浏览器矛盾。可通过 ID “message” 获取 DOM 元素内容:
dojo.byId(“message”).innerHTML;。

dojo.query
想一次引用几个元素就需要引入 dojo.query 函数。
如果您想一次引用 ID 为 “list” 的无序列表的所有 li子元素,然后将每个元素的内容打印到控制台上。有了 dojo.query,这将非常简单。
dojo.query 函数接受一个字符串参数,使用一个 CSS3 选择器引用您想选择的元素。使用 dojo.forEach 函数对这个数组进行遍历,然后将发现的每个元素的 innerHTML 属性输出到控制台。

(2)其他有用的实用函数
除了 DOM 查询和元素选择之外,在 Dojo 中还有很多其他实用函数使得使用 DOM 更为容易。在 “Hello, World” 示例中您已经见过其中两个。dojo.body 函数只返回文档的 body 元素,dojo.body 的一个引用,以及文档对象本身。dojo.create 让您可以快速创建一个新元素、定义它的属性、然后将它放在 DOM 中。
现有的其他函数包括 dojo.place,它允许您将现有的或新创建的元素放在文档的任何地方。dojo.empty 只执行您所期望的 — 清空一个 DOM 元素的内容。dojo.destroy 删除一个节点以及其所有子元素。

(3)数组和 NodeLists
数组允许您存储一个值集合,在标准 JavaScript 中提供。在 Dojo 中,数组被扩展来包括几个帮助函数。这些扩展的数组被称为 NodeLists。一个 NodeList 可以使用任何标准数组函数,以及其他 Dojo 特定函数,当您使用上一小节中描述的 dojo.query 函数时,返回值是一个 NodeList(准确地说是 dojo.NodeList)对象。我们来看看 NodeLists 中可用的函数。

dojo.forEach
第一个值得一提的函数是 dojo.forEach。这个函数允许您在 NodeList 上定义一个迭代器,提供一个可以应用到 NodeList 中的每一项上的功能。
正如您所看到的,forEach 函数遍历数组中的每一项,然后在其上执行附加功能。上面,您已经使用了一个匿名函数,但是您还可以使用一个命名函数。

dojo.indexOf
dojo.indexOf 函数使得您可以在一个数组中找出具体一个值的位置。最好是结合示例说明。使用上一小节创建的列表数组,尝试找出值为 name 的数组的地址:dojo.indexOf(list, “name”);.
在数组中值为 name 的项下标为 1。记住 JavaScript 数组的下标是从 0 开始的,因此该值是数组的第 2 项。如果您想要尝试使用该函数寻找一个数组中并不存在的值,返回值为 -1。
该函数返回给定值的第一个位置的下标,因此,如果数组中有多个项有相同的值,它只停在第一项上,Dojo 提供一个类似的函数,dojo.lastIndexOf,允许您寻找特定值的最后一个位置。该函数与 dojo.indexOf 运行方式完全一样。

dojo.filter
dojo.filter 函数支持您创建一个新数组,这个新数组是另一个数组的过滤版。例如,如果您想为您早期创建的列表数组创建一个新版本,但是又不想包括值为 is 的那些项。

(4)其他 NodeList 函数
Dojo 还有一些其他 NodeList 函数,在处理数组时非常有用。dojo.map 函数允许您创建一个新数组 — 现有数组的修改版。例如,如果您想创建一个代表货币值的数字数组。您可以使用一个映射函数以货币格式返回这些值的一个数组。dojo.some 允许您检查在数组中匹配指定标准的项是否至少有一个。类似地,dojo.every 用于检查是否每一项匹配指定标准。

3、Dojo 中的事件处理
多数 JavaScript 库都有一个本地 JavaScript 事件处理的跨浏览器实现,允许您附加函数,在事件触发时调用。这可能很有用,Dojo 通过允许您将函数链接到其他函数进一步延伸了这一概念,这可能是 DOM 事件、对象时间、用户定义函数、或者是 “topics”。

(1)DOM 事件处理
将函数附加到 DOM 对象的第一个方法是使用 dojo.connect 函数。
还可以:

dojo.query("#list li").onclick(function(e) {
    dojo.style(e.target, {
        fontWeight: "bold"
    });
});

e 参数是 Event 对象的一个引用,对象的 target 属性帮助您确定触发事件的元素。您可以使用这个来指出粗体样式应该被应用的元素。试一下单击这 3 个列表项,单击之后每一个都会变成粗体。

(2)连接函数到其他函数
在前面的示例中,您可以连接函数到 DOM 事件上。Dojo 也支持您以同样的方式将函数连接到其他函数。

function toggleImage() {
    //Code to show/hide loading image goes here
}
function callAjax() {
    toggleImage();
    //Code to call Ajax function goes here
}
function handleResponse() {
    //Code to handle Ajax response goes here
    toggleImage();
}

这段代码没什么问题,toggleImage 函数定义包含在 callAjax 和 handleResponse 函数中。如果您想添加另一个函数调用,您需要再次修改这些函数来包含额外调用。现在不需要向这些函数本身添加函数调用,只需要使用 dojo.connect 来在它们之间建立一个链接。

function toggleImage() {
    //Code to show/hide loading image goes here
}
function callAjax() {
    //Code to call Ajax function goes here
}
function handleResponse() {
    //Code to handle Ajax response goes here
}
dojo.connect(callAjax, toggleImage);
dojo.connect(handleResponse, toggleImage);

这种编码风格未必每个开发人员都会喜欢,但是它允许您以这样一种方式组织您的代码,使它阅读起来更容易些。

(3)发布和订阅主题
Dojo 事件处理最后值得注意的一点是发布和订阅主题的能力。这使得 Dojo 组件可以彼此交互,即使它们没有意识到彼此的存在。例如,假设您定义了一个名为 printName 的主题,它绑定了一个 message 对象包含一个人的姓和名。您可以有一个订阅这个主题的组件,在任何时候当另一个组件使用一个人的名字发布到该主题时,这将会将这个名字打印到控制台。

dojo.subscribe("printName", function(msg) {
    console.log("The person's name is: "+msg.first_name+" "+msg.last_name);
});

要发布到该主题,您需要传递一个附带主题 API 的对象数组,(在本例中,对象必须有一个名称和一个姓氏)。如下发布到一个主题:

dojo.publish("printName", [
    {
        first_name: "Joe",
        last_name: "Lennon"
    }
]);

4、使用 dojo.xhr* 增强 Ajax
创建 Ajax 驱动的 web 应用程序通常是通过创建 XmlHttpRequest (XHR) 对象完成的,这将向指定的 URL 发出一个 HTTP 请求,传递一个请求头部和正文并定义回调函数,来定义当返回一个成功响应正文或一个 HTTP 失败响应时该完成什么操作。实现跨浏览器 XHRs,至少可以说是很麻烦,但是,谢天谢地,Dojo 使用一组 dojo.xhr* 函数极大地减轻了这个令人头疼的麻烦,允许发出 GET、POST、PUT 和 DELETE 请求。
提供如下 4 个函数:

    xhrGet
    xhrPost
    xhrPut
    xhrDelete

所有这些函数都遵守相同的语法:接受一个属性配置对象作为参数。在这些对象中可以定义您想要发出的 Ajax 请求的各个方面。再一次说明,这些选项在所有 XHR 函数中都是一样的。

(1)配置选项
XHR 函数一些比较有用的配置选项如下:
url:这是 HTTP 请求的 URL。它必须和发出这一请求的页面有同样的域和端口组合。
handleAs:允许您定义响应的处理格式,默认是 text,但是,json、javascript、xml、还有一些其他选项也可用。在本节后面您将看到一个创建 Ajax 请求的示例,使用一个处理

JSON 响应格式的回调函数。
form: form 元素的一个引用或者字符串 ID 表示。form 中每个字段的值将被同请求一起作为请求体发送。
content: 一个对象,包含您想要传递给请求体中资源的参数。如果两者都提供,这个对象将与从 form 属性中获取的值混合在一起。

XHR 函数调用的示例

dojo.xhrGet({
    url: "save_data.php",
    content: {
        id: "100",
        first_name: "Joe",
        last_name: "Lennon"
    }
});

这将在同一个位置(与文档本身)异步向 save_data.php 文件发出一个 HTTP GET 请求。它也传递内容对象属性到 PHP 脚本作为参数。在 PHP 中,您可以使用 $_GET 变量检索这些值,然后可能将它们保存到一个数据库。

(2)回调函数
在之前的示例中,学习了如何使用 dojo.xhrGet 调用一个 Ajax 请求。这个示例对于实际发出请求已经足够了,它不提供设施来处理任何响应。回调函数也被传递到配置对象,下列选项是可用的:
load: 当 Ajax 请求返回一个成功响应消息时,执行此函数。响应数据和请求对象作为参数被传递到这个函数。
error: 如果 Ajax 请求出现问题,该函数将被调用。如果在 Ajax 请求中定义的 URL 无效、请求超时或者发生其他 HTTP 错误,这将会出现。错误消息和请求对象被作为参数传递。
handle:该函数允许您将加载和错误回调函数合并到一个函数中(如果您确实不关心请求结果是成功或是出现错误,这将非常有用)。

(3)使用 JSON 数据运行
我们通过创建一个更为真实的示例将 dojo.xhr* 函数用来进行一个更好的测试。首先,创建一个新文件 — 将该文件放在与 listing1.html 文件相同的目录下 — 并向其中添加一些 JSON 数据。

{
    count: 4,
    people: [
        {
            first_name: "Joe",
            last_name: "Lennon",
            age: 25                
        },{
            first_name: "Darragh",
            last_name: "Duffy",
            age: 33
        },{
            first_name: "Jonathan",
            last_name: "Reardon",
            age: 30
        },{
            first_name: "Finian",
            last_name: "O'Connor",
            age: 23
        }
    ]}

现在,在 Firebug 中创建一个 Ajax 请求(确保 listing1.html 页面被加载到 Firefox 中,这样 Dojo 才可以被加载)。该请求使用 load 回调函数处理 JSON 响应,并打印一个表格到页面。

dojo.xhrGet({
    url: "data.json",
    handleAs: "json",
    load: function(data) {
        var table = "<table border=\"1\">";
        table += "<tr><th>Name</th><th>Age</th>
</tr>";
        dojo.forEach(data.people, function(person) {
            table += "<tr><td>";
            table += person.first_name+" "+person.last_name;
            table += "</td><td>";
            table += person.age;
            table += "</td></tr>";
        });
        table += "</table>";
        dojo.place(table, dojo.body());
    }
});

在真实的示例中,可以使用一个服务器端语言(比如 PHP、Python、ASP.NET 或者 Java)、根据通过 Ajax 请求传递给它的参数动态生成 JSON 数据。

二、 使用 Dojo 掌握面向对象开发

1、什么是面向对象开发?
面向对象编程(Object-Oriented Programming,OOP)是一个软件开发范式,它基于称为 “对象” 的数据结构的定义,由数据属性和函数组成。这些属性(成员变量)和函数(或方法)定义软件与那个对象可能进行的交互。OOP 的主要好处是,简化您的代码结构,有助于代码重用和维护。

“面向对象” 基础知识
面向对象编程的基本前提是:在您的软件中创建一些对象,这些对象定义一系列应用于该对象的属性和一系列能够检索或修改该对象的属性的方法或函数。比如car,它有manufacturer、model number、registration number、color、cubic capacity等属性;有 accelerate、brake、change gear、turn、stop 等方法。在 OOP 中,对所有汽车定义通用的基本属性和方法,每辆汽车都将采用那个定义的形式,尽管每辆汽车都有不同的值。本文稍后将介绍,软件开发中有不同的面向对象途径。

2、常用 OOP 术语
下面简要描述讨论面向对象开发时使用的一些常用术语。需要说明的是,并非所有 OOP 类型都包含每个概念;例如,基于原型的对象语言(比如 JavaScript)中就没有 “类”。


在基于类的面向对象开发中,一个类定义组成一个对象的不同属性和函数。类定义用于生成对象的模板,因此它们应该定义这些对象能够遵守的公共属性和动作。类通常由成员变量和方法构成。

成员变量
对象的成员变量就是该对象的属性。在前面提到的汽车示例中,这些属性包括该汽车的 manufacturermodel、color、cubic capacity,等等。

方法
方法是对象能够执行的动作。例如,一辆汽车能够 accelerate、brake、turn,等等。通常,方法将修改成员变量的值。例如,当一个 car 对象使用 accelerate 方法加速时,它的当前速度属性将增加。许多对象都有一个称为 constructor 的方法,该方法在对象创建后立即被调用。

实例或对象
实例或对象即实际对象本身,而不是用于定义对象的模板。例如,有一个名为 myCar 的对象,它拥有一个汽车模板的属性和方法。在一个对象的实例中,属性将实际拥有值。例如,myCar 可能拥有一个值为 silver 的 color 属性,一个值为 2500 的 cubic capacity 属性。一个对象属性的当前值称为该对象的状态,该状态可以在该对象的整个生命周期内变化。

继承性
在基于类的 OOP 中,继承性是这样一个过程:子类继承其父类的成员变量和方法。除了继承这些属性和动作之外,子类可以定义自己的成员变量和方法,并提供父类的属性的默认值。例如,有一个 FourByFour 类,它是 Car 类的一个子类。这个子类可以将其父类的 drivetrain 属性的默认值设置为 4WD(四轮驱动)。另外,它可以定义另一个名为 transfer case 的属性,该属性仅适用 4x4 汽车;并定义一个方法,该方法允许您更改其他普通车辆上没有的低速档(low range gear)。

封装
在基于类的 OOP 中,成员变量通常被定义为私有变量,以免被从类本身的范围外访问或修改。有一些称为 “修改器” 的特殊方法,它们允许定义检索或修改类中的私有成员函数的值的方法。这些方法(通常称为 getters 和 setters)允许程序员使用隐藏的信息,使应用程序和其他类只能访问某些属性。这种技术通常称为 “封装”。

抽象
抽象是通过只定义那些在对象的当前上下文中对该对象很重要的属性和方法来减小对象的复杂性的过程。例如,当定义一个 Car 类时,可以通过定义一辆汽车拥有的、对其他类型的车辆(比如有蓬货车、卡车、摩托车等)也常见的所有属性,来将这个类进一步抽象为一个 Vehicle 类。这样,Car 类将从 Vehicle 类继承这些属性,就像一个 Motorcycle 类或 Van 类那样。

多态性
在 OOP 上下文中,多态性意味着可以从它的超类继承方法,而不必提供所有方法的相同实现。例如,有两个 Car 类的两个子类,一个用于自动挡汽车(我们称其为 ATCar),另一个用于手动挡汽车(我们称其为 MTCar)。所有 Car 对象都能加速,因此 ATCar 和 MTCar 都将从它们的父类继承 accelerate 方法。但是,在一个 ATCar 中,当引擎达到某个 RPM 级别时,您的 accelerate 方法将自动调用 change gear 方法。结果,您在 ATCar 子类中覆盖了 accelerate 方法的父类定义,而在 MTCar 中,该方法就是子类从 Car 类继承而来的方法。

3、面向对象的 JavaScript
上面介绍的许多概念都是称为基于类的面向对象编程的特定 OOP 范式所专用的。但是,并非所有编程语言都遵守这个范式。另一种常见 OOP 类型是基于原型的面向对象编程,JavaScript 语言就使用该范式。

(1)JavaScript 并不只是一种基本脚本语言
当 JavaScript 首次流行时,它主要用作一种在基本 web 页面上执行一些简单技巧的方法。大多数 JavaScript 用户都不是软件开发人员,他们是图形或 web 设计师,他们可能拥有丰富的 HTML 经验,但他们的编程语言知识很少,甚至没有。HTML 本身在支持生成动态效果方面的功能是非常有限的,而这正是 JavaScript 优势所在。然而,大多数设计师并不实际学习如何用 JavaScript 编程,而只是尽量找到它们需要的代码段,仅仅学习调试该代码段以满足自身需要的所需的知识,然后使用那段小代码。当设计师这样做过几次之后,他们就会误以为他们自己的 JavaScript 水平不错了。
在 web 早期,JavaScript 的功能很有限。但是,它现在已经成长为一个成熟的、功能齐全的编程语言,它不再只用于为网站编写一些简单技巧,而是用于驱动整个富互联网应用程序。事实上,作为一种语言,JavaScript 今天还在以各种方式应用。例如,CouchDB 面向文档数据库管理系统就使用 JavaScript 函数来查询数据库中的数据。
许多 Java 开发人员(以及使用其他更传统的编程语言的开发人员)可能会轻视 JavaScript,认为它是一种基本脚本语言。尽管 JavaScript 已经发展得非常强大,认为它只是用于在网站上执行一些小技巧的误解仍然存在。这是因为大多数使用 JavaScript 的 web 开发人员将使用 jQuery、Prototype 或 Dojo 这样的库来避免为它们编写 JavaScript 的工作。事实上,很多 web 开发人员可能被视为编写 jQuery 应用程序的专家,但对于 JavaScript 本身,他们实际上几乎没有什么专长。很多这样的开发人员没有意识到的是,JavaScript 实际上非常强大,且包含开箱即用的面向对象特性。

(2)基于原型的 OOP
JavaScript 实现的面向对象编程类型不同于 Java™ 代码中使用的类型。尽管 Java 编程基于一个基于类的 OOP 模型,但 JavaScript 基于一个不怎么基于类的 OOP 模型,这个模型称为基于原型的对象。对象只在需要的时候才被声明,而不是定义一组用于创建对象的类模板。当一个对象需要从另一个对象继承特性时,它可以简单地克隆一个原型对象的特性。原型 OOP 的一个关键优势是,对象原型可以在运行时修改,这意味着对象结构的定义不是严格的。大多数基于类的 OOP 编程语言不允许类在运行时动态改变(但有几个例外,比如 Perl、Python 和 Ruby)。

(3)纯 JavaScript 中基本的面向对象示例
清单 1. JavaScript 中的一个基本对象原型

function Car() { }
var myCar = new Car();
console.log(myCar);
输出: Car {}

清单 2. 定义您的对象中的一个成员变量

function Car() { }
Car.prototype.current_speed = 0;
var myCar = new Car();
console.log(myCar);
输出: Car { current_speed=0}

清单 3. 向原型添加一个方法

function Car() { }
Car.prototype.current_speed = 0;
Car.prototype.accelerate = function(increment) {
    this.current_speed += increment;
}
var myCar = new Car();
myCar.accelerate(30);
myCar.accelerate(20);
console.log(myCar);
输出: Car { current_speed=50,  accelerate=function()}

清单 4. 添加一个构造器函数体

function Car(reg_no) { 
    this.reg_no = reg_no;
    console.log('Car with registration no. '+this.reg_no+' created.');
}
Car.prototype.reg_no = '';
Car.prototype.current_speed = 0;
Car.prototype.accelerate = function(increment) {
    this.current_speed += increment;
}
var myCar = new Car('10C500');
myCar.accelerate(30);
myCar.accelerate(20);
console.log(myCar.current_speed);
输出:Car with registration no. 10C500 created.
     50

清单 5. 一个更完整的 Car 原型

function Car(reg_no) { 
    this.reg_no = reg_no;
}
Car.prototype.reg_no = '';
Car.prototype.current_speed = 0;
Car.prototype.current_gear = 0;
Car.prototype.accelerate = function(increment) {
    this.current_speed += increment;
}
Car.prototype.decelerate = function(decrement) {
    this.current_speed -= decrement;
}
Car.prototype.increaseGear = function() {
    this.current_gear++;
}
Car.prototype.decreaseGear = function() {
    this.current_gear--;
}

现在我们以此为基础创建一个称为 ATCar 的继承对象原型,该原型描述一辆自动挡汽车。这个示例并不是一辆完美的汽车,因为它基于速度而不是 RPM 进行换挡,但它能够帮助您理解原型 OOP 中的继承性和多态性(见清单 6)。

清单 6. 一个 ATCar 对象原型,继承自 Car

function ATCar(reg_no) {
    Car.call(this, reg_no);
}
ATCar.prototype = new Car();
ATCar.prototype.constructor = ATCar;
ATCar.prototype.accelerate = function(increment) {
    Car.prototype.accelerate.call(this, increment);
    if(increment >= 10) this.increaseGear();
}
ATCar.prototype.decelerate = function(decrement) {
    Car.prototype.decelerate.call(this, decrement);
    if(this.current_speed === 0) this.current_gear = 0;
    else if(decrement >= 10) this.decreaseGear();
}

注意:构造器使用 Car 对象原型上的 call 函数。只要一个 ATCar 被实例化,这通常会调用父原型定义上的构造器。这就是继承性,这样ATCar 原型就不需要了解 Car 对象原型的内部工作原理。把 ATCar 函数的 prototype 属性设置为 Car 函数的一个新实例。本质上,这将告知 JavaScript :想在 ATCar 原型中继承 Car 原型的属性和方法。
最后,我们看看这个示例的应用情况(见清单 7)。

清单 7. 使用自动挡汽车原型

var myCar = new ATCar('10C500');
myCar.accelerate(30);
myCar.accelerate(20);
myCar.decelerate(5);
console.log(myCar);

(4)原型 OOP 的问题
对于基于原型的面向对象编程比基于类的 OOP 好还是差,不同的人有不同的观点。Dojo 提供了一些很棒的特性来模拟基于类的系统,以便您以类似的方式编写 JavaScript 代码。下一节解释如何使用 Dojo 的这些特性在 JavaScript 中创建基于类的应用程序。

4、使用 Dojo 模拟基于类的 OOP
在深入讨论 Dojo 的基于类的模拟之前,注意到目前为止,Dojo 仍然是一个 JavaScript 库。Java 代码和 JavaScript 不是一回事;事实上,它们的差别很大。Dojo 并不试图迫使 JavaScript 像 Java 代码那样操作,相反,它允许 Java(和其他基于类的 OOP 语言)开发人员以一种他们熟悉的方式使用 JavaScript OOP,而底层结构仍然以一种原型方式工作。

使用 dojo.declare 创建类
要使用 Dojo 创建类,可以使用 dojo.declare 函数。现在,我们使用这个函数来创建一个 Car 类(见清单 8)。

清单 8. 使用 dojo.declare 创建一个 Car 类

dojo.declare("Car", null, {
});
var myCar = new Car();
console.log(myCar);

这是创建一个类并实例化该类的一个对象的基本 shell。dojo.declare 函数接受 3 个参数:
类名
类继承的超类
包含该类的所有属性和方法的一个对象
在清单 8 中的示例中,您声明了一个名为 Car 的类,它不从任何超类继承,也没有任何成员变量和方法。

清单 9. 一个更完整的 Car 类

dojo.declare("Car", null, {
    reg_no: "",
    current_speed: 0,
    current_gear: 0,
    constructor: function(reg_no) {
        this.reg_no = reg_no;
    },
    accelerate: function(increment) {
        this.current_speed += increment;
    },
    decelerate: function(decrement) {
        this.current_speed -= decrement;
    },
    increaseGear: function() {
        this.current_gear++;
    },
    decreaseGear: function() {
        this.current_gear--;
    }
});

如您所见,与 vanilla JavaScript 声明对象原型的方法相比,这种类声明方式更容易。在继续讨论继承性之前,我们先检查一下对象是否可以被实例化,以及那些方法是否有效(见清单 10)。
清单 10. 使用 Car 类

var myCar = new Car("10C500");
myCar.accelerate(30);
myCar.accelerate(20);
myCar.decelerate(5);
console.log(myCar.reg_no+" travelling at "+myCar.current_speed+" mph");

5、继承性和多继承性
清单 11. 使用 Dojo 创建 ATCar 子类

dojo.declare("ATCar", Car, {
    accelerate: function(increment) {
        this.inherited(arguments);
        if(increment >= 10) this.increaseGear();
    },
    decelerate: function(decrement) {
        this.inherited(arguments);
        if(decrement >= 10) this.decreaseGear();
    }
});

清单 12. 运行中的新子类

var myCar = new ATCar("10C500");
myCar.accelerate(30);
myCar.accelerate(20);
myCar.decelerate(5);
console.log(myCar.reg_no+" travelling at "+myCar.current_speed+" 
   mph in gear "+myCar.current_gear);
这将生成以下输出:10C500 travelling at 45 mph in gear 2

Dojo 还支持多继承性。多继承性允许一个子类从多个父类派生,从每个父类继承属性和方法。严格说来,只有一个父类被认为是超类(数组中的第一个),但每个父类的构造器都将被调用,调用顺序与这些父类在数组中的顺序一致。

为演示多继承性,我们以一个 Smartphone 为例,除了接打电话和收发文本消息外,它还有很多功能(见清单 13)。通常,它应该还有播放音乐、观看视频等功能。为简单起见,我们假设一个 Phone 能打电话,一个 MediaPlayer 能播放视频,而一个 Smartphone 具有上述两个功能。
清单 13. Dojo 中的多继承性

dojo.declare("Phone", null, {
    phone_number: "",
    minutes_remaining: 0,
    constructor: function(properties) {
        this.phone_number = properties.phone_number;
        this.minutes_remaining = properties.minutes_remaining;
        console.log("Phone "+this.phone_number+" powered on. You have 
"+this.minutes_remaining+" minute(s) remaining.");
    }
});
dojo.declare("MediaPlayer", null, {
    disk_space: 0,
    songs:[],
    constructor: function(properties) {
        this.disk_space = properties.disk_space;
        this.songs = properties.songs;
        console.log("Media Player powered on. You have "+this.songs.length+" songs,
with "+this.disk_space+" GB free space left.");
    }
});
dojo.declare("Smartphone", [Phone, MediaPlayer], {
    phone_id: "",
    constructor: function(properties) {
        this.phone_id = properties.phone_id;
        console.log("Smartphone ID "+this.phone_id+" boot up complete.");
    }
});
var songs = [
    {artist:"U2",title:"Vertigo"},
    {artist:"Coldplay",title:"Yellow"}
];
var myPhone = new Smartphone({
    phone_number:"(555) 123-4567", 
    minutes_remaining: 60, 
    disk_space: 2.5, 
    songs: songs,
    phone_id: "4345FDFD7JAPO76"
});
console.log(myPhone);

这里值得指出的一点是:dojo.declare 是如何实现多继承性的。一组类被传递,而不只是将父类作为第二个参数传递。这些父类的构造器将以它们在数组中的顺序自动被调用。如果每个父类构造器都接受不同的参数,那么 Dojo 将不能区分应该传递给每个构造器函数的参数。因此,如果需要将不同的参数传递给不同的构造器,应该在简单 JavaScript 中以 “键/值” 对的形式添加参数并在构造器中以那种方式使用它们。

使用 dojo.mixin 来改进多继承性示例
Dojo 提供了一个不错的工具函数 dojo.mixin,它允许您通过从左到右合并对象属性来混合对象(见清单 14)。
清单 14. 一个基本 dojo.mixin 示例

var objA = { a: 1, b: 2 };
var objB = { b: 3, c: 4 };
dojo.mixin(objA, objB);
console.log(objA);

两列:第一列带有 abc,第二列带有 1 3 4
最初在 objA 中被设置为 2 的 b 属性已经被来自 objB 的值 3 所覆盖。而且,c 属性已经被添加。这个基本示例完成后,我们来看看如何在您的多继承性示例中使用 dojo.mixin。

Dojo 中的打包和模块化开发
开发大型应用程序时,可能需要使用带有许多成员变量和方法的类。如果是Java 开发环境:不同的类应该驻留在不同的文件中,按照包进行分组。然后,当继承或其他目的需要时,再 “导入” 类,以确保它们仅在必要时才被加载。使用 JavaScript 时,没有这样的开箱即用打包和模块系统,但幸运的是,Dojo 提供了一个解决方案。

例如,在 Car 类示例中,使用 Java 代码时您可能将该类存储在一个包中,如清单 15 所示。
清单 15. 在 Java 编程中打包类

package com.ibm.developerworks.dojoseries;
public class Car {
        //Car class code goes here
}

稍后可以将这个类导入其他 Java 类中,如清单 16 所示。
清单 16. 在 Java 编程中导入类

package com.ibm.developerworks.dojoseries;
import com.ibm.developerworks.dojoseries.Car;
public class ATCar extends Car {
        //ATCar class code goes here
}

Dojo 通过 dojo.provide 和 dojo.require 函数提供了一个类似的打包系统。我们看看清单 16 中的 Java 代码在 Dojo 中是什么样子。首先,我们看看清单 17 中的 Car 类。
清单 17. 在 Dojo 中打包类

dojo.provide("com.ibm.developerworks.dojoseries.Car");
dojo.declare("com.ibm.developerworks.dojoseries.Car", null, {
        //Car class code goes here
});

您可能已经注意到,这与 Java 代码非常相似,尽管类的整个包路径在 dojo.provide 语句中提供,而不只是到包含包的路径。包路径很重要,因为它还决定当 Dojo 试图使用 dojo.require 加载这个类时在哪里寻找它。因此,对于清单 17 中的示例,Car.js 文件应该存储在相对路径 com/ibm/developerworks/dojoseries/Car.js 中。如果它没有存储在那个位置,那么 Dojo 在需要它时就不能正确加载它。下面,我们看看如何导入这个类并从它创建一个子类(见清单 18)。
清单 18. 在 Dojo 中导入类

dojo.provide("com.ibm.developerworks.dojoseries.ATCar");
dojo.require("com.ibm.developerworks.dojoseries.Car");
dojo.declare("com.ibm.developerworks.dojoseries.ATCar", 
   com.ibm.developerworks.dojoseries.Car, {
        //ATCar class code goes here
});

注意到,这里再次使用 dojo.provide 语句来确定这个类的加载路径。这个特殊的类将被存储在相对路径 com/ibm/develoeprworks/dojoseries/ATCar.js 中。然后使用 dojo.require 来加载 Car 类 — 使用它的完整包路径。最后,声明这个子类,将完整路径作为第二个参数传递给其父类。由于这个类现在还没有被 Dojo 加载,因此它在 DOM 中可用且可以通过它的名称直接加载,不需要被放置到一个字符串中。
尽管从技术上讲类名可以与 dojo.provide 语句中提供的路径不同(注意,使用 dojo.require 进行的任何类加载必须使用 dojo.provide 中设置的完全限定路径),但我们强烈建议不要这样做,因为这样只会导致混乱。

6、全部打包 — 使用 Dojo 的构建系统
使用 Dojo 的对象方向特性时,很有可能需要将类分隔到一些不同的文件中,以便使您的代码组织和管理任务变得更轻松。但是,重要的是要注意,加载许多 JavaScript 小文件可能会对您的应用程序造成严重的性能影响。每当一个 web 浏览器需要下载和执行一个 JavaScript 文件时,它都必须发送一个单独的 HTTP 请求,等待服务器做出响应,然后处理响应。因此,加载一个大文件通常比加载许多小文件更快。
这正是 Dojo 的构建系统发挥作用的地方,它允许定义一些层,每一层都合并来自几个 JavaScript 源文件的源代码并缩小生成的单个文件, 从而保持文件大小最小。这个缩小过程能够极大地缩小您的代码,方法有二:一是移除所有不必要的空白和注释;二是将本地变量重命名为更短的名称并尽可能重构它们在函数中的使用。通过使用这个构建系统,可以使您的源代码组织良好便于开发,但是在部署到生产环境时要确保它以最优的性能水平运行。

三、利用 Dijit 组件框架打造丰富的用户界面

1、Dijit 是什么?
Dijit 是 Dojo 工具包的富组件用户界面库。这些组件完全可以主题化,并且可以通过两种方式声明:可以使用 HTML 风格的标签以声明式方法声明,也可利用 JavaScript 以编程方式声明。这一部分将简要介绍 Dijit,介绍它提供的组件,并描述开箱即用的各种主题。

2、丰富的用户界面组件
Dijit 的主要特性就是它提供的丰富的用户界面组件套件。大多数 Web 应用程序开发人员都熟知基本 HTML 元素和表单元素的局限性,以及利用

等元素和 CSS 规则对应用程序进行布局的难度。这正是 Dijit 能够发挥其作用的场所。Dijit 提供了一系列在 HTML 中不可用的组件(当然,在无需您自行编写的情况下),其中包括:
日历
调色板
对话框(模态(modal)或非模态)
富文本编辑器
内嵌编辑器
菜单栏和上下文菜单
进度条
折叠菜单和面板
工具栏
工具提示

除了上述列表之外,DojoX 扩展库还提供了一系列额外的组件,包括网格、图表、视频播放器、灯箱效果等。
除了 UI 组件之外,Dijit 还提供了一系列经过改进的表单字段,这提供了比常规 HTML 表单元素更多的灵活性和功能。这些表单控件包括:
特定数据格式的各种文本框控件,包括币种、日期、数字和时间
数字微调钮
自动展开的文本区域
自动完成的组合框
多选列表框
带有下拉菜单的按钮
切换按钮(切换开关)
滑块
Dijit 还包括多种布局组件,使您能够轻而易举地组织 Web 应用程序的布局。您不必再为表格或 CSS 浮动内容而烦恼,Dijit 允许您为应用程序布局定义复杂的结构。Dijit 提供的布局组件包括:
折叠容器
堆叠容器
边框容器
选项卡容器
内容窗格
如您所见,Dijit 拥有丰富的用户界面组件,如果开发人员希望能够自行编写这类组件,则需要花费漫长的时间。利用 Dijit,您就可以缩短应用程序的开发时间,因为不必再为设计或开发复杂的用户界面组件而烦恼。

3、主题
上一节中介绍了 Dijit 用户界面组件的部分示例,所有这些示例均使用了 Dijit 附带的 “Claro” 主题。Dijit 还开箱即用地提供了其他几种主题,使您能够更好地将 Dijit 组件的样式与应用程序的风格相匹配。有Tundra、Soria 和 ihilo 主题。
如果 Dijit 包含的主题不适合您的应用程序,那么您可以轻松定义自己的主题,并自定义所有 Dijit 组件,以便确切地满足您的需求。

4、初识 Dijit
要创建 Dijit 应用程序,则需要包含 Dojo 库本身、正在使用的主题的 CSS 文件,以及对您的 HTML 文档的 body 元素中的主题选择的引用。随后,还要使用 dojo.require 函数加载要包含在应用程序之中的 Dijit 组件。
首先从用于使用 Claro 主题的 Dijit 应用程序的一个基本模板开始。

清单 1. 基本 Dijit 模板

<!doctype html>
<html lang="en" dir="ltr">
    <head>
        <title>Dijit Template</title>
        <link rel="stylesheet" href="https://ajax.googleapis.com/ajax/libs
/dojo/1.5/dijit/themes/claro/claro.css" />
        <style type="text/css">
            body, html { font-family:helvetica,arial,sans-serif; font-size:90%; }
        </style>
    </head>
    <body class="claro">        
        <script src="https://ajax.googleapis.com/ajax/libs/dojo/1.5/dojo/dojo.xd.js"
djConfig="parseOnLoad: true"></script>
        <script>
            dojo.require("dijit.dijit");
            //Add Dijit components you are using here using dojo.require

            dojo.addOnLoad(function() {                
                //JavaScript content here
            });
        </script>
    </body>
</html>

在清单 1 中,这里加载了来自 Google CDN 的 claro.css 样式表。此外,为 元素提供一个与正在使用的主题名称相匹配的类名称。本例使用 Claro 主题,因此提供了类名称 claro。您可能会认为,要包含 Dijit,除了基本 Dojo 工具包之外,您还需要包含一系列的 JavaScript 文件。事实上并非如此;您可以使用 Dojo 的加载系统,使用 dojo.require 动态加载您需要的 Dijit 框架元素。然而,我添加的一项内容就是加载 Dojo 的

<body> 第一行添加<div dojoType="dijit._Calendar"></div>

接下来,在 dojo.require(“dijit.dijit”); 代码行下,添加以下代码行:
dojo.require(“dijit._Calendar”);。打开浏览器即可看到效果。
通过这种方式声明 Dijit 组件目前是将 Dojo 添加到应用程序之中的最便捷的方法。如果适用,您可以在其他 widget 内添加 widget(举例来说,您可以在一个 TitlePane 内添加一个调色板),甚至可以轻而易举地将事件处理程序连接到您的组件。

首先,让我们在一个 widget 内嵌套另一个 widget。使用清单 2 所示的代码片段取代 HTML 部分中的日历行。
清单 2. 在一个标题窗格内添加调色板的代码片段

<div dojoType="dijit.TitlePane" title="Color Picker">
    <div dojoType="dijit.ColorPalette"></div>
</div>

接下来,使用清单 3 中的代码取代日历的 dojo.require 调用。
清单 3. 更换日历的 dojo.require 调用

dojo.require("dijit.TitlePane"); dojo.require("dijit.ColorPalette");

请保存文件,重载 Web 浏览器。
您可以看到,调色板出现在标题窗格组件中,如果您折叠标题窗格,则不会显示调色板。

现在,让我们通过两种方法为调色板添加一个事件处理程序。首先,使用内置的 onClick 属性,在用户选择一种颜色时显示包含选定值的警告窗口。为此,请将您的 dojo.ColorPalette 对象的 HTML 元素更改为:

<div dojoType="dijit.ColorPalette" onChange="alert(this.value);"></div>

请保存文件,并将它载入浏览器,随后尝试单击一种颜色。此时应该看到一个警告窗口,其中显示了您选定的颜色的十六进制值。内嵌的事件处理程序非常适合单行操作,但如果您希望执行一些更为复杂的操作,那么这种方法就不太适用。幸运的是,您可以声明 dojo/method

<div dojoType="dijit.ColorPalette">
    <script type="dojo/method" event="onChange" args="evt">
         alert(this.value);
    </script>
</div>

请保存文件,重载浏览器,您将看到它执行了相同的操作。然而,这一次,您在 Dijit 组件的 HTML 元素内添加了一个 script 块。这里并没有采用传统的 “text/Javascript” 类型值,而是为其提供了一个 dojo/method 值,告知 Dojo 解析器来解析这个代码块。它利用事件属性,定义应将此代码绑定到哪个事件,并使用 args 属性将所有参数传递给函数。
声明式语法是我个人最喜爱的 Dojo 特性之一,它使得搭建相对复杂的应用程序设计变得极为简单。此外,它还为不熟悉 JavaScript 的人员提供了一种开发复杂 Web 应用程序的方法。

6、利用 JavaScript 处理 Dijit
尽管使用编程的方法不像通过声明式方法使用 Dijit 一样简单,但通过编程式方法添加组件也并不是很难。首先,让我们创建一个基本日历示例,就像上一节所创建的那个示例一样。从基本 Dijit 模板入手(清单 1),在body第一行加入:

<div id="myCalendar"></div>。

接下来,在 dojo.require(“dijit.dijit”); 代码行下,添加以下代码:
dojo.require(“dijit.Calendar”);。
最后,在 dojo.addOnLoad 功能模块内,添加以下代码行:var calendar = new dijit.Calendar({}, “myCalendar”);。
请保存文件,并将它载入浏览器。看到与之前相同的日历。在本例中,您为 HTML 代码添加了一个基本占位符元素,其 ID 为 myCalendar。随后,您使用表达式 new dijit.Calendar 以编程的方式创建了日历。此函数的第一个参数是一个配置对象(本例中为空)。第二个参数是应将组件绑定到的 HTML 元素的 ID,在本例中是 myCalendar。

嵌套组件又如何?让我们继续我们的工作,编写在标题窗格内有一个调色板的示例代码。首先,HTML 部分应包含以下内容:

<div id="myTitlePane"></div>。

接下来,您需要清单 5 中的以下 dojo.require 语句(当然,也需要 dijit.dijit)。
清单 5. dojo.require 语句
dojo.require(“dijit.TitlePane”);
dojo.require(“dijit.ColorPalette”);

最后,应将清单 6 中的代码置于 dojo.addOnLoad 块中。
清单 6. 以编程的方式创建标题窗格和调色板

var colorPalette = new dijit.ColorPalette({});
var titlePane = new dijit.TitlePane({
    content: colorPalette,
    title: "Color Picker"
}, "myTitlePane");

请保存文件并将它载入浏览器,您将得到与之前一样的调色板。在观察编程式 Dijit 组件内的事件处理之前,让我们先来看看编写上述代码的替代方法。我们使用了清单 7 中所示的代码来取代 HTML 块。
清单 7. 使用嵌套式 HTML 元素来设定组件布局

<div id="myTitlePane">
    <div id="myColorPalette"></div>
</div>

dojo.addOnLoad 块应包含清单 8 所示的代码。
清单 8. 仅使用 ID 绑定嵌套式组件

var colorPalette = new dijit.ColorPalette({}, "myColorPalette");
var titlePane = new dijit.TitlePane({
    title: "Color Picker"
}, "myTitlePane");

在这个版本中,您使用所绑定的 HTML 元素添加了标题窗格内的调色板。在 JavaScript 中,随后会使用 ID 绑定组件,这意味着不必使用内容属性将调色板添加到标题窗格之中。我倾向于使用这种方法,因为 HTML 结构为页面提供了更多语义含义,使您能够更加轻松地通过 HTML 语法观察页面布局。
最后,让我们来看看如何为通过编程的方式声明的 Dijit 组件添加事件处理。使用清单 9 中的代码取代 var colorPalette = new dijit.ColorPalette({}, “myColorPalette”); 代码行。
清单 9. 为以编程的方式创建的组件添加事件处理程序

var colorPalette = new dijit.ColorPalette({
    onChange: function(evt) {
        alert(this.value);
    }
}, "myColorPalette");

请保存文件,重载浏览器,并单击一种颜色,您会瞬间看到所选定颜色的十六进制值。此外,也可以将代码行保持原样,并在其下方添加如清单 10 所示的代码。
清单 10. 使用 dojo.connect 添加事件处理程序

dojo.connect(colorPalette, "onChange", null, function(evt) {
    alert(this.value);
});

有必要指出的是,在 Dijit 组件中,事件的名称是区分大小写的。Dojo 对常规 DOM 对象中的事件完全使用小写字母,对 Dijit 组件中的事件使用驼峰式大小写(驼峰式大小写即第一个字母采用小写,其他任何后接的词均首字母大写),因此在处理 Dijit 组件时,请务必使用后一种方法。

7、使用 dijit.byId
dojo.byId 函数,它能获取对一个 DOM 元素的引用。类似地,Dojo 提供了一个 dijit.byId 函数,允许您获得 Dijit 组件的一个句柄。如果您正在以声明的方式创建组件,并且希望在 JavaScript 代码中与其交互,那么这种方式尤为有用。让我们来看一个例子。
以基本 Dijit 模板(清单 1)为基础,在 HTML 部分中添加以下代码:

<button id="myButton" dojoType="dijit.form.Button">Push Me</button>

接下来,在您的 dojo.require 部分中,请添加如清单 11 所示的代码行。
清单 11. 使用 dojo-require

dojo.require("dijit.form.Button");
dojo.require("dijit.Dialog");

最后,让我们使用 dijit.byId 获得按钮的一个引用,并在用户单击按钮时显示一个对话框。将清单 12 中的代码添加到 dojo.addOnLoad 函数块中。
清单 12. 使用 dijit.byId 连接到一个按钮

var button = dijit.byId("myButton");
dojo.connect(button, "onClick", null, function(evt) {
    var dialog = new dijit.Dialog({
        content: "You clicked the button!",
        title: "Message"
    }).show();
});

请保存文件,并在您的浏览器中打开它。然后单击按钮,您会看到一个对话框。

8、使用 Dijit 的布局特性
除了一些非常有用的组件和表单控件之外,Dijit 还提供了一个出色的布局框架,使您能够更加轻松地组织应用程序的界面。这一节将介绍如何使用 Dijit 的各种布局组件。为了简便起见,所有示例均使用 Dojo 的声明式语法提供,但是,如有必要,你可以将其转为编程式方法。

(1)内容窗格(panes)
内容窗格是最基本的 Dijit 布局组件。它们允许您定义一个内容部分,通过直接将 HTML 代码提供给内容属性来载入这一部分,或者通过 XmlHttpRequest 调用异步载入它。就后者而言,在从服务器载入内容时,内容窗格会显示一条正在加载的消息。
让我们来看一个非常基础的内容窗格实例。首先,创建一个新文件,将其命名为
content.html,然后为其添加以下代码:

<h1>content.html content here!</h1>

现在,在基本 Dijit 模板中(如 清单 1 所示),将清单 13 中的代码添加到模板的 HTML 部分。
清单 13. 使用 Dijit 内容窗格

<button dojoType="dijit.form.Button" id="myButton">
    Load content using XHR
</button>
<div dojoType="dijit.layout.ContentPane" id="myContentPane">
    <h1>Static content here!</h1>
</div>

接下来,将清单 14 中的代码添加到 dojo.require 部分
清单 14. 将内容窗格添加到 dojo.require 部分

dojo.require("dijit.form.Button");
dojo.require("dijit.layout.ContentPane");

最后,连接按钮和内容窗格,将 content.html 载入内容窗格。在 dojo.addOnLoad 内添加清单 15 的代码。
清单 15. 将按钮连接到内容窗格

var button = dijit.byId("myButton");
var contentPane = dijit.byId("myContentPane");
dojo.connect(button, "onClick", null, function(evt) {
    contentPane.attr("href","content.html");
});

请保存文件,并将它载入您的 Web 浏览器。您会看到通过 XHR 加载之前,包含使用 XHR 加载内容按钮和 “Static content here” 这句话的窗口。
现在,请单击按钮,消息将会发生变化。
通过 XHR 加载之后,包含使用 XHR 加载内容按钮和 “content.html content here” 这句话的窗口。
不可否认,这个示例非常简单,但它应该能够展示将动态内容载入一个 Dijit ContentPane 组件是多么轻松。Dialog 等其他 Dijit 组件中实际上也使用了这个组件,用它来呈现内容部分。这些组件往往还用于布局容器之中,例如堆叠容器和选项卡容器,稍后我们将介绍相关内容。

(2)堆叠容器
堆叠容器就是允许使用包含多个不同的子容器的容器,但每次仅有一个子容器可见(有时称之为卡片式布局)。如果您正在创建分步向导,那么这种布局组件尤为有用。让我们来看看如何创建 Dijit 堆叠容器,并通过一个堆叠控制器在堆叠的控制器之间进行导航。
首先,通过基本 Dijit 模板(清单 1)创建一个新文件。在 HTML 代码部分中,添加清单 16 所示的代码。
清单 16. 创建一个堆叠容器和控制器

<div dojoType="dijit.layout.StackContainer" id="stackContainer">
    <div dojoType="dijit.layout.ContentPane" title="Stack 1">
        This is the content in stack 1.
    </div>
    <div dojoType="dijit.layout.ContentPane" title="Stack 2">
        This is the content in stack 2.
    </div>
</div>
<div dojoType="dijit.layout.StackController" 
containerId="stackContainer"></div>

在保存该文件之前,请务必确保它包含所需的必要 dojo.require 调用(请参见清单 17)。
清单 17. 向 dojo.require 添加堆叠容器和控制器

dojo.require("dijit.layout.StackContainer");
dojo.require("dijit.layout.StackController");
dojo.require("dijit.layout.ContentPane");

请保存文件,并将它载入您的浏览器。单击控制器按钮会允许您更改当前选定的堆叠。所有这一切均无需使用 JavaScript(当然,dojo.require 调用除外)。

(3)选项卡容器
与堆叠容器相似,选项卡容器允许您拥有多个视图,而任意给定时间仅有一个视图可见。然而,与堆叠容器不同的是,选项卡容器提供了一些选项,这些选项以显示在内容上方、下方或旁边的选项卡的形式出现,可在各视图之间切换。除此之外,它们不需要任何独立控件,因为它们直接构建在组件本身之内。与前面一样,您可以将清单 18 所示的代码添加到代码的 HTML 部分中。
清单 18. 创建一个选项卡容器

<div style="width: 535px; height: 290px">
    <div dojoType="dijit.layout.TabContainer" style="width: 100%; height: 100%;">
        <div dojoType="dijit.layout.ContentPane" title="Tab 1">
            This is the content in tab 1.
        </div>
        <div dojoType="dijit.layout.ContentPane" title="Tab 2">
            This is the content in tab 2.
        </div>
    </div>
</div>

在本例中,选项卡容器包含于一个外部

容器之中,该容器定义了可以包含选项卡容器的区域。当然,不要忘记将相关的代码行添加到您的 dojo.require 块中(请参见清单 19)。
清单 19. 将选项卡容器添加到 dojo.require

dojo.require("dijit.layout.TabContainer");
dojo.require("dijit.layout.ContentPane");

请保存文件,并将它载入您的浏览器。您可以单击相关选项卡,在不同视图之间进行切换。

(4)折叠容器
另外一种允许您每次显示一个部分的容器就是折叠容器。这种容器通常采用垂直展开/折叠的布局形式,每次仅可打开一个部分,该部分展开时将填充折叠容器的整个空间。要了解这种容器,最好的方法就是观察示例,因此下面我们将给出一个示例。
首先,将清单 20 所示的代码添加到您的模板的 HTML 部分中。
清单 20. 创建一个折叠容器

<div style="width: 535px; height: 290px">
    <div dojoType="dijit.layout.AccordionContainer" style="width: 100%; 
height: 100%;">
         <div dojoType="dijit.layout.ContentPane" title="Blade 1">
             This is the content in blade 1.
         </div>
         <div dojoType="dijit.layout.ContentPane" title="Blade 2">
             This is the content in blade 2.
         </div>
        <div dojoType="dijit.layout.ContentPane" title="Blade 3">
            This is the content in blade 3.
        </div>
    </div>
</div>

请不要忘记您的 dojo.require 引用(请参见清单 21)。
清单 21. 将折叠容器添加到 dojo.require

dojo.require("dijit.layout.AccordionContainer");
dojo.require("dijit.layout.ContentPane");

在您的浏览器中打开折叠容器,3 个水平选项卡,第一个选项卡处于展开状态,填满了整个窗口,另外两个选项卡处于折叠状态。
要在不同的视图之间进行切换,只需单击相关部分(我更喜欢称之为 blade)的标题即可。

(5)边框容器布局
最后,我们来看看边框容器。如果您过去使用过用户接口库,例如 Swing,那么您可能会熟知边框容器的概念,它允许您将组件放置在四个区域中:东、南、西、北。Dojo 中提供了同样的概念,但能提供分隔线,因此更为强大。同样,最好通过示例来进行说明,因此我们来构建一个示例(如清单 22 所示)。
清单 22. 创建一个边框容器

<div style="width: 535px; height: 290px">
    <div dojoType="dijit.layout.BorderContainer" style="width: 100%; 
height: 100%;">
        <div dojoType="dijit.layout.ContentPane" region="top" splitter="true">
            This is the content in the top section.
        </div>
        <div dojoType="dijit.layout.ContentPane" region="left" style="width: 100px;" 
splitter="true">
            This is the content in the left section.
        </div>
        <div dojoType="dijit.layout.ContentPane" region="center" splitter="true">
            This is the content in the center section.
        </div>
        <div dojoType="dijit.layout.ContentPane" region="right" style="width: 100px;" 
splitter="true">
            This is the content in the right section.
        </div>
        <div dojoType="dijit.layout.ContentPane" region="bottom" splitter="true">
             This is the content in the bottom section.
         </div>
     </div>
</div>

清单 22 创建了一个拥有顶端部分和底端部分的布局,左、右和中心部分位于其间。在尝试之前,请将以下代码(如清单 23 所示)添加到您的 dojo.require 部分中。
清单 23. 将边框容器添加到 dojo.require

dojo.require("dijit.layout.BorderContainer");
dojo.require("dijit.layout.ContentPane");

启动您的浏览器,打开刚刚保存的文件。看到包含顶端部分、底端部分以及两者之间的另外 3 个部分的窗口。
这不仅为应用程序创建了一种结构良好的布局,而且能够定制布局,用户可以拖动各窗格的中心手柄,通过拖动来调整应用程序特定部分的大小。对于一个甚至都没有使用 JavaScript 的应用程序来说(除了 dojo.require 调用之外),这令人印象非常深刻。
正如您在这一节中所看到的那样,Dijit 使得为应用程序创建高级布局变得极为轻松,完全不需要复杂的 JavaScript 代码。本节中使用的所有布局组件均可通过编程的方式创建。

9、DojoX:Dojo 的绑定扩展库
除了 Dojo Base、Dojo Core 和 Dijit 库之外,Dojo 还提供了对于 DojoX 的支持,这是一组并未纳入框架其他部分的实验性和补充性的组件和特性。未纳入框架其他部分的原因各式各样,可能是代码尚未准备好投入生产,可能是由于过大而不适于包含在默认部分中,也可能仅仅是因为扩展并未得到广泛利用,或者不像 Dojo 工具包主要部分中包含的特性那样存在普遍的需求。因此,在使用 DojoX 组件时,您应该倍加谨慎,部分此类组件可能未经过全面的测试或者支持可访问性或国际化的认证。
DojoX 包括多种附加函数。下面的列表给出了 Dojox 提供的部分组件示例:
音频/视频回放
图表绘制
Comet 客户端
适合多种不同数据源的广泛的数据存储,包括 CouchDB 数据库、CSV 文件,以及 Flickr、Google Search、Amazon S3 等 Web API
矢量绘制 API
附加的表单 widget
数据网格
图像 widget(包括图片库、幻灯片放映和灯箱)
JSON 查询
更多布局容器
RPC 支持
多种其他 widget,包括仪表、其他日历和选色器、鱼眼 widget、文件选取器、横幅广告交替组件等
在您的应用程序中包含 DojoX 扩展是非常简单的。其他 JavaScript 库需要您浏览存储库、下载额外的文件、将其上传至服务器、在您的代码中引用这些文件,而 DojoX 截然不同,其工作方式与 Dijit 组件大体相同。最简单的说明方法就是给出示例,因此我们将创建一个新页面,使用 DojoX 的评分表单 widget。

首先,我们将使用 清单 1 中的基本 Dijit 模板创建一个新页面。在顶端包含 claro.css 文件的代码行下方,添加第二个 标签,加载评分 widget CSS <linkrel="stylesheet" href="https://ajax.googleapis.com/ajax/libs/dojo/1.5/dojox/form/resources/Rating.css" />。在代码的 HTML 部分中,添加以下代码行,添加一个有五颗星可供选择的评分控件,默认选择三颗星:

<div dojoType="dojox.form.Rating" numstars="5" value="3"></div>。

最后,使用前述 dojo.require 载入 Dijit 组件时,添加以下代码行:

dojo.require("dojox.form.Rating");。

到这里,我们的工作就完成了。实际上,这就是在您的应用程序中使用 DojoX 组件所需的全部工作。当然,部分较为复杂的 Widget(例如网格等)可能需要比这种基本组件更多的工作,但所需的基本步骤都是相同的。最终结果包含五颗星的窗口,其中三颗星显示为黄色。

相关学习:
web前端之dojo实际应用及开发六:页面布局(附有源码)
web前端之dojo实际应用及开发五:丰富的用户界面(附有源码)
web前端之dojo实际应用及开发四:面向对象开发[关键](附有源码)
web前端之dojo实际应用及开发三:dojo.xhr* 增强 Ajax(附有源码)
web前端之dojo实际应用及开发二:事件处理(附有源码)
web前端之dojo实际应用及开发一:开始dojo(附有源码)
web前端之Dojo与jQuery综合比较分析
web前端之dojo(用javascript语言实现的开源DHTML工具包)

原文链接

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

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值