jQuery $(document).ready() and ASP.NET Ajax asynchronous postback

by Arnold Matusz 24 2 2009

I’ve been a bit skeptic about the thought of combining ASP.NET Ajax with jQuery, partly because I didn’t really know what the impact was going to be. But jQuery is very fast, very appreciated throughout general opinion, there are extremely many plugins available for free, writing your own plugins only requires little JavaScript knowledge (opposed to writing your own extender in .NET, like controls in the AjaxControlToolkit).

To be sincere, there is a very short learning curve so there is no reason for not trying jQuery. I’ve tried with regular web applications, but when I coupled jQuery with ASP.NET Ajax on little niggle stood out.

Many call it the ASP.NET jQuery postback problem, but using the technique below should make it no problem anymore. $(document).ready() isn’t called after an asynchronous postback. What this means? You lose the functionality that should be executed within $(document).ready() after an UpdatePanel rendered its contents after an asynchronous postback.

But it’s not difficult to solve this issue! Microsoft AJAX Library contains an event called: EndRequest which can be handled with JavaScript code. What actually happens in this situation is that a piece of JavaScript codes is executed whenever an asynchronous postback has ended. If we execute our logic from $(document).ready() inside the EndRequest handler we are good to go :).

As a short example I came up with the following:
- there is a TextBox on a page, and a Button (both inside an UpdatePanel)
- whenever the user pushes the Button, I fill out the textbox with the current server time
- there is a script tag on the page containing some JavaScript code to display an alert whenever the $(document).ready() has been executed.

The server side controls needed for this example are:

    <asp:ScriptManager ID="sm" runat="server">
        <Scripts>
            <asp:ScriptReference Path="~/Javascript/jquery-1.3.2.js" />
        </Scripts>
    </asp:ScriptManager>
    <asp:UpdatePanel ID="uppnl" runat="server">
        <ContentTemplate>
            <asp:TextBox ID="txtTime" runat="server"></asp:TextBox>
            <asp:Button ID="btnTime" runat="server" Text="Get current time" OnClick="btnTime_Click" />
        </ContentTemplate>
    </asp:UpdatePanel>

In codebehind you only need the click event handler for the Button:

    protected void btnTime_Click(object sender, EventArgs e)
    {
        txtTime.Text = DateTime.Now.ToLongTimeString();
    }

Including the following JavaScript code in your page reveals the actual problem:

<script type="text/javascript">
    function alertTest() {
        $(document).ready(function() {
            alert('$(document).ready() has been called');
        });
    }

    alertTest();
</script>

The alert is shown only when you load the page, this of course would also happen after a full postback. Everything inside the $(document).ready() function will be called as soon as the DOM is ready to be manipulated. The actual problem is that we are still interested to execute the code inside $(document).ready() after an asynchronous postback using the standard ASP.NET UpdatePanel control.

The solution comes in the form of some additional JavaScript code:

<script type="text/javascript">
    Sys.WebForms.PageRequestManager.getInstance().add_endRequest(EndRequestHandler);
function EndRequestHandler(sender, args) { if (args.get_error() == undefined) { alertTest(); } } function alertTest() { $(document).ready(function() { alert('$(document).ready() has been called'); }); } alertTest(); </script>

This way, on each postback, on each asynchronous postback the JavaScript code will still be called. Why this is needed? Because in many cases you’ll need to bind jQuery plugins to controls that are being updated inside UpdatePanels.

Now you can still enjoy ASP.NET Ajax with the simplicity of the UpdatePanel, and you can still enjoy jQuery with all it’s speed and performance and plugins and all.

If anybody needs help with this issue I’ll gladly help!

DZone it Digg it Submit to StumbleUpon Submit to Technorati Submit to reddit.com Submit to del.icio.us Submit to NewsVine Submit to Furl Submit to BlinkList

Tags: , , , , ,

jQuery | ASP.NET | AJAX | .NET | JavaScript

Comments

2/24/2009 10:37:48 PM #

trackback

jQuery $document.ready() and ASP.NET Ajax asynchronous postback

You've been kicked (a good thing) - Trackback from DotNetKicks.com

DotNetKicks.com |

2/24/2009 10:39:37 PM #

trackback

jQuery $document.ready() and ASP.NET Ajax asynchronous postback

You are voted (great) - Trackback from Web Development Community

Web Development Community |

2/24/2009 10:42:02 PM #

trackback

jQuery $document.ready() and ASP.NET Ajax asynchronous postback

Thank you for submitting this cool story - Trackback from DotNetShoutout

DotNetShoutout |

2/24/2009 10:45:49 PM #

trackback

jQuery $document.ready() and ASP.NET Ajax asynchronous postback

DotNetBurner.com - news and articles about .net DotNetBurner

jQuery $document.ready() and ASP.NET Ajax asynchronous postback - DotNetBurner |

2/25/2009 9:28:47 AM #

r.owliaei

take a look at
www.elemenex.com/index.php
a real simple example but it's useful.

r.owliaei United States |

2/26/2009 8:37:31 AM #

pyccki

Pretty Cool.

Thanx

pyccki United States |

2/26/2009 12:42:01 PM #

curlyfro

you should be able to solve this with the new jQuery 1.3 .live() or the livequery plugin.

curlyfro United States |

2/27/2009 11:44:38 AM #

Adam

Great article!  I have one question.  Why are you calling $(document).ready inside the alertTest() function?  Couldn't the same be accomplished without that?  For example, couldn't alertTest() be as simple as just this? :

function alertTest() { alert('$(document).ready() has been called'); }

You currently call alertTest() from within the document.ready - you also call it on the EndRequestHandler - but then alertTest() calls document.ready also.  Seems redundant, but maybe I'm missing something Smile

Thanks!


Adam United States |

