Building Interactive User Interfaces with Microsoft ASP.NET AJAX: Rebinding Client-Side Events After a Partial Page Postback
By Scott Mitchell
Introduction
The UpdatePanel is the workhorse of the ASP.NET Ajax library. It is responsible for defining regions of a web page that trigger partial page postbacks (as opposed to full page postbacks). Such partial page postbacks transfer less information between the client and server and have their user interfaces updated seamlessly, thereby leading to a more interactive user experience. (For more information on UpdatePanels, refer to Using the UpdatePanel.) One side-effect of a partial page postback is that the HTML elements within the UpdatePanel are replaced with the markup returned on postback. This behavior is not noticeable and is not an issue unless you have client-side event handlers wired up to the elements within the UpdatePanel. Such client-side event handlers are lost after a partial page postback.
Consider a very simple UpdatePanel that contains just a TextBox and a Button. Furthermore, assume we have JavaScript on the page that creates an event handler for the
TextBox's focus and blur events, which "highlights" the TextBox when the user focuses it and unhighlights it when losing focus. Initially, this
script works as expected - clicking on the TextBox will "highlight" it. However, things break down once the Button is clicked. When the Button is clicked the UpdatePanel
triggers a partial page postback and submits an asynchronous HTTP request back to the server. The requested ASP.NET page then goes through its life-cycle again, but this time
only the markup in the UpdatePanel (and the hidden form fields on the page) are returned to the browser. The UpdatePanel then overwrites its existing markup with the
markup just returned from the server. Unfortunately, this overwriting obliterates the focus and blur client-side event handlers, meaning that
selecting the TextBox no longer highlights it.
In short, if there are client-side event handlers attached to HTML elements within an UpdatePanel it is imperative that they be rebound after a partial page postback. This article looks at three different ways to accomplish this. Read on to learn more!
Exploring the Problem
As noted in the introduction, client-side event handlers for HTML elements in an UpdatePanel are lost after a partial page postback. In this article we'll identify three different approaches for overcoming this obstacle, but before we do let me take a moment to explain the problem we are trying to solve in more detail to make sure we are all on the same page. (The code samples provided here are taken from a sample application I built, which you can download at the end of this article.)
Imagine that you have a web page that uses a bit of JavaScript to highlight form fields like textboxes, drop-down lists, and checkboxes whenever a user selects the
form field and removes the highlighting once focus has been lost. This functionality can be accomplished by adding the following JavaScript to your page's
<head> section:
<script type="text/javascript" src='<%=Page.ResolveClientUrl("~/Scripts/jquery-1.4.2.min.js") %>'></script>
|
The above script starts makes use of the jQuery library, which is a lightweight, cross-browser JavaScript library designed to ease
JavaScript's most common tasks, including inspecting and manipulating the Document Object Model (DOM). Note that the first <script> element above
starts by referencing the file where this library exists (~/Scripts/jquery-1.4.2.min.js). Next, the script defines two event handler functions named
HighlightFormField and UnhighlightFormField, which add and remove the highlightFormField CSS class from the HTML element that
raised the event. The $(document).ready event handler, which fires after the browser has loaded the DOM, wires up the all textboxes (input[type=text]),
drop-down lists (select) and multi-line textboxes (textarea) focus and blur events to the HighlightFormField
and UnhighlightFormField event handlers, respectively.
With the above JavaScript in place, anytime a user selects a textbox or drop-down list it has the highlightFormField CSS class applied. The
highlightFormField CSS class (not shown here) indicates a light-yellow background color and a dashed border. The screen shot below shows the page
when visited through a browser and after clicking on the first textbox, which now appears highlighted.
In the above user interface there is a DropDownList that asks the user if they want to receive the newsletter. If the user chooses Yes we might want to show a textbox to collect their email address. This could be done by adding a Panel to the page that contains a TextBox control to collect the email address.
<asp:Panel runat="server" ID="pnlNewsletterFollowup" Visible="false">
|
Next, the DropDownList control's AutoPostBack property would need to be set to True so that we could determine when the DropDownList was modified and display
(or hide) the email Panel accordingly. We'd also create a server-side event handler for the DropDownList's SelectedIndexChanged event that would show or hide
the Panel as needed.
protected void ddlGetNewsletter_SelectedIndexChanged(object sender, EventArgs e)
|
With the above modifications everything would continue to work as expected presuming that the user interface was not in an UpdatePanel. If changing the DropDownList's
value caused a full postback to fire then changing the DropDownList would reload the entire page. On postback the $(document).ready event handler would
execute and the client-side event handlers to highlight the form fields on the page would be established.
However, if we put the above user interface within an UpdatePanel then things break down. When the page is first visited the $(document).ready event handler
executes and creates the client-side event handlers - selecting a textbox highlights it as expected. But when the user makes a selection from the drop-down list there would
be a partial page postback. On partial page postback, the UpdatePanel overwrites its existing markup with the markup returned from the server (which now includes the
textbox for the user's email address), but doing so severs the client-side event handlers. Consequently, after a partial page postback the form fields are no longer
highlighted upon selection.
So how do we correct this behavior? In short, we need the client-side event handlers to be reestablished after the partial page postback completes. There are many ways this can be accomplished. Let's look at three such ways.
Using the PageRequestManager Object's endRequest Event Handler
In Performing Client Actions in Response to Partial Page Postbacks, a previous article in this series, we learned that whenever a partial page postback is triggered the
PageRequestManager
object manages the interaction, from dispatching the HTTP request back to the server to receiving to response and updating the user interface. During the life-cycle
of a partial page postback, the PageRequestManager object performs the following client-side actions:
- The
initializeRequestevent is raised - this is the first event raised during the partial postback life-cycle and affords us an opportunity to determine the HTML element that triggered the postback and cancel the postback, if needed. - The
beginRequestevent is raised - this event is raised just before the request is sent to the server. The UpdateProgress control uses this event to displayed it's output. - The request is sent to the server and the page is re-rendered there.
- The
pageLoadingevent is raised when the server returns its response. - The
pageLoadedevent is raised. This event is raised whenever the content on the page is refreshed, be it via a full page postback or a partial page postback. - The
endRequestevent is raised, signalling completion of the partial page postback lifecycle.
endRequest event is raised, signalling the completion of the partial page postback. After the partial page postback has completed
the UpdatePanel's user interface has been overwritten and the client-side event handlers that were associated with the HTML elements in that user interface have been
lost. This, then, is a great time to add those event handlers back in.
To accomplish this we need to do two things:
- Move the code that adds the client-side event handlers to the page's textboxes and drop-down lists from the
$(document).readyevent handler to a new function (let's call itWireUpFormFields). We then need to call this new function from$(document).ready. - Create an event handler for the
PageRequestManagerobject'sendRequestevent that calls theWireUpFormFieldsfunction.
Here's the updated <script> tag that goes in the <head> section. Note that the client-side event wiring code has been moved to a new
function, WireUpFormFields, and that the $(document).ready event handler calls this function.
<script type="text/javascript">
|
Next, we need to have the WireUpFormFields function called whenever the endRequest event fires. This code must appear after the
ScriptManager control, so don't put it in the <head> section. Instead, add this <script> block at the end of the page (or anywhere
else after the ScriptManager control).
<script type="text/javascript">
|
Using the UpdatePanel's Server-Side Load Event Handler
As we just saw, when a partial page postback occurs a number of client-side events fire. There are also server-side events that fire. For example, on each request to the web page - whether its the initial visit, a partial page postback, or a full page postback - the UpdatePanels on the page have their
Load events fire.
We can create a server-side Load event handler that programmatically injects JavaScript that wires up the client-side event handlers for the controls in the
UpdatePanel.
The code for the UpdatePanel's Load event handler would look similar to the following. Note that I am using the ScriptManager object's
RegisterStartupScript method to programmatically inject JavaScript into the page. In short, I'm saying, "Whenever the UpdatePanel is loaded, call the
WireUpFormFields client-side function."
protected void upDataEntry_Load(object sender, EventArgs e)
|
Using jQuery's live() Function
If you are using jQuery, as I did in these examples, there's an even easier approach - the
live() function. The
live() function defines an event handler for one or more HTML elements now or in the future. What that means is that you can say things like, "Hey, jQuery,
I want you to affix this event handler to the click event of any <a> element on the page, whether it exists now or at some future point."
Consequently, if an <a> element is dynamically added to the page at a later time, jQuery will automatically wire up its click event to
the specified event handler.
For our purposes we want to wire up the HighlightFormField and UnhighlightFormField event handlers to the focus and blur
events of the textboxes and drop-down lists on the page now or in the future. The "in the future" part ensures that after a partial page postback, when the original
textboxes and drop-down list are replaced with new ones, those new form fields will automatically be configured to have the client-side event handlers we had initially
defined when the page was first loaded.
Using the live() function is pretty straightforward - just specify the event name (as a string) and the event handler, as the following script shows:
<script type="text/javascript">
|
Using the live() function is probably the easiest and most intuitive approach, but it presumes that you are using jQuery. If you are not using jQuery you'll
need to rely on one of the two other techniques covered in this article.
Happy Programming!
Attachments:
Further Reading
$(document).ready and UpdatePanels?$(document).ready() and pageLoad() are not the same!