65.9K
CodeProject 正在变化。 阅读更多。
Home

使用 jQuery 实现 Web 应用常用任务

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.96/5 (15投票s)

2010 年 9 月 15 日

CDDL

8分钟阅读

viewsIcon

70726

downloadIcon

976

使用 jQuery 插件实现的一些客户端常用任务,并附带演示源代码。

webappcommontasks.demo.png

引言

我使用 jQuery 的经验之一是,我经常需要搜索解决方案来处理一些特定的任务。尽管很有可能你试图解决的问题已经被别人通过一个 插件 解决了,但你仍然需要消化、评估、测试,有时还需要修改它以适应你的项目需求。对于大多数快速 Web 开发来说,这都是可以的。然而,对于一些基于浏览器、低级别的任务,这些任务几乎是所有 Web 应用都常见的,搜索多个插件然后将它们组合在一起并进行测试会浪费不必要的时间。本文提供了一个 jQuery 插件 (CBEXP),它将最常见的任务封装在一起,并且可以重用于不同的 Web 应用。

例如,如果你的 Web 应用需要有条件地(例如,一个 Ajax 调用尚未返回)在用户离开或关闭浏览器窗口时发出警告,或者你的客户端/服务交互依赖于 cookie,并且你想确保浏览器 cookie 未被禁用,或者你需要解析查询字符串并在客户端检索特定的参数值,或者需要在不向服务器回发的情况下动态加载不同的样式表等,那么拥有一个 jQuery 插件来处理所有这些低级别的、基于浏览器的任务将会非常好,这样我们就可以更专注于应用特定的功能。

事实上,CBEXP 插件最初是为真实的、面向消费者的 Web 应用而开发的,它已经过测试并且运行良好,希望它也能对你的 Web 应用有所帮助。

你可以先在这里查看 演示 HTML 页面。

依赖项

此插件依赖于 jQuery 1.4.2、 Cookie 插件jQuery-JSON 插件,它设计为无需服务器页面(ASP、JSP、PHP、Ruby on Rails 等)即可使用。当然,它也可以与服务器页面一起使用,我只是想指出它与静态 HTML 文件配合良好。我通常将其放入一个面向公众的登录页面,如果任何浏览器设置出现意外情况,我们可以在页面加载时告知用户。

配套的源代码中有一个 `cbexp_demo.html` 页面,它是一个 `static` HTML 页面,展示了常用任务是如何执行的示例。

由于所有常用任务都将由 JavaScript 代码执行,所以首先要检查的是确保浏览器的 JavaScript 已启用,如果未启用,我们将告知用户需要启用它并显示启用步骤。

通过 `cbexp_demo.html` 页面,HTML <noscript> 标签用于在最终用户的浏览器禁用 JavaScript 时显示替代内容。演示页面中的所有页面内容都包装在一个“`pageContainer`” `(id) DIV` 中,最初,页面的 CSS(`stylesheets/cbexp.demo.css`)会隐藏 `pageContainer` DIV。如果 JavaScript 未启用,用户只能看到 `<noscript>` 标签内的内容;如果 JavaScript 已启用,`cbexp.demo.js` 中的 `documentReady` 事件处理程序将显示 `pageContainer`,有关详细信息请参见图 1。

// Fig.1. document ready event handler will not executed 
// if JavaScript is disabled, otherwise it would fade in the page content 

$(function () { $('#pageContainer').fadeIn(); });

现在我们已经解决了依赖问题,让我们开始处理 jQuery cbexp 插件的第一个常用任务。

检查 Cookie 是否已启用

同样,当 Cookie 被禁用时,我们会告知用户需要启用 Cookie。所有这些用于无 Cookie 情况的 HTML 内容都包装在一个 ID 为“`noCookieContainer`”的 `DIV` 中,就像 `pageContainer` 一样,它最初由 `stylesheets/cbexp.demo.css` 隐藏。

// Fig. 2. Page CSS sets both pageContainer and noCookieContainer hidden
#pageContainer, #noCookieContainer {
	display:none;
}  

在 `documentReady` 事件处理程序中,它会调用客户端 cookie 检测方法,当未启用时,它会显示 `noCookie` 内容。当你的 Web 应用同时需要 JavaScript 和 Cookie 时,`document ready` 事件处理程序将变成

//Fig.3 When both JavaScript and Cookie are required, 
//document ready handles the detection and show/hide appropriate content
$(function ()
{
	if ($.cbexp.isCookieEnabled())
		$('#pageContainer').fadeIn();
	else
		$('#noCookieContainer').fadeIn();
});