2/27/2009 4:26:07 PM #

ranomore

Thanks for this!

ranomore United States |

3/1/2009 6:50:02 AM #

Walter Lussi

The more flexible and dynamic things get, the more error proned they get.
I'd rather use a little more coding instead of sheer undebuggable coding.

Walter Lussi Switzerland |

3/1/2009 6:26:25 PM #

Dave Ward

Look into the pageLoad() shortcut for Application.Load.  That's a bit more straightforward.

Dave Ward United States |

3/1/2009 6:56:54 PM #

Arnold Matusz

You currently call alertTest() from within the document.ready - you also call it on the EndRequestHandler - but then alertTest() calls document.ready also.


@Adam - you are completely right about this, you definitely haven't missed anything. The real reason why this remained in this form is that I had a comment within that $(document).ready() which stated: //insert any other js code here. I had some other js calls there which I've deleted for the sake of this example, and I've probably deleted the comment aswell.

Thanks allot for your comment Adam.

Arnold Matusz Romania |

3/1/2009 7:07:42 PM #

Arnold Matusz

The more flexible and dynamic things get, the more error proned they get.
I'd rather use a little more coding instead of sheer undebuggable coding.


Yes, I'ld completely agree with this, but there are several situations where the usage of jQuery comes in very handy. I'll give a short example: Implementing a GridView with a CheckBox on each row and in the Header with the selection options: select all, deselect all in the header and deselect header if not all checkboxes are selected, etc.. This is somehow a standard behaviour that one would want to achieve, but sincerely the easiest way to do it is with jQuery.

You can also choose to solve this problem with codebehind programming and ASP.NET Ajax but that is painfully slow compared to what jQuery offers you for this particular solution.

This post serves those who got into trouble with running a particular piece of code, after there was an ASP.NET Ajax asynchronous postpack.

Regarding undebuggability, well you are right, in very complex scenarios bugs are a bit more difficult to catch. But you know, there is no upside without a downside.

Arnold Matusz Romania |

3/1/2009 7:35:34 PM #

Arnold Matusz

Look into the pageLoad() shortcut for Application.Load.  That's a bit more straightforward.

Doing it David Ward's way would look like this:
        
Sys.Application.add_load(applicationLoadHandler);
function applicationLoadHandler() {
    alertTest();
}

Arnold Matusz Romania |

3/1/2009 7:37:50 PM #

Arnold Matusz

Or even

function pageLoad() {
    alertTest();
}

Arnold Matusz Romania |

4/23/2009 7:37:14 AM #

Geoff Rayback

Thanks for this!  I was encountering this error and had diagnosed it correctly, but couldn't quite get my head around what needed to happen to solve it.  Thanks for your concise treatment of the subject.

Geoff Rayback United States |

4/29/2009 6:24:08 AM #

andres

Excelente respuesta, very thanks!!

andres Colombia |

6/3/2009 9:02:32 AM #

sia nada

This worked like a charm! Awesome work!

sia nada United States |

6/30/2009 10:05:08 AM #

Gero

Thank you for your article. It's helped me.

Gero Ukraine |

8/31/2009 2:13:31 AM #

Pavan

Thank you for your article. I have an usercontrol within that i have written simple $(document).ready(function() {  that reads a textbox(placed in usercontrol) value and shows that value as a alert. Now i placed two instances of this user controls on the aspx page within the same updatepanel. whenever there is asynchronous postback i want to dispaly the values of each text box inside each usercontrol as an alert.  i mean for every asynchronous postback i want to execute the code inside the $(document).ready(function() { for each instance of the user control, and want to display the values of both textboxes as alerts, is there anyway to do so?

Pavan India |

8/31/2009 2:28:05 AM #

Arnold Matusz

Hi

The problem you are having here is that you hardcode the function name that is called after the asynchronous postback. (I.E. when the page content is rendered, Sys.WebForms.PageRequestManager.getInstance().add_endRequest(EndRequestHandler) , will apear twice because you have two user controls on that page, but because you redefine EndRequestHandler twice, only the last EndRequestHandler function will be taken into consideration).

You can deal with this issue in 2 ways:
1) the easiest way is to create your $(document).ready functionality outside the usercontrol so you only define your EndRequestHandler once. (to access the textbox you will need to use some sort of CssClass name to identify it, but this is not difficult - there are other ways aswell). I recomend this way in the case where you have more user controls on a page  (there is no need to create EndRequestHandler1-15 if you have 15 user controls on the page).
2) the elegant way would be to add some sort of identifier to the function header which would uniquely link the EndRequestHandler to that usercontrol.
You could do this in the following way:

Sys.WebForms.PageRequestManager.getInstance().add_endRequest(EndRequestHandler_<%= this.UniqueID %>);
function EndRequestHandler_<%= this.UniqueID %>() {
        // YOUR LOGIC HERE
}

At runtime the special markup is converted into some text, which will then uniquely identify each user controls.

I hope this helps you, if not, don't hesitate to contact me.

Arnold Matusz Romania |

8/31/2009 9:55:08 PM #

Pavan

Thank you very much for clarifying my doubt.

Pavan India |

11/1/2009 7:42:49 PM #

trackback

jQuery and ASP.NET PostBack

jQuery and ASP.NET PostBack

Tech Guru |

2/21/2010 10:28:01 PM #

Madhanlal JM

Thank you very much for the article.

Madhanlal JM India |

5/18/2010 6:22:01 AM #

steve

great post ! Thanks !
At the moment i have exactly this problem.
Could bang my head against the wall because I could not find a solution for this untill I saw your post.
thanks a lot...

steve Germany |

9/2/2010 6:10:17 AM #

srini

Great Post ! Thanks!
           It helped me to fix issues cased by the asynchronous postback.
Thanks

srini United States |