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

Microsoft AJAX: 保持焦点,防止请求丢失,避免用户“连点”

starIconstarIconstarIcon
emptyStarIcon
starIcon
emptyStarIcon

3.75/5 (6投票s)

2008 年 9 月 15 日

CPOL

4分钟阅读

viewsIcon

42251

downloadIcon

260

如何扩展异步回发在更新面板中的行为。

引言

当尝试创建基于 Windows 应用程序的 Web 应用程序时,最终用户或管理层通常可以接受的行为差异之间会有一条细线。目前 Web 上有几个例子可以向您展示如何对异步请求进行排队和/或在请求之间保持焦点,但是我想将所有这些逻辑封装到一个不错的 JavaScript 对象中,以隐藏我自己的业务对象的该功能,出于可扩展性的原因。我必须承认,这里的一些想法来自不同的帖子,但我不再有链接。如果有人碰巧找到了显示类似代码的链接,我将在此处引用它。

目标

创建一个 JavaScript 对象,封装 Microsoft AJAX 的 pageRequestManager,以便在处理 UpdatePanel 的异步回发时扩展其行为。

代码

对象创建

第一步是创建 JavaScript 对象,并通过将其构造函数调用放在 .js 文件的顶部来初始化它(这确保我无需担心在文件本身之外构造它)。

var _AsyncPostbackUtility = new AsyncPostbackUtility();
function AsyncPostbackUtility(){}

构造函数

然后,我需要在构造函数中包含一些逻辑,该逻辑将连接到 pageRequestManager 的事件,以便我可以在回发期间与之交互。

注意:我检查是否定义了 Sys(即,页面上是否有 scriptManager,并且 scriptManager 是否有机会被定义)。如果您已将此脚本包含在脚本管理器的 scripts 目录中,则一切正常,但如果您直接在 HTML 页面中创建了自己的 include 标签,则可能会出问题。

function AsyncPostbackUtility(){ 
    var currentFocusedControlId = ""; //the last control that had focus. 
    var requestQueue = new Array(); //a pending request queue. 
    if (Sys) 
        Sys.Application.add_init(appInit); 
    else 
        throw 'Sys is not defined, please verify you ' + 
              'have a script manager on the page ' + 
              'and that this object js file is included ' + 
              'in its script collection.'; 

function appInit(){ 
    Sys.WebForms.PageRequestManager.getInstance().
        add_initializeRequest(initializeRequestHandler); 
    Sys.WebForms.PageRequestManager.getInstance().
        add_pageLoading(pageLoadingHandler); 
    Sys.WebForms.PageRequestManager.getInstance().
        add_endRequest(endRequestHandler); 
    } 
    …
}

事件处理程序

现在我们有了对象创建和事件连接,是时候将一些逻辑放入事件处理程序本身,以实现我们想要的功能了。

初始化请求处理程序

这是客户端发出回发请求时调用的第一个事件。在此事件中,我们有能力取消请求,如果已经在异步回发中,我们将使用它。您可能会问自己为什么我们需要一个队列,答案很简单。如果在另一个请求正在等待时尝试请求,则页面上的第一个请求将被客户端取消以支持第二个请求,您可以想象,这可能会使您的 UI 略微不同步。请注意,我们维护对导致上次回发的元素的引用,并且我们仅对不是由该元素生成的请求进行排队。这样做是为了防止用户在异步回发花费的时间超出预期时“连点”,并且禁用 UI 或使用进度指示器没有用。通过对请求进行排队,我们可以使应用程序感觉更流畅,而无需在每次回发时禁用表单,并确保我们不会错过用户快速移动并触发回发时来自 UI 的更新。

function initializeRequestHandler(sender, args){ 
    var postBackElement = args.get_postBackElement(); 
    if (Sys.WebForms.PageRequestManager.getInstance().
                     get_isInAsyncPostBack() == false){ 
        executingElement = postBackElement; 
    } 
    else{ 
    if (executingElement != postBackElement){ 
        var evArg = $get("__EVENTARGUMENT").value; 
    Array.enqueue(requestQueue, new Array(postBackElement, evArg)); 
    } 
    args.set_cancel(true); 
    } 
}

页面加载请求处理程序

此事件在页面请求管理器获取异步回发的结果后触发,但在它重建 UpdatePanel 之前触发,并且此对象专门用于获取对 ActiveElement 对象的引用,以便在重建 UpdatePanel 后保持焦点。

注意:Firefox 3/IE6 及更高版本将支持 activeElement 属性。如果您必须支持不同或更早版本的浏览器,我建议此时使用另一种方法。

function pageLoadingHandler(sender, args){ 
    currentFocusedControlId = typeof(document.activeElement) == 
                              "undefined" ? "" : document.activeElement.id; 
}

结束请求处理程序

此事件在 UpdatePanel 加载完毕并且异步回发已完成之后触发。在这里,我们将保持焦点并检查我们的队列,以查看是否需要处理其他回发。

function endRequestHandler(sender, args){ 
    focusControl(); 
    if (requestQueue.length > 0){ 
        var elemEntry = Array.dequeue(requestQueue); 
        var _element = elemEntry[0]; 
        var _eventArg = elemEntry[1]; 
        Sys.WebForms.PageRequestManager.getInstance()._doPostBack(
                                          _element.id, _eventArg); 
    }
}

焦点控制方法

焦点控制方法很简单,除了在末尾使用技巧将光标重置到文本框或文本区域元素的末尾。

function focusControl(){ 
    if (typeof(currentFocusedControlId) != 
               "undefined" && currentFocusedControlId != ""){ 
        var targetControl = 
            document.getElementById(currentFocusedControlId); 
    if (targetControl.contentEditable != "undefined"){ 
        oldContentEditableSetting = targetControl.contentEditable; 
    
        targetControl.contentEditable = false; 
    } 

    targetControl.focus(); 
    if (oldContentEditableSetting != "undefined") 
        targetControl.contentEditable = oldContentEditableSetting; 
        targetControl.value = targetControl.value; 
    } 
}

如何使用代码

它开箱即用,非常简单。只需通过页面上的脚本管理器将脚本引用添加到本文的 .js 文件,或者您可以从 .js 文件中删除构造函数,并在您的网页的 body 加载事件之后在某个位置调用它。

© . All rights reserved.