UML软件工程组织

 

 

Ajax 和 XML: 五个很酷的 Ajax 小部件
 
Jack D Herrington (jherr@pobox.com), 高级软件工程师, Leverage Software Inc. 出处: IBM
 

本文内容包括:

随着 Web 2.0 浪潮的到来,用户体验得到了全新的关注。用户体验的一部分就是以新颖的方式与用户交互以及为用户提供信息。这些新的界面常常被称作小部件,它们使用 Asynchronous JavaScript + XML (Ajax) 与服务器通信。本文介绍了五个可用于增强站点交互性的小部件。

 Web 2.0 强调以独特、新颖的方式与网站的客户交互。其中很多创新技术都使用图形和小部件,它们与服务器进行通信,获取用于显示的数据。在本文中,我将介绍五个这样的小部件 —— 有些是开源的,有些是需要许可的 —— 它们通过 Ajax 和 XML 与服务器通信。

  • carousel: 这个小部件是一个滚动的图像浏览器,客户可以通过滚动查看一系列的项目,每个项目用一个小图形表示。当用户单击一个项目时,进行什么处理可以由您来决定。carousel 在实际情况中的例子有 Flikr 站点和 Apple 的 iTunes 界面。carousel 是免费提供的,它基于流行的 jQuery JavaScript 框架。
  • SWF/Charts:这种基于 Adobe Flash 的控件从服务器上的 XML 中读取图表数据和样式选项,然后根据数据显示一个图表。它的界面非常优雅,由于很容易创建 XML 数据,所以很容易将动态图形添加到页面中。
  • SWF/Gauge: 与 SWF/Charts 类似,这个 Flash 小部件使用服务器上的 XML 来构建一个完全可定制的仪表盘显示屏。其外观可以仿制飞机或汽车上的仪表盘,或者更流行的样式。这可完全由您选择。
  • 就地编辑: 严格来说它不能算是个小部件,而是从用户那里获得信息的一种直观的、交互式的、轻量级的方式。这种功能是 Scriptaculous 框架附带的,位于 prototype.js 库之上。
  • DHTML windows: DHTML window 为在页面内容上放置无模式的悬浮窗提供了一种机制。用户可以移动窗口,调整它的大小,或者使之消失。窗口的内容可以由页面上的 JavaScript 指定,也可以通过 Ajax 从服务器上读取。这种类型的窗口非常适合用作一种报警机制,也很适合用于弹出小的窗体,从而避免重新装载整个页面。

我将首先展示 SWF/Charts 小部件,因为我认为它是最容易部署的小部件之一。相对于所花费的精力,它的回报也是最大的。

SWF/Charts 小部件

俗话说:“一画抵千言”。这句话很难反驳,尤其是在谈论图形的时候。然而一直以来,在 Web 上画图并非易事。虽然有些 Web 框架包括了一些用于构建图像的基本图形,但大多数 Web 框架都缺少即开即用的画图工具。这种功能的缺失使您必须自己来构造图形。

如果有一个小部件能将 XML 编码的数据画出来,岂不是很好?事实上就有这么一个小部件:SWF/Charts。为了开始使用这个小部件,我从网站下载了 SWF 文件,另外还下载了这个小部件所使用的其他一些 SWF 文件。然后,我将这些文件安装在我的站点上,并在 HTML 上添加了一个到 SWF 小部件的链接,如 清单 1 所示。

清单 1. Chart_page.html

<html><body>

<object
classid="clsid:D27CDB6E-AE6D-11cf-96B8-444553540000"
codebase="http://download.macromedia.com/pub.../swflash.cab#version=6,0,0,0"
width="400" height="250">
<param name="movie"
value="charts.swf?xml_source=chart_data.xml&library_path=charts_library">

<embed
src="charts.swf?xml_source=chart_data.xml&library_path=charts_library"
width="400" height="250"
type="application/x-shockwave-flash"
pluginspace="http://www.macromedia.com/go/getflashplayer">
</embed>
</object>

</body></html>

