13.1 渐进增强与 Ajax

相信大家对渐进增强这个概念已经不陌生了。请允许我在这里再啰嗦一遍:渐进增强就是先着手实现一个具有基本功能的产品,然后在些基础上为使用现代浏览器的用户添加一些装饰,以保证所有用户享受到基本甚至更好的体验。

大量运用Ajax的应用经常会面临用户不能使用JavaScript的风险。为了避免这种风险,可以先使用表单构建一个传统的客户端-服务器页面,而在JavaScript可用的情况下再修改表单,提供更有效的交互方式。

下面我们来写一个例子,实现通过表单来查询jQuery API文档。既然jQuery网站上已经有了这么一个表单,那么我们在这里只要照抄过来就好了:

  1. <form id="ajax-form" action="http://api.jquery.com/" method="get">
  2. <fieldset>
  3. <div class="text">
  4. <label for="title">Search</label>
  5. <input type="text" id="title" name="s">
  6. </div>
  7. <div class="actions">
  8. <button type="submit">Request</button>
  9. </div>
  10. </fieldset>
  11. </form>

 下载示例代码

如同本书其他HTML、CSS以及JavaScript示例一样,上面的标记只是完整文档的一个片段。如果读者想试一试这些示例,可以从以下地址下载完整的示例代码: Packt Publishing 网站 http://www.packtpub.com/support ,或者本书网站 http://book.learningjquery.com/

这个搜索表单已经应用了一些CSS样式,但实际上只是一个常规的表单元素,其中包含一个文本输入框和一个提交按钮,如图13-1所示。

收获JSONP数据 - 图1

图 13-1

在单击Request按钮后,这个表单照常提交,用户的浏览器会跳转到http://api.jquery.com/,而结果如图13-2所示。

收获JSONP数据 - 图2

图 13-2

如果JavaScript可用,我们希望把上图中的内容加载到搜索页面的#response容器内,而不是让用户离开当前页面。既然数据和表单在同一个服务器上,那么就可以使用.load()方法取得页面中相关的部分了,参见代码清单13-1。

代码清单13-1

  1. $(document).ready(function() {
  2. var $ajaxForm = $('#ajax-form'),
  3. $response = $('#response');
  4. $ajaxForm.on('submit', function(event) {
  5. event.preventDefault();
  6. $response.load('http://api.jquery.com/ #content',
  7. $ajaxForm.serialize());
  8. });
  9. });

可是,这个API站点有一个不同的主机名,浏览器的同源策略不允许这样传输数据。怎么办呢?我们现在需要一个好用的跨域数据传输方案。为此,我们在http://book.learningjquery.com/上以JSONP格式公开它的数据,这种格式很适合跨域使用。

收获JSONP数据

第6章曾介绍过,JSONP无非就是简单的JSON加上了服务器支持,让我们能够向不同的站点发送请求。在请求JSONP数据时,需要提供一个特殊的查询字符串参数,发送请求的脚本就是通过该参数来收获数据的。JSONP服务器可以在认为合适的任何时候调用该参数。对于jQuery API站点而言,这个参数(也是默认的名字)是callback

因为可以使用默认回调函数名,所以通过jQuery发送JSONP请求时,唯一要做的就是说明我们想要的是jsonp类型的数据,参见代码清单13-2。

代码清单13-2

  1. $(document).ready(function() {
  2. var $ajaxForm = $('#ajax-form'),
  3. $response = $('#response');
  4.  
  5. $ajaxForm.on('submit', function(event) {
  6. event.preventDefault();
  7.  
  8. $.ajax({
  9. url: 'http://book.learningjquery.com/api/',
  10. dataType: 'jsonp',
  11. data: {
  12. title: $('#title').val()
  13. },
  14. success: function(data) {
  15. console.log(data);
  16. }
  17. });
  18. });
  19. });

然后,可以从控制台中看到返回的JSONP数据。此时的数据是对象的数组,每个对象描述了一个jQuery方法,数据结构如下:

  1. {
  2. "url": "http://api.jquery.com/innerWidth/",
  3. "name": "innerWidth",
  4. "title": ".innerWidth()",
  5. "type": "method",
  6. "signatures": [
  7. {
  8. "added": "1.2.6"
  9. }
  10. ],
  11. "desc": "Get the current computed width for the first element in the set of matched elements, including padding but not border.",
  12. "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>",
  13. "categories": [
  14. "CSS",
  15. "Dimensions",
  16. "Manipulation > Style Properties",
  17. "Version > Version 1.2.6"
  18. ],
  19. "download": ""
  20. }

我们所要显示的关于每个方法的数据都包含在这些对象里了。只要套用适当的格式把它们显示出来就好。为了显示每个方法的说明而创建HTML代码有点麻烦,因此下面就来创建一个辅助函数,参见代码清单13-3。

代码清单13-3

  1. var buildItem = function(item) {
  2. var title = item.name,
  3. args = [],
  4. output = '<li>';
  5. if (item.type == 'method' || !item.type) {
  6. if (item.signatures[0].params) {
  7. $.each(item.signatures[0].params, function(index, val) {
  8. args.push(val.name);
  9. });
  10. }
  11. title = (/^jQuery|deferred/).test(title) ? title : '.' + title;
  12. title += '(' + args.join(', ') + ')';
  13. } else if (item.type == 'selector') {
  14. title += ' selector';
  15. }
  16. output += '<h3><a href="' + item.url + '">' + title + '</a></h3>';
  17. output += '<div>' + item.desc + '</div>';
  18. output += '</li>';
  19. return output;
  20. };

这个buildItem()函数负责把JSONP对象转换为一个HTML列表项。在此,必须处理多个方法参数和多种函数签名的情况,所以使用了循环并调用了.join()方法。这一步完成后,又创建了一个指向主文档的链接,然后输出当前项的描述。

这样,我们就有一个函数,它可以创建一个列表项的HTML代码。当Ajax调用完成后,可以针对每一个对象来调用这个函数,把得到的结果显示出来,参见代码清单13-4。

代码清单13-4

  1. $(document).ready(function() {
  2. var $ajaxForm = $('#ajax-form'),
  3. $response = $('#response'),
  4. noresults = 'There were no search results.';
  5. var response = function(json) {
  6. var output = '';
  7. if (json && json.length) {
  8. output += '<ol>';
  9. $.each(json, function(index, val) {
  10. output += buildItem(val);
  11. });
  12. output += '</ol>';
  13. } else {
  14. output += noresults;
  15. }
  16. $response.html(output);
  17. };
  18. $ajaxForm.on('submit', function(event) {
  19. event.preventDefault();
  20. $.ajax({
  21. url: 'http://book.learningjquery.com/api/',
  22. dataType: 'jsonp',
  23. data: {
  24. title: $('#title').val()
  25. },
  26. success: response
  27. });
  28. });
  29. });

因为我们已经在$.ajax()选项的外部定义了成功处理程序,所以可以直接引用它的函数名。即使出现简洁的考虑,也可以在这里使用一个嵌入的匿名函数。但为了保证代码清晰可读,单独定义一个函数显然更好。

定义了这个成功处理程序后,再进行搜索就可以在表单旁边显示出漂亮的结果了,如图13-3所示。

收获JSONP数据 - 图3

图 13-3