Jquery Event won't fire after ajax call

86,940

Solution 1

When you remove an element and then replace it (via javascript), it loses any event bindings that were added to it on page load.

(This also applies to content added to the page after page load - i.e. ajax loaded content)

There are several possible solutions for this.

1) Encapsulate your "binding" code and call it both on page load and immediately after the element in question gets added back to the page. For example:

$(document).ready(function(){
    // bind event handlers when the page loads.
    bindButtonClick();
});

function bindButtonClick(){
    $('.myClickableElement').click(function(){
        ... event handler code ...
    });
}

function updateContent(){
    $.ajax({
        url : '/ajax-endpoint.php',
        data : {'onMyWay' : 'toServer'},
        dataType : 'html',
        type : 'post',
        success : function(responseHtml){
            // .myClickableElement is replaced with new (unbound) html element(s)
            $('#container').html(responseHtml);

            // re-bind event handlers to '.myClickableElement'
            bindButtonClick();  
        }
    });
}

2) The more elegant way to handle this: use jQuery's .on() method. With it, you are able to bind event handlers to elements other than the event target - i.e. an element that never gets removed from the page.

$(document).ready(function(){
    $('body').on('click','.myClickableElement',function(){
        ... event handler code ....
    });
});

Some further explanation:

The .on() method uses event delegation to tell a parent element to retain your event handler code (3rd argument), and fire it when the event target (2nd argument) has a certain type of event (1st argument) performed on it.

If you are using a version of jQuery prior to 1.7 use the now deprecated delegate method which essentially does the same thing.

Also, it is worth noting that because of the way events "bubble up" through the dom tree, the event target (2nd argument of .on() method) must be a descendant of the delegating element (jQuery object's selector). For example, the following would NOT work

<div id="container-1">
    <div>
        <div id="another-div">
            Some Stuff
        </div>
    </div>
</div>

<div id="container-2">
    <a id="click-me">Event Target!!!</a>
</div>

<script type="text/javascript">
    $('#container-1').on('click','#click-me',function(){
        ... event handler code ....
        // This will never execute, should've used '#container-2', or 'body', or 'document' instead of '#container-1'
    });
</script>

The body or document elements are usually safe choices since typically every element on the page is a descendant.

Solution 2

You can enclose the event script in DIV and run a Replaceall command after dynamically loading the content.

<div class="somescript">
-- Event Script that you want to map with dnamically added content
<div>

-- Dynamically load your content and run the below command after it.

$(".somescript").replaceAll($(".somescript"));

Once the dynamically loaded content is loaded and replace command has been run, the events will be mapped and code will run fine.

Share:
86,940
Matthew Colley
Author by

Matthew Colley

I am currently a PHP developer working with Magento 1 and 2 and Commerce Cloud.

Updated on July 05, 2022

Comments

  • Matthew Colley
    Matthew Colley almost 2 years

    This is a rather strange issue with jquery. I am loading a div

    <div id="container">
    

    on page load. Each record is tabular data with a 'delete' ajax function associated with it. When the page loads and clicking the 'delete' link, the ajax call fires off just fine. However, once the event is fired, the data is returned from the ajax call, and the div is populated with data (but the page does not refresh or reload) When I click the link again, the ajax script will not fire. Here is my code:

    $(document).ready(function() {
        $("button.done").button({
        }).click(function() {
            var BatchID = $("input#BatchID").val();
            var Amount = $("input#Amount").val();
            var Name = $("input#CheckName").val();
            var Check_Number = $("input#Check_Number").val();
            var Company = $("select#Company").val();
            var Department = $("select#Department").val();
            $.ajax({
                type: 'GET',
                url: '/app/usagCheckEntryWS.html',
                data: {
                    "BatchID" : BatchID,
                    "Amount" : Amount,
                    "Name" : Name,
                    "Check_Number" : Check_Number,
                    "Company" : Company,
                    "Department" : Department
                },
                success: function (data) {
                        var ang = '';
                        var obj = $.parseJSON(data);
                        $.each(obj, function() {
                           ang += '<table><tr><td width="45">' + this["RefID"] + '</td><td width="140">' + this["Name"] + '</td><td width="95">' + this["CheckNumber"] + '</td><td align="right" width="70">$' + this["Amount"] + '</td><td width="220" style="padding-left: 15px;">' + this["Description"] + '</td><td><div class="delete" rel="' + this["RefID"] + '"><span>Delete</span></div></td></tr></table>';
                        });
                        $('#container').html(ang);
                        $("input#Amount").val('');
                        $("input#CheckName").val('');
                        $("input#Check_Number").val('');
                        $("select#Company").val('MMS');
                        $("th#dept").hide();
                        $('input#CheckName').focus();
                }
            });
        });
    });
    

  • Arnaud Bouchot
    Arnaud Bouchot over 6 years
    You - are - The - Man! I met this issue when replacing parts of the dom using socket.io events, My bootstrap carousel would not trigger events I need when slides finished scrolling, I have wrapped my event driven js in the bindsomething() functions as you described and it's all running fine now, Thank you!
  • Charles Robertson
    Charles Robertson about 5 years
    Wow. Excellent solution. I have had a problem with SoundCloud auto play in Chrome, due to Google's new Auto Play policy. This manages to fix the problem. I have added a flag, so that when the window loads, the bindButtonClick function is passed 0, so the event handler is prevented from being executed. But, inside the Ajax call, I pass it 1, and the event handler kicks in!
  • SScotti
    SScotti about 5 years
    Just a question and a comment, although I see that this is an old post. I have had a lot of trouble using function submitorderhandler() { $(document).on('click', '#xxx", function(e) {
  • Speuline
    Speuline almost 5 years
    Oh my god.... I was struggling with this... I added a ajaxComplete function to handle it, and also a check if the eventhandler existed already and I still had issues. You saved my day!
  • Augusto Samamé Barrientos
    Augusto Samamé Barrientos over 4 years
    where's the option to buy a virtual beer in this thing?
  • Jess
    Jess about 4 years
    I've heard that having lots of document or body level event handlers can slow down a page.
  • Jason Fingar
    Jason Fingar about 4 years
    @Jess can't say I've ever noticed that, doesn't mean it isn't true. I will say that this answer is really old now and while it may still be relevant to those using this approach, I would recommend folks to take a modern approach (using component-driven frameworks or libraries) for any client-side apps with any sort of complexity involved. Usually those frameworks are not using jQuery at all for event binding
  • Nico Haase
    Nico Haase over 3 years
    Please add some explanation to your answer such that other can learn from it. What makes you think that any handlers bound to elements within that div are still bound after replacing the content?
  • Amit kumar
    Amit kumar about 2 years
    Thank you for this brief explanation especially 2nd point, until now i thought i knew .on() and how it functions lol