Charts.swf 有两个参数:一个是其库目录的位置,还有一个是 XML 数据的 URL。XML 数据格式相当简单。清单 2 显示了一个简单的例子。

清单 2. Chart_data.xml

<chart>
<chart_type>bar</chart_type>
<chart_data>
<row>
<null/>
<string>2005</string>
<string>2006</string>
</row>
<row>
<string>Projected</string>
<number>500</number>
<number>700</number>
</row>
<row>
<string>Actual</string>
<number>600</number>
<number>900</number>
</row>
</chart_data>
</chart>

这个文件基本上都是用于图表的数据,还有一些可选的视觉信息。在这个例子中,我将图表的类型指定为条形图。我下载的 SWF 文件所在的那个站点上有关于可以设置的选项以及可用的图形类型的更多信息。

当在 Firefox 浏览器中浏览到这个文件时,可以看到如 图 1 显示的图形。

图 1. 使用中的 Chart 小部件
 
 可以看到,这个图表的默认颜色模式和外观确实很整洁。这个图恰到好处地对轴线值进行了均匀布置。整体效果非常好,而我为之付出的精力并不多。

显然,可以用一个动态 Web 页面替换 graph_data.xml 文件:只要返回的数据具有正确的格式,图形控件关系不大。本文中的所有例子都是这样的。实际上,可以在一个 Web 浏览器中运行本地文件上的所有例子,而不必使用 Web 服务器(比如 Apache Tomcat 或 IBM? WebSphere? Application Server)或 Web 编程语言(比如 PHP、Microsoft? ASP.NET、Java? 2 Enterprise Edition [Java EE])。

SWF/Gauge 小部件

显示数据的另一种极具吸引力的方式就是使用仪表盘小部件。个人而言,我不是很欣赏这种做法,因为即使是显示一点点信息都要占用大量的空间。但仪表盘是高管仪表板的一个关键特性,所以若能快速创建仪表盘,将会十分方便。

但是,如果 Web 连简单的柱形图都处理不好的话,那么当然也就不能处理圆形的仪表盘了。所以,我又将目光投向创建 XML/Graph 的同一家公司。他们也有一个针对仪表盘的解决方案:XML/Gauge。

我将从嵌入了 SWF/Gauge 小部件的 HTML 开始,如 清单 3 所示。

清单 3. Gauge_page.html

<html><body>

<object classid="clsid:D27CDB6E-AE6D-11cf-96B8-444553540000"
codebase="http://download.macromedia.com/.../swflash.cab#version=6,0,0,0"
width="110" height="55">
<param name=movie VALUE="gauge.swf?xml_source=gauge_data.xml">
<embed src="gauge.swf?xml_source=gauge_data.xml"
width="110" height="55" type="application/x-shockwave-flash"
pluginspace="http://www.macromedia.com/go/getflashplayer">

</embed></object>

</body></html>

gauge.swf 带有一个参数:数据的位置。在这个例子中,这个位置为 gauge_data.xml,如 清单 4 所示。

清单 4. Gauge_data.xml

<gauge>

<circle fill_color="888888" start="275" fill_alpha="100"
line_color="555555" line_thickness="3" line_alpha="90"
radius="50" x="55" end="445" y="55"/>
<circle fill_color="99bbff" start="280" fill_alpha="90"
line_thickness="4" line_alpha="20" radius="45" x="55"
end="440" y="55"/>
<circle fill_color="666666" start="317" fill_alpha="100"
line_color="333333" line_alpha="0" radius="44" x="55"
end="322" y="55"/>
<circle fill_color="666666" start="337" fill_alpha="100"
line_color="333333" line_alpha="0" radius="44" x="55"
end="342" y="55"/>
<circle fill_color="666666" start="357" fill_alpha="100"
line_color="333333" line_alpha="0" radius="44" x="55"
end="362" y="55"/>
<circle fill_color="666666" start="377" fill_alpha="100"
line_color="333333" line_alpha="0" radius="44" x="55"
end="382" y="55"/>
<circle fill_color="666666" start="397" fill_alpha="100"
line_color="333333" line_alpha="0" radius="44" x="55"
end="402" y="55"/>
<circle fill_color="666666" start="417" fill_alpha="100"
line_color="333333" line_alpha="0" radius="44" x="55"
end="422" y="55"/>
<circle fill_color="99bbff" start="280" fill_alpha="100"
radius="40" x="55" end="440" y="55"/>
<circle fill_color="FF4400" start="280" fill_alpha="100"
radius="44" x="55" end="310" y="55"/>
<circle fill_color="44FF00" start="50" fill_alpha="100"
radius="44" x="55" end="80" y="55"/>
<circle fill_color="99bbff" start="280" fill_alpha="80"
radius="40" x="55" end="440" y="55"/>
<circle fill_color="333333" start="270" fill_alpha="100"
line_alpha="0" radius="20" x="55" end="450" y="55"/>