注意对 `$.cbexp.isCookieEnabled()` 的调用,它是在 CBEXP 插件中实现的,作为一种客户端 cookie 检测方法,它简单地尝试写入一个 cookie,如果能够读回它,那么浏览器 cookie 设置就没问题了。在 jQuery Cookie 插件的帮助下,这是 `isCookieEnabled` 的代码。

//Fig.4 Client side cookie detector, writes/reads then remove the detector cookie
isCookieEnabled: function ()
{
    $.cookie('cbexp_cookie_detector', 'cbexp_test');
    var retVal = ('cbexp_test' == $.cookie('cbexp_cookie_detector'));

    $.cookie('cbexp_cookie_detector', null);

    return retVal;
}

当然,检测 cookie 设置还有许多其他方法,例如往返服务器,但我发现这种客户端方法既简单又快速。如果你的 Web 应用不需要 cookie,那么你可以不依赖 jQuery Cookie 插件来使用 CBEXP 插件。

除了 cookie 设置检测,查询字符串解析是另一个常用任务。

解析查询字符串

CBEXP 插件提供了一个简单高效的方法来解析当前页面 URL 的所有查询字符串,在没有定义查询字符串的情况下,它会返回一个空数组对象。这是代码:

//Fig. 5 Basic query string parsing method with in cbexp plugin
getQueryString: function ()
{
    var qs = window.location.search;
    if (qs.length <= 1)
        return new Array();
    qs = qs.substring(1, qs.length);
    var a = qs.split("&");
    var b = new Array();
    for (var i = 0; i < a.length; ++i)
    {
        var p = a[i].split('=');
        b[p[0]] = decodeURIComponent(p[1]);
    }
    return b;
}   

可以通过查询 `getQueryString` 的返回值来检索特定的查询字符串参数值,例如:`$.cbexp.getQueryString()[QueryStringParamName]`,如果值已定义,它将返回一个从 URI 编码字符串解码的 `string`,如果未定义,它将返回 `null`。

稍后在本文中,我将举例说明如何根据不同的查询字符串参数值动态加载 CSS 和 HTML。

以上是关于浏览器信息检测和信息检索任务,让我们将注意力转向客户端-服务交互。

使用 X-HTTP-Method-Override 扩展 Ajax 调用

在我们的 Web 应用中,我们特意将安全的 RESTful 风格的 Web 服务 API 设计为 `POST`-only 或重载 `POST` 风格。当客户端发起服务调用时,它只使用一个 HTTP 动词 — `POST`。主要原因是我们的 Web 应用面向普通消费者,每个最终用户可能都有不同的防火墙设置,有些防火墙根本不允许 `PUT` 或 `DELETE`,只有 `GET` 和 `POST` 才能通过端口 80。由于我们仍然保护了 Web 服务中的 `GET` 请求,所有请求实际上都变成了 `POST` 请求。

我们在服务请求方面获得了一些一致性,例如请求体中用于身份验证和授权信息,但我们在 HTTP 动词的标准操作含义方面有所损失,比如 `GET`(读取)、`PUT`(创建)和 `DELETE` 的常规含义。为了解决这个服务请求动词含义缺失的问题,并尝试利用服务器端框架对 HTTP 动词的支持(将特定的 HTTP 动词映射到服务层中相应的 CRUD 方法),我们扩展了 jQuery Ajax API,可以选择性地设置 X-HTTP-Method-Override HTTP 头,这与 Google Data Protocol 的做法类似。

思路是:所有服务请求都将通过 CBEXP 插件中的一个通用 Ajax 包装方法,通过选择性地传入 `GET`、`PUT` 和 `DELETE` 动词,该方法将为 Ajax 调用打包正确的头信息和设置,具体细节如下:

Fig. 6. Ajax call wrapper for overloaded POST method with X-HTTP-Method-Override support	
postJson: function (postURL, jsDataObj, onSuccess, onError, httpMethodOverride)
{
    var beforeSendCallback = null;

    try
    {
        if (httpMethodOverride != undefined)
        {
            httpMethodOverride = httpMethodOverride.toUpperCase();
            if (httpMethodOverride == "GET" || 
		httpMethodOverride == "PUT" || httpMethodOverride == "DELETE")
            {
                beforeSendCallback = function (xhr)
                {
                    xhr.setRequestHeader("X-HTTP-Method-Override", httpMethodOverride);
                    xhr.withCredentials = true;
                };
            }
        }
        else
        {
            beforeSendCallback = function (xhr)
            {
                xhr.withCredentials = true
            };
        }

        $.ajax({
            type: "POST",
            beforeSend: beforeSendCallback,
            contentType: "application/json",
            dataType: "json",
            url: postURL,
            data: $.toJSON(jsDataObj),
            async: true,
            success: onSuccess,
            error: onError
        });
    }
    catch (err)
    {
        alert("Ajax call error : " + err.description);
    }
}

