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






3.75/5 (6投票s)
如何扩展异步回发在更新面板中的行为。
引言
当尝试创建基于 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 加载事件之后在某个位置调用它。