<rotate start="280" shake_span="2" shadow_alpha="15"
step="1" x="55" span="0" y="55" shake_frequency="20">
<rect fill_color="ffff00" fill_alpha="90" line_alpha="0"
height="40" x="53" width="4" y="13"/>
</rotate>

<circle fill_color="111111" start="270" fill_alpha="100"
line_thickness="5" line_alpha="50" radius="15" x="55"
end="450" y="55"/>

</gauge>

可以看到,SWF 对这个小部件采取了一种不同的处理方法。实际上,我是用圆、弧和矩形之类的基本图形构建仪表盘的,而没有通过为仪表盘(或图形)指定数据的方式。

坦白地说,我更喜欢一组预先设置好的仪表盘,这样一来,我所要做的只是用它们提供数据。但是,这里的这种方法仍然有效,它给予了我无限的调整空间 —— 尽管我更喜欢预先设置好的仪表盘以便在此基础上展开工作。

当在浏览器中访问那个页面时,可以看到如 图 2 所示的仪表盘。

图 2. 使用中的仪表盘小部件


 您可能会想,由于局限于一些基本图形,这种小部件并不能带来更好的效果。事实并非如此。基本图形也包括一些简单的动画技术,所以仍然有可发挥的空间,例如可以创建热点链接区,用户可以通过单击这些链接进行导航。此外,还可以想得更远一些,不仅可以将它用于仪表盘,而且还可以使用其简单的图形原语构建任何类型的图像和简单的动画。

就地编辑

在桌面应用程序中,就地编辑功能并不鲜见,但是到目前为止,这个功能在 Web 上还不多见。在 Web 2.0 中,交互性变得非常重要,所以像就地编辑之类的技术也更加普遍起来。

为了实现就地编辑,可以自己编写代码,也可以使用一个 JavaScript 框架来处理大部分事情。最流行的一个工具包就是 Scriptaculous 框架,它构建在 prototype.js 库之上。Scriptaculous 库使得构建就地编辑控件非常容易。

清单 5 显示了就地编辑的一个简单的 HTML 测试文件。

清单 5. Inplace.html

<html><head>
<script src="prototype.js"></script>
<script src="effects.js"></script>
<script src="controls.js"></script>
<script src="scriptaculous.js"></script>
</head><body>
<table width="100%">
<tr><th width="10%">Name</th>
<td width="90%"><p id="name">Candy bar</p></td>
</tr></table>
<script>
new Ajax.InPlaceEditor('name', 'submitted.html' );
</script>
</body>
</html>

首先,Inplace.html 包括所有必需的 JavaScript 源文件。然后,我插入了一个简单的表,其中有一个包含可就地编辑的 数据的段落。在文件的最后,我插入了一小段脚本,用于为那个段落创建 InPlaceEditor 对象。

InPlaceEditor 构造函数带有两个参数:一个参数是段落的 ID,另一参数是用于在我完成编辑之后处理提交的那个页面的 URL。在这个例子中,该页面为 submitted.html;但实际上,它也可能是一个 ASP.NET、Java EE 或 PHP 页面,或者是使用其他一些动态 Web 技术的页面。