注意,它利用了 jQuery JSON 插件中的 `$.toJSON(jsDataObj)` 将 JavaScript 对象序列化为 JSON 字符串作为 `POST` 请求体。

以上是我们对 Ajax 调用的扩展。从更广泛的意义上讲,“Ajax”不仅指通过 `XMLHTTPRequest` 与服务交互,还包括纯粹基于客户端逻辑动态地、有条件地加载 HTML、JavaScript 和 CSS 文件到 DOM,让我们看看 EBEXP 插件如何支持此作为常用任务。

从客户端动态加载 HTML、JavaScript 和 CSS

jQuery 是一个轻量级但功能强大的 JavaScript 库,我惊讶地发现它已经内置支持通过 jQuery .load API 动态加载 HTML,并且还有一个 .getScript API 用于动态加载 JavaScript 文件。它绝对支持在构建动态网页方面采用各种技术和处理方式。在客户端需要运行时加载 CSS 文件的情况下,CBEXP 插件提供了一个简单易用的方法。

Fig. 7 Support for programmatically loading CSS
loadPageCSS: function (cssUrl)
{
    $('<link>').appendTo('head').attr({
        rel: "stylesheet",
        type: "text/css",
        href: cssUrl
    });
}

该方法简单地创建一个具有适当属性的新 `<link>` 标签,并将其附加到页面的 header 中。根据 CSS 的一般层叠规则,如果后期加载的 CSS 与之前加载的外部 CSS 规则存在任何冲突,那么后期加载的 CSS 将会覆盖之前加载的。

虽然代码看起来简短而简单,但当把所有部分组合在一起时,它确实实现了强大的功能,例如让客户端确保浏览器具有正确的设置,以及根据参数(通过解析查询字符串)以编程方式加载 CSS 来动态更改页面的布局、外观和感觉。

在用户离开之前警告用户

在某些用例中,当用户尝试离开时,无论是通过点击链接、在浏览器地址栏输入新 URL、点击浏览器窗口的关闭按钮,我们都想警告用户,如果他们离开,某些数据将会丢失。CBEXP 插件支持一种编程方式来启用或禁用警告消息。从应用程序的角度来看,当设置了 dirty 标志时,它就启用了,否则它将被禁用。下面是 CBEXP 中的代码:

setDirtyFlag: function (docDirty)
{
    $.cbexp.isDirty = docDirty;
    if (docDirty)
        $(window).bind('beforeunload', $.cbexp.confirmExit);
    else
        $(window).unbind('beforeunload', $.cbexp.confirmExit);
},

getDirtyFlag: function ()
{
    return $.cbexp.isDirty;
},

confirmExit: function ()
{
    if ($.cbexp.isDirty)
        return "Exiting this page will end your session. 
	If you haven't saved your info, it could be lost.";
},

总结

配套的 演示页面 展示了如何使用 CBEXP 插件执行这些常用任务的示例。你可以 启动演示页面 并进行测试。

  • 禁用浏览器的 JavaScript(或简单地从 Internet Explorer 的本地硬盘加载它),然后重新加载它,它只会显示 noscript 内容。
  • 禁用浏览器 cookie 然后重新加载页面,它只会显示 `noCookie` 内容。
  • 在浏览器中同时启用 JavaScript 和 Cookie,然后重新加载它,它将显示绿色主题和两栏布局。
  • 使用查询字符串 `?theme=blue_layout` 重新加载页面,它将显示蓝色主题和反向布局。

请注意,以上所有功能都利用了 CBEXP 的常用任务支持来检测并以编程方式加载 CSS 和 HTML 来做出反应。页面的 header 和 footer 是在运行时加载到 DOM 中的,它本质上启用了一个“客户端主页”,其中 header 和 footer 与其他页面共享。

演示页面可以扩展到包括动态加载 JavaScript,我将把它留给你,如果你觉得这个话题很有趣。

历史

  • 2010年9月15日:初稿发布
© . All rights reserved.