13.1 渐进增强与 Ajax
相信大家对渐进增强这个概念已经不陌生了。请允许我在这里再啰嗦一遍:渐进增强就是先着手实现一个具有基本功能的产品,然后在些基础上为使用现代浏览器的用户添加一些装饰,以保证所有用户享受到基本甚至更好的体验。
大量运用Ajax的应用经常会面临用户不能使用JavaScript的风险。为了避免这种风险,可以先使用表单构建一个传统的客户端-服务器页面,而在JavaScript可用的情况下再修改表单,提供更有效的交互方式。
下面我们来写一个例子,实现通过表单来查询jQuery API文档。既然jQuery网站上已经有了这么一个表单,那么我们在这里只要照抄过来就好了:
<form id="ajax-form" action="http://api.jquery.com/" method="get">
<fieldset>
<div class="text">
<label for="title">Search</label>
<input type="text" id="title" name="s">
</div>
<div class="actions">
<button type="submit">Request</button>
</div>
</fieldset>
</form>
下载示例代码
如同本书其他HTML、CSS以及JavaScript示例一样,上面的标记只是完整文档的一个片段。如果读者想试一试这些示例,可以从以下地址下载完整的示例代码: Packt Publishing 网站 http://www.packtpub.com/support ,或者本书网站 http://book.learningjquery.com/。
这个搜索表单已经应用了一些CSS样式,但实际上只是一个常规的表单元素,其中包含一个文本输入框和一个提交按钮,如图13-1所示。
图 13-1
在单击Request按钮后,这个表单照常提交,用户的浏览器会跳转到http://api.jquery.com/,而结果如图13-2所示。
图 13-2
如果JavaScript可用,我们希望把上图中的内容加载到搜索页面的#response
容器内,而不是让用户离开当前页面。既然数据和表单在同一个服务器上,那么就可以使用.load()
方法取得页面中相关的部分了,参见代码清单13-1。
代码清单13-1
$(document).ready(function() {
var $ajaxForm = $('#ajax-form'),
$response = $('#response');
$ajaxForm.on('submit', function(event) {
event.preventDefault();
$response.load('http://api.jquery.com/ #content',
$ajaxForm.serialize());
});
});
可是,这个API站点有一个不同的主机名,浏览器的同源策略不允许这样传输数据。怎么办呢?我们现在需要一个好用的跨域数据传输方案。为此,我们在http://book.learningjquery.com/上以JSONP格式公开它的数据,这种格式很适合跨域使用。
收获JSONP数据
第6章曾介绍过,JSONP无非就是简单的JSON加上了服务器支持,让我们能够向不同的站点发送请求。在请求JSONP数据时,需要提供一个特殊的查询字符串参数,发送请求的脚本就是通过该参数来收获数据的。JSONP服务器可以在认为合适的任何时候调用该参数。对于jQuery API站点而言,这个参数(也是默认的名字)是callback
。
因为可以使用默认回调函数名,所以通过jQuery发送JSONP请求时,唯一要做的就是说明我们想要的是jsonp
类型的数据,参见代码清单13-2。
代码清单13-2
- $(document).ready(function() {
- var $ajaxForm = $('#ajax-form'),
- $response = $('#response');
- $ajaxForm.on('submit', function(event) {
- event.preventDefault();
- $.ajax({
- url: 'http://book.learningjquery.com/api/',
- dataType: 'jsonp',
- data: {
- title: $('#title').val()
- },
- success: function(data) {
- console.log(data);
- }
- });
- });
- });
然后,可以从控制台中看到返回的JSONP数据。此时的数据是对象的数组,每个对象描述了一个jQuery方法,数据结构如下:
{
"url": "http://api.jquery.com/innerWidth/",
"name": "innerWidth",
"title": ".innerWidth()",
"type": "method",
"signatures": [
{
"added": "1.2.6"
}
],
"desc": "Get the current computed width for the first element in the set of matched elements, including padding but not border.",
"longdesc": "<p>This method returns the width of the element, including left and right padding, in pixels.</p>\n<p>This method is not applicable to <code>window</code> and <code>document</code> objects; for these, use <code><a href=\"/width\">.width()</a> </code> instead.</p>\n<p class=\"image\"><imgsrc=\"/images/0042_04_05.png\"/></p>",
"categories": [
"CSS",
"Dimensions",
"Manipulation > Style Properties",
"Version > Version 1.2.6"
],
"download": ""
}
我们所要显示的关于每个方法的数据都包含在这些对象里了。只要套用适当的格式把它们显示出来就好。为了显示每个方法的说明而创建HTML代码有点麻烦,因此下面就来创建一个辅助函数,参见代码清单13-3。
代码清单13-3
var buildItem = function(item) {
var title = item.name,
args = [],
output = '<li>';
if (item.type == 'method' || !item.type) {
if (item.signatures[0].params) {
$.each(item.signatures[0].params, function(index, val) {
args.push(val.name);
});
}
title = (/^jQuery|deferred/).test(title) ? title : '.' + title;
title += '(' + args.join(', ') + ')';
} else if (item.type == 'selector') {
title += ' selector';
}
output += '<h3><a href="' + item.url + '">' + title + '</a></h3>';
output += '<div>' + item.desc + '</div>';
output += '</li>';
return output;
};
这个buildItem()
函数负责把JSONP对象转换为一个HTML列表项。在此,必须处理多个方法参数和多种函数签名的情况,所以使用了循环并调用了.join()
方法。这一步完成后,又创建了一个指向主文档的链接,然后输出当前项的描述。
这样,我们就有一个函数,它可以创建一个列表项的HTML代码。当Ajax调用完成后,可以针对每一个对象来调用这个函数,把得到的结果显示出来,参见代码清单13-4。
代码清单13-4
$(document).ready(function() {
var $ajaxForm = $('#ajax-form'),
$response = $('#response'),
noresults = 'There were no search results.';
var response = function(json) {
var output = '';
if (json && json.length) {
output += '<ol>';
$.each(json, function(index, val) {
output += buildItem(val);
});
output += '</ol>';
} else {
output += noresults;
}
$response.html(output);
};
$ajaxForm.on('submit', function(event) {
event.preventDefault();
$.ajax({
url: 'http://book.learningjquery.com/api/',
dataType: 'jsonp',
data: {
title: $('#title').val()
},
success: response
});
});
});
因为我们已经在$.ajax()
选项的外部定义了成功处理程序,所以可以直接引用它的函数名。即使出现简洁的考虑,也可以在这里使用一个嵌入的匿名函数。但为了保证代码清晰可读,单独定义一个函数显然更好。
定义了这个成功处理程序后,再进行搜索就可以在表单旁边显示出漂亮的结果了,如图13-3所示。
图 13-3