清单 6 显示了简单的 submitted.html 文件。

 清单 6. Submitted.html

<p>Name changed!</p>

现在来做一下测试。首先用浏览器打开这个 HTML 文件。一开始可以看到初始的文本。当把鼠标放在文本上时,它变成黄色,如 图 3 所示。

图 3. 就地编辑的初态


 黄色意味着用户可以单击该字段,然后对其进行编辑。于是,我单击该字段,随后得到了一个 Name 字段、一个 ok 按钮和一个 cancel 链接,如 图 4 所示。

图 4. 单击之后编辑文本
 
 然后,我改变文本,并单击 ok,这样就会将数据发送到服务器(或者 submitted.html 页面,就像这个例子一样)。然后,服务器返回替换了初始文本的 HTML 页面。在这个例子中,我发回了 Name changed! (如 图 5 所示);在实际应用中,它更可能是数据的新值。

图 5. 单击 ok 后的新内容


 这些简单的界面升级就可以使应用程序的可用性大大提高。等待页面装载(尤其是从较慢的服务器上装载)会让人觉得界面太土、太陈旧。使用就地编辑器之类的简单工具就可让应用程序光鲜起来,而实现起来并不复杂。

DHTML 窗口

浏览器使在 Web 页面上构建模式窗口变得更困难,这也许是件好事。但是有时候,小窗口也不错。它们便于显示警告,或者弹出小型表单。它们也是弹出广告的一种很好的方式,这些烦人的广告总是盖住了页面的内容。哦,等等:最后一句收回。

不管怎样,为动态 HTML(DHTML)页面构建窗口并不容易。所以,当我发现这个基于流行的 Protoype.js 库的极其健壮的窗口包时,感到非常高兴。它不仅容易使用,界面还可以换肤,而且在每个浏览器上都运行得很好。清单 7 显示了 window.html 页。

清单 7. Window.html

<html>
<head>
<link href="default.css" rel="stylesheet" type="text/css" />
<script src="prototype.js"></script>
<script src="window.js"></script>
</head>
<body>
<script>
var win = new Window( 'myPopup', {
title: "Terms and Conditions",
top:70, left:100, width:300, height:200,
resizable: true, url: "terms.html",
showEffectOptions: { duration: 3 }
}
);
win.show();
</script>
</body>
</html>

我首先将 prototype.js 和 window.js 源文件放进头部。然后,用我喜欢的参数构建弹出对象,包括大小、位置、标题和小部件从中获取显示内容的页面的 URL。通过 Ajax 从一个页面装载内容仅仅是获取内容的一种方法。您也可以通过 JavaScript 代码动态地设置它们,或者用页面上已有的 <div> 标记围住窗口。

在这个例子中,我引用了在 清单 8 中显示的 terms.html 文件。

清单 8. Terms.html

<html><body bgcolor="white">
<h1>Terms and Conditions</h1>
<p>In order to use this site you must comply
with the following conditions...</p>
</body></html>

当在浏览器中打开该页面时,会看到如 图 6 所示的窗口。

图 6. 初始窗口
 
 不,那不是两个层叠的 Mac 窗口。其中一个是真正的 Firefox 浏览器窗口,另一个是 Mac 风格的假 DHTML 窗口。只不过彼此看起来很相似。

我可以放大和移动窗口,如 图 7 所示。

图 7. 移动和缩放之后的窗口
 
 为了写作本文,同时也为了工作之用,我对几个 DHTML 窗口库进行过一些考察,我可以肯定地告诉您,这一个是我感觉最棒的一个。 其他窗口包有的在显示上有问题,有的显示起来不完整,有的在我调整其大小时效果很差。而这个窗口则看上去非常像浏览器中的一个真正的窗口。

carousel 小部件

从事过与用户界面(UI)相关的工作的人都会说,屏幕是非常宝贵的。将尽可能多的数据塞进一个给定的空间里,同时又不显得拥挤,这一点很重要。所以,当我第一次在 Apple iTunes 中看到一个 carousel 控件时,我感到异常惊喜。

