Monday, August 30, 2010

Learning:Restore Focus after TextBox Automatic Postbacks

Restore Focus after TextBox Automatic Postbacks



Introduction

Postbacks triggered by a DropDownList or a Check Box are hardly noticeable. However it’s a different story when an UpdatePanel refreshes its content after a partial automatic postback. Specifically, two side-effects will occur.
  • Loss of focus: None of the controls on the page will have the focus unless it is explicitly set in the server-side code.
  • Loss of data: Any data changes made between the moment when the postback is triggered and the moment when the postback is finished are wiped out.
For example, a user enters data in a Text Box and presses the Tab key to move to the next field and begins typing. Seconds later, the content in this text box disappears and the user is forced to reenter the data. In this article, I will show you how to save time and prevent the above scenario from happening.

Solution

The root cause of these issues is derived from the way an UpdatePanel is refreshed on partial postback. When an ASP.NET page receives the result of a partial postback, it clears out the current content on the UpdatePanel and resets the inner HTML to a newly received result. All of the DOM objects within the UpdatePanel are destroyed and recreated, resulting in the loss of focus and data. The best solution, of course, is to avoid postbacks in the first place. However, there are times when developers use postbacks for convenience instead of business logic. If the business logic of the postbacks requires no new data from database, (i.e., all information is already available on the client-side), then it can and should be handled with client-side JavaScript code.
However, if new data is required, there is an alternative solution in PageMethods, which is used by Iron Speed Designer’s popup text and image. PageMethods sends to and receives from the server much less data than partial postbacks. They apply the results to existing DOM objects instead of destroying and recreating them, thus preserving the focus and data.
If the two solutions above are not applicable to your page and postback is your only option, there is not much you can do to prevent data loss. The best you can do is to make the server process as fast as possible. However, you can prevent focus loss, or more precisely, restore focus with some JavaScript code.

Implementation

The following JavaScript code is modified from Yuriy Solodkyy’s blog.... Append it to the end of ~\ApplicationWebForm.js for existing projects. You may also append it to a code template in Iron Speed Designer’s installation folder here: \ProjectTemplates\vs200x\cs(vb)\ApplicationWebForm.js. This will ensure that changes are applied to future projects. JavaScript

var lastFocusedControlId = "";

function focusHandler(e) {
    document.activeElement = e.originalTarget;
}

function appInit() {
    if (typeof (window.addEventListener) !== "undefined") {
      window.addEventListener("focus", focusHandler, true);
    }
    Sys.WebForms.PageRequestManager.getInstance().add_pageLoading(pageLoadingHandler);
    Sys.WebForms.PageRequestManager.getInstance().add_pageLoaded(pageLoadedHandler);
}

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

function focusControl(targetControl) {
    if (Sys.Browser.agent === Sys.Browser.InternetExplorer) {
      var focusTarget = targetControl;
      if (focusTarget && (typeof (focusTarget.contentEditable) !== "undefined")) {
        oldContentEditableSetting = focusTarget.contentEditable;
        focusTarget.contentEditable = false;
      }
      else {
        focusTarget = null;
}
try {
          targetControl.focus();
          } catch (err) {}
      if (focusTarget) {
        focusTarget.contentEditable = oldContentEditableSetting;
      }
    }
    else {
        targetControl.focus();
    }
}

function pageLoadedHandler(sender, args) {
    if (typeof (lastFocusedControlId) !== "undefined" && lastFocusedControlId != "") {
      var newFocused = $get(lastFocusedControlId);
        if (newFocused) {
        focusControl(newFocused);
        }
    }
}
Next, in ~\App_Code\Shared\BaseApplicationPage.cs (vb), find the method Control_ClearControls_PreRender() and insert the commented code block “Register RestoreFocus.”
C#

protected void Control_ClearControls_PreRender(object sender, EventArgs e) {
    this.ClearControlsFromSession();

    // Register RestoreFocus
    if(!IsPostBack)
      ScriptManager.RegisterStartupScript(this, GetType(), "RestoreFocus",
        "Sys.Application.add_init(appInit);", true);
}
VB .NET

Protected Sub Control_ClearControls_PreRender(ByVal sender As Object, ByVal e As EventArgs) Me.ClearControlsFromSession()

    ' Register RestoreFocus
   If Not IsPostBack Then
    ScriptManager.RegisterStartupScript(Me, [GetType](), "RestoreFocus", _
        "Sys.Application.add_init(appInit);", True)
   End If
End Sub
Now all pages will return the focus to the previous owner before postbacks.

Conclusion

Text Box automatic postbacks may cause loss of focus and data. The best solution is to avoid them. But if using automatic postbacks is necessary, try restoring focus with JavaScript code.

No comments:

Post a Comment