views:

14833

answers:

7

I'm using jQuery to wire up some mouseover effects on elements that are inside an UpdatePanel. The events are bound in $(document).ready . For example:

$(function() {

    $('div._Foo').bind("mouseover", function(e) {
        // Do something exciting
    });

});

Of course, this works fine the first time the page is loaded, but when the UpdatePanel does a partial page update, it's not run and the mouseover effects don't work any more inside the UpdatePanel.

What's the recommended approach for wiring stuff up in jQuery not only on the first page load, but every time an UpdatePanel fires a partial page update? Should I be using the ASP.NET ajax lifecycle instead of $(document).ready?

+6  A: 

I would use one of the following approaches:

  1. Encapsulate the event binding in a function and run it every time you update the page. You can always contain the event binding to specific elements so as not to bind events multiple times to the same elements.

  2. Use the livequery plug-in, which basically performs method one for you auto-magically. Your preference may vary depending on the amount of control you want to have on the event binding.

Eran Galperin
+66  A: 

An UpdatePanel completely replaces the contents of the update panel on an update. This means that those events you subscribed to are no longer subscribed because there are new elements in that update panel.

What I've done to work around this is re-subscribe to the events I need after every update. I use $(document).ready() for the initial load, then this snippet below to re-subscribe every update.

var prm = Sys.WebForms.PageRequestManager.getInstance();

prm.add_endRequest(function() {
    // re-bind your jquery events here
});

The PageRequestManager is a javascript object which is automatically available if an update panel is on the page. You shouldn't need to do anything other than the code above in order to use it as long as the UpdatePanel is on the page.

If you need more detailed control, this event passes arguments similar to how .NET events are passed arguments (sender, eventArgs) so you can see what raised the event and only re-bind if needed.

Read more about the RequestManager here: asp.net/.../UpdatePanelClientScripting.aspx (If using .NET 2.0)

Here is the latest version of the documentation from Microsoft: msdn.microsoft.com/.../bb383810.aspx


One other option you may have, depending on your needs is to use jQuery's live() event subscriber, or the jQuery plugin livequery. These methods are more efficient than re-subscribing to DOM elements on every update. Read all of the documentation before you use this approach however, since it may or may not meet your needs. There are a lot of jQuery plugins that would be unreasonable to refactor to use live(), so in those cases, you're better off re-subscribing.

Dan Herbert
Thanks for this answer. Its helped me too
Scott
Thank you! this helped me as well!
Jason
oh my goodness sweet lord this is amazing.... i can't upvote this enough times. i will just comment again instead :)
Jason
Can't thank you enough for this. I wish I would have found this post about 3 hours ago.
ryanulit
+1: helfpul for all the background, although the answer below from Barbaros actually gave me easier steps to getting to the solution. ;)
Brian MacKay
+1 enjoy that Guru badge :)
Matthew Jones
+2  A: 

I had a similar problem and found the way that worked best was to rely on Event Bubbling and event delegation to handle it. The nice thing about event delegation is that once setup, you don't have to rebind events after an AJAX update.

What I do in my code is setup a delegate on the parent element of the update panel. This parent element is not replaced on an update and therefore the event binding is unaffected.

There are a number of good articles and plugins to handle event delegation in jQuery and the feature will likely be baked into the 1.3 release. The article/plugin I use for reference is:

http://www.danwebb.net/2008/2/8/event-delegation-made-easy-in-jquery

Once you understand what it happening, I think you'll find this a much more elegant solution that is more reliable than remembering to re-bind events after every update. This also has the added benefit of giving you one event to unbind when the page is unloaded.

Joe Brinkman
+2  A: 

FWIW, I experienced a similar issue w/mootools. Re-attaching my events was the correct move, but needed to be done at the end of the request..eg

var prm = Sys.WebForms.PageRequestManager.getInstance();
prm.add_endRequest(function() {...

Just something to keep in mind if beginRequest causes you to get null reference JS exceptions.

Cheers

+12  A: 
<script type="text/javascript">

        function BindEvents() {
            $(document).ready(function() {
                $(".tr-base").mouseover(function() {
                    $(this).toggleClass("trHover");
                }).mouseout(function() {
                    $(this).removeClass("trHover");
                });
         }
</script>

The area which is going to be updated.

<asp:UpdatePanel...
<ContentTemplate
     <script type="text/javascript">
                    Sys.Application.add_load(BindEvents);
     </script>
 *// Staff*
</ContentTemplate>
    </asp:UpdatePanel>
Barbaros Alp
Exactly what I've been searching for. Thanks.
cruster
+1: This worked perfectly for my situation.
Brian MacKay
Great solution and worked perfectly.
Swoop
I just found this a second time two months later and used it again. I wish I could upvote twice.
Brian MacKay
+11  A: 

Upgrade to jQuery 1.3 and use:

$(function() {

    $('div._Foo').live("mouseover", function(e) {
        // Do something exciting
    });

});

Note: live works with most events, but not all. There is a complete list in the documentation.

svinto
Using live fixes the issue with update panels. No hoops needed to jump through.
Tim
+3  A: 

User Control with jQuery Inside an UpdatePanel

This isn't a direct answer to the question, but I did put this solution together by reading the answers that I found here, and I thought someone might find it useful.

I was trying to use a jQuery textarea limiter inside of a User Control. This was tricky, because the User Control runs inside of an UpdatePanel, and it was losing its bindings on callback.

If this was just a page, the answers here would have applied directly. However, User Controls do not have direct access to the head tag, nor did they have direct access to the UpdatePanel as some of the answers assume.

I ended up putting this script block right into the top of my User Control's markup. For the initial bind, it users $(document).ready, and then it uses prm.add_endRequest from there:

<script type="text/javascript">
    function BindControlEvents() {
        //jQuery is wrapped in BindEvents function so it can be re-bound after each callback.
        //Your code would replace the following line:
            $('#<%= TextProtocolDrugInstructions.ClientID %>').limit('100', '#charsLeft_Instructions');            
    }

    //Initial bind
    $(document).ready(function () {
        BindControlEvents();
    });

    //Re-bind for callbacks
    var prm = Sys.WebForms.PageRequestManager.getInstance(); 

    prm.add_endRequest(function() { 
        BindControlEvents();
    }); 

</script>

So... Just thought someone might like to know that this works.

Brian MacKay