carousel 控件 可以在一个固定的区域里显示多个图像。在图像区域的左边和右边有左箭头和右箭头。如果单击箭头,则图像就会向左或向右移动,并被一组新的图像取代。在 iTunes 中,图像都是一些唱片封面,每种唱片风格都有一个 carousel 控件。

这个控件可以节省很多空间:可以将 30 个唱片封面放在三个封面大的空间里,并还能以合理的大小显示每个封面。而且,这个空间也很直观。它看上去像一个简化的滚动条。

它的缺点就是不容易实现,很重要的一个原因就是图像左右移动这个诱人的动画效果不好实现。所以,很高兴能看到一个名为 carousel 的、构建在 jQuery JavaScript 框架之上的 carousel。

在 清单 9 中,我在 Web 页面上实现了一个简单的 carousel 小部件。

清单 9. Carousel.html

<html>
<head>
<script type="text/javascript" src="js/jquery-1.0.3.js"></script>
<script type="text/javascript" src="js/jcarousel.js"></script>
<style type="text/css">
#mycarousel { display: none; }
.jcarousel-scope { position: relative; width: 255px;
-moz-border-radius: 10px; background: #D4D0C8;
border: 1px solid #808080; padding: 20px 45px; }
.jcarousel-list li { width: 81px; height: 81px;
margin-right: 7px; }
.jcarousel-list li img { border: 1px solid #808080; }
.jcarousel-list li a { display:block; outline: none;
border: 2px solid #D4D0C8; -moz-outline:none; }
.jcarousel-list li a:hover { border: 2px solid #808080; }
.jcarousel-next { position: absolute; top: 45px;
right: 5px; cursor: pointer; }
.jcarousel-next-disabled { cursor: default; }
.jcarousel-prev { position: absolute; top: 45px;
left: 5px; cursor: pointer; }
.jcarousel-prev-disabled { cursor: default; }
.loading { position: absolute; top: 0px;
right: 0px; display: none; }
</style>
<script type="text/javascript">
function loadItemHandler( carousel, start, last, available ) {
if (available) { carousel.loaded(); return; }
var cr = carousel;
jQuery.get("data.xml", function(data) { appendItemCallback(cr, start, last, data); });
};

function appendItemCallback( carousel, start, last, data ) {
var items = data.match( /(\<img .*?\>)/g );

for (i = start; i <= last; i++) {
if ( items[ i - 1 ] == undefined ) break;
var item = carousel.add( i, getItemHTML( items[i-1]) );
item.each(function() {
jQuery("a.thickbox", this).click(function() {
var t = this.title || this.name || null;
var g = this.rel || false;
TB_show(t,this.href,g);
this.blur();
return false;
});
});
}
carousel.loaded();
};

function getItemHTML( item ) {
var found = item.match( /href=\"(.*?)\"/ );
var url = jQuery.trim(found[1]);
var title = jQuery.trim(found[1]);
var url_m = url.replace(/_s.jpg/g, '_m.jpg');
return '<a href="' + url_m +
'" title="' + title +
'" class="thickbox"><img src="' + url +
'" width="' + 75 + '" height="' + 75 +
'" alt="' + title + '" /></a>';
};

var nextOver = function() {
jQuery(this).attr("src", "img/horizontal-ie7/next-over.gif"); };

var nextOut = function() {
jQuery(this).attr("src", "img/horizontal-ie7/next.gif"); };

var nextDown = function() {
jQuery(this).attr("src", "img/horizontal-ie7/next-down.gif"); };

function nextButtonStateHandler(carousel, button, enabling) {
if (enabling) {
jQuery(button).attr("src", "img/horizontal-ie7/next.gif")
.mouseover(nextOver).mouseout(nextOut).mousedown(nextDown);
} else {
jQuery(button).attr("src", "img/horizontal-ie7/next-disabled.gif")
.unmouseover(nextOver).unmouseout(nextOut).unmousedown(nextDown);
}
}

var prevOver = function() {
jQuery(this).attr("src", "img/horizontal-ie7/prev-over.gif"); };

var prevOut = function() {
jQuery(this).attr("src", "img/horizontal-ie7/prev.gif"); };

var prevDown = function() {
jQuery(this).attr("src", "img/horizontal-ie7/prev-down.gif"); };

function prevButtonStateHandler(carousel, button, enabling) {
if (enabling) {
jQuery(button).attr("src", "img/horizontal-ie7/prev.gif")
.mouseover(prevOver).mouseout(prevOut).mousedown(prevDown);
} else {
jQuery(button).attr("src", "img/horizontal-ie7/prev-disabled.gif")
.unmouseover(prevOver).unmouseout(prevOut).unmousedown(prevDown);
}
}

jQuery(document).ready(function() {
jQuery().ajaxStart(function() { jQuery(".loading").show(); });
jQuery().ajaxStop(function() { jQuery(".loading").hide(); });
jQuery("#mycarousel").jcarousel({
itemVisible: 3, itemScroll: 2, wrap: true,
loadItemHandler: loadItemHandler,
nextButtonStateHandler: nextButtonStateHandler,
prevButtonStateHandler: prevButtonStateHandler
});
});
</script></head><body><div id="mycarousel">
<div class="loading">
<img src="img/loading.gif" width="16" height="16" border="0" />Loading...</div>
<img src="img/horizontal-ie7/prev.gif" border="0" class="jcarousel-prev" />
<img src="img/horizontal-ie7/next.gif" border="0" class="jcarousel-next" />
<ul></ul>
</div></body></html>

是的,与前面的例子相比,这个例子多了很多代码。但大部分代码用于设置图形和解释从服务器返回的 Ajax 数据。实际上,本文中的大部分代码都是以下载部分提供的一个例子为基础的。所以,我不必学习很多东西,也不必阅读任何文档,就可以使用这个控件。

清单 10 显示了用于 carousel 的数据。

清单 10. Data.xml

<images>
<img href="pics/image1.jpg" />
<img href="pics/image2.jpg" />
<img href="pics/image3.jpg" />
<img href="pics/image4.jpg" />
</images>

这是一个简单的 XML 文件,它有一个 <images> 标记,其中包含一组 <img> 标记,在这些标记中保存有每个图像的 URL。 您可以使用您喜欢的任何格式,因为这个控件并不是一个纯正的 Ajax 小部件。我还编写了解释 XML 并创建 carousel 中每个幻灯片元素的代码。最终的结果如 图 8 所示。

我可以单击图像,进入到包含该图像的页面(或我指定的任何 URL)。或者,我也可以通过单击左箭头或右箭头滚动 carousel,以查看更多图像。实际效果确实令人印象深刻。

结束语

我展示了网上提供的一些小部件和工具,它们有的是商业性的,有的是免费的。当我准备撰写本文时,我也看到过很多没有使用 Ajax 的工具。虽然这些工具不适合放到本文讨论,但是它们本身也是很值得注意的。特别是,网上供下载的很多高质量的开源 WYSIWYG 编辑器给我留下了深刻的印象。当用户不得不使用文本框中的 HTML 将内容以粗体、斜体、链接、图像等形式放到站点上时,他们常常会感到十分郁闷。而这些编辑器则能隐藏所有 HTML,使用户感觉起来像是在文字处理应用程序中进行编辑一样。

除了 WYSIWYG 编辑器以外,还可以找到进度条、带选项卡的对话框、折叠式控件、时钟、日期选择器、RSS 和 Outline Processor Markup Language (OPML) 阅读器,甚至交互终端窗口的解决方案。所以,在构建自己的 DHTML 或 Flash 控件之前,显然应该看看网上有没有可用的控件(通常都是免费的)。通过使用像前面提到的那样的小部件,可以很容易地为站点增加交互性。

 

组织简介 | 联系我们 |   Copyright 2002 ®  UML软件工程组织 京ICP备10020922号

京公海网安备110108001071号