39

Yesterday I had an issue where a .on('click') event handler I was assigning wasn't working right. Turns out it's because I was was trying to apply that .on('click') before that element existed in the DOM, because it was being loaded via AJAX, and therefore didn't exist yet when the document.ready() got to that point.

I solved it with an awkward workaround, but my question is, if I were to put a <script> tag IN the ajax loaded content and another document.ready() within that, would that second document.ready() be parsed ONLY once that ajax content is done being loaded? In other words, does it consider that separately loaded ajax content to be another document, and if so, does having another document.ready() within that ajax-loaded HTML work the way I think it does?

Alternatively; what would be a better way to handle this situation? (needing to attach an event listener to a DOM element that doesn't yet exist on document.ready())

  • 1
    For event handlers, use event delegation. For anything else (plugin initialisation, for example) use the callback function for your AJAX call. – Anthony Grist Nov 27 '13 at 15:32

6 Answers6

35

To answer your question: No, document.ready will not fire again once a ajax request is completed. (The content in the ajax is loaded into your document, so there isn't a second document for the ajax content).

To solve your problem just add the event listener to the Element where you load the ajax content into it. For example:

$( "div.ajaxcontent-container" ).on( "click", "#id-of-the-element-in-the-ajax-content", function() {
  console.log($( this ));
});

For #id-of-the-element-in-the-ajax-content you can use any selector you would use in $("selector"). The only difference is, only elements under div.ajaxcontent-container will be selected.

How it works: As long as div.ajaxcontent-container exists all elements (if they exist now or only in the future) that match the selector #id-of-the-element-in-the-ajax-content will trigger this click-event.

nbar
  • 6,028
  • 2
  • 24
  • 65
  • 1
    Thanks! Most answers were the same but yours specifically answered my question and then went on to explain event delegation. Cheers! –  Nov 27 '13 at 16:01
  • 2
    Actually document.ready will be called in ajax-loaded HTML. Try to add into ajax-loaded page and you will see that message logged. But function.ready on main page is not fired after ajax.. just to be clear. – Vitaliy Markitanov Jan 12 '16 at 00:49
  • @VitaliyMarkitanov Can you please explain this _function.ready on main page is not fired after ajax_ – user1451111 Nov 23 '18 at 04:36
  • look at this psedo page $(readyParentWhenParenLoaded) $(readyChild) So when you load child page from parent via ajax call readyParentWhenParenLoaded will not be invoked, but readyChild will – Vitaliy Markitanov Nov 24 '18 at 14:15
  • Great answer, saved my time, because I didn't understand event dumping and bubbling at that time. The key feature of this approach is to add event handler to unremovable parent of dynamically loaded part of the DOM. – paulokunev Sep 09 '21 at 12:22
10

Javascript in the resulting ajax call will not be excecuted (by default) due to safety. Also, you can't directly bind event to non-existing elements.
You can bind an event to some parent that does exist, and tell it to check it's children:

$(document).ready(function(){
    $(document).on('eventName', '#nonExistingElement', function(){ alert(1); }
    // or:
    $('#existingParent').on('eventName', '#nonExistingElement', function(){ alert(1); }
});

Always try to get as close to the triggering element as you can, this will prevent unnessesary bubbling through the DOM


If you have some weird functions going on, you could do something like this:

function bindAllDocReadyThings(){
    $('#nonExistingElement').off().on('eventName', function(){ alert(1); }
    // Note the .off() this time, it removes all other events to set them again
}
$(document).ready(function(){
    bindAllDocReadyThings();
});
$.ajaxComplete(function(){
    bindAllDocReadyThings();
});
Martijn
  • 15,791
  • 4
  • 36
  • 68
  • This doesn't work without errors if you don't have the functions on all loaded pages. Therefore I suggest to add something like `$(document).ajaxStart(function() { delete window.bindAllDocReadyThings; });` and rather define the functions within the documents as `bindAllDocReadyThings = function () { ... }` – Smamatti Jul 14 '14 at 15:26
5

try this, that is not working because your control is not yet created and you are trying to attach a event, if you use on event it will work fine. let me know if you face any issues.

$(document).ready(function(){
    $(document).on('click', '#element', function (evt) {
        alert($(this).val());
    });
});
Mad Physicist
  • 107,652
  • 25
  • 181
  • 264
Rudresha Parameshappa
  • 3,826
  • 3
  • 25
  • 39
2

The answer here is a delegated event:

JSFiddle

JSFiddle - Truly dynamic

jQuery

$(document).ready(function(){
    // Listen for a button within .container to get clicked because .container is not dynamic
    $('.container').on('click', 'input[type="button"]', function(){
        alert($(this).val());
    });

    // we bound the click listener to .container child elements so any buttons inside of it get noticed
    $('.container').append('<input type="button" class="dynamically_added" value="button2">');
    $('.container').append('<input type="button" class="dynamically_added" value="button3">');
    $('.container').append('<input type="button" class="dynamically_added" value="button4">');
    $('.container').append('<input type="button" class="dynamically_added" value="button5">');
});

HTML

<div class="container">
    <input type="button" class="dynamically_added" value="button1">
</div>
MonkeyZeus
  • 20,375
  • 4
  • 36
  • 77
0

I'm working on a code-base with a friend that has a similar requirement. The delegated event handler option is definitely best if all you want is to attach event handlers. An alternative, especially if you need to do other DOM processing in your $(document).ready function, is to put the code you want run into a script element at the end of your code. Basically, instead of:

<script type="text/javascript">
  $(document).ready(function() {
    // Your code here
  });
</script>
<!-- rest of dynamically loaded HTML -->

Try swapping the script and the rest of the HTML around so you have:

<!-- rest of dynamically loaded HTML -->
<script type="text/javascript">
  // Your code here
</script>

This forces the browser to only process your code once it has loaded every other DOM element in the dynamically loaded HTML. Of course this means you'll have to make sure the inserted HTML does not have unintended UI consequences by using CSS/HTML instead of JS. Its an old Javascript trick from years gone by. As a bonus, you don't need jQuery for this anymore.

I should mention that in Chromium v34, putting a second $(document).ready call inside a <script> tag in the dynamically loaded HTML seems to wait for dynamically loaded DOM to load and then runs the function as you described. I'm not sure this behaviour is standard though as it has caused me great grief when trying to automate tests with this kind of code in it.

jeteon
  • 3,471
  • 27
  • 40
0

JQuery AJAX .load() has a built-in feature for handling this. Instead of simply $('div#content').load('such_a_such.url'); you should include a callback function. JQuery .load() provides room for the following:

$('div#content').load('such_a_such.url',
    { data1: "First Data Parameter",
      data2: 2,
      data3: "etc" },
    function(){ $('#span1').text("This function is the equivalent of");
                $('#span2').text("the $(document).ready function.");
    }
 );

However, you do not need to include the data argument.

$( "#result" ).load( "ajax/test.html", function() { alert( "Load was performed." ); });

http://api.jquery.com/load/

RoboticRenaissance
  • 1,130
  • 1
  • 10
  • 17