jQuery .on() method doesn't see new elements

JavascriptJqueryEvent HandlingClickJquery Events

Javascript Problem Overview


I'm getting a JSON element and building a list from its items like this:

getTitles: function(data) {
    data = data || {};
    var list = [];
    
    $.getJSON(
        '/titles',
        data,
        function(data) {
            $.each(data.data, function(key, val) {
                list.push(
                    '<li><a href="'+ val.href +'">'+ val.title +'</a><span class="count">'+ val.count +'</span></li>'
                )
            });
            
            $('#title-items').html(list.join(''));
        }
    );
}

And I'm binding click event for a elements like this:

$('a').on('click', function(e) {
    alert('clicked');
    e.preventDefault();
});

Old a elements shows alert but new ones follow URL. Event handler doesn't work for new ones. How can I solve this?

Javascript Solutions


Solution 1 - Javascript

You are not using the correct code to get live functionality.

$('#title-items').on('click', 'a', function(e) {
    alert('clicked');
    e.preventDefault();
});
  1. First, select your common ancestor element (#title-items in this example). You can use document here too if you want to handle all a elements.
  2. Pass the event type (on), then the sub selector (a), and then the callback function for the event.

Now, when click events bubble up to #title-items, it will check to see if the element is an a element, and if so, fire the callback.

Solution 2 - Javascript

You want to use event delegation to capture events triggered on events that are present in the DOM at any point in time:

$(<root element>).on('click', 'a', function(e) {
    alert('clicked');
    e.preventDefault();
});

UPDATE - In this example the <root element> is an ancestor of the links to which you are binding that is present in the DOM at the time of binding.

The basic idea is that since we can't attach an event handler to a DOM element not yet in the DOM, we attach the event handler to an ancestor element and wait for the event to bubble up to the ancestor element. Once the event reaches the ancestor event the event.target property is checked to see what was the originally clicked element.

Solution 3 - Javascript

You can use the arrive.js library (it uses MutationObserver internally).

document.arrive('a', function(){
    // 'this' refers to the newly created element
    var newElem = this;
});

Solution 4 - Javascript

In regards to the accepted answer, note that if you'll use a as a jQuery object vs a string - if you'll make a mistake in the jQuery selector - the event will always be fired when #title-items is clicked ( no matter of a / p etc .. )

var a_links1 = $('a');
var a_links2 = $('bla');

$('#title-items').on('click', a_links1, function(e) {
    alert('clicked');   <= fire only on <a> elements
});

$('#title-items').on('click', a_links2, function(e) {
    alert('clicked');   <= fire on every element under <#title-items>
});

That is unless you'll use strings

$('#title-items').on('click', 'a', function(e) {
    alert('clicked');   <= fire only on <a> elements
});

$('#title-items').on('click', 'bla', function(e) {
    alert('clicked');   <= won't fire at all, as the selector won't match anything
});

jsFiddle: https://jsfiddle.net/7f8y5ak6/1/

It is important to understand that, because when you're expecting $('bla') to be added in the future ( or after some click ) - but you loaded the bind event before that - jQuery's selector won't "find"/"see" them - then you're expected this behavior when everything is clickable.

Solution 5 - Javascript

This one worked for me https://www.learningjquery.com/2017/02/jquery-on-method-the-issue-of-dynamically-added-elements

jQuery .on() method used as direct event will not work for dynamically added elements. Use delegate event via passing the selector parameter so that container takes the responsibility of executing the event in case of elements added in future dynamically.

You can keep .on inside or outside of $(document).ready both logs will work!

For custom tag

$(document).on("click","[mycustomtag]", function(){
    console.log("Clicked.");
});

for HTML classes or id's etc

$(document).on("click","div", function(){
    console.log("Clicked.");
});

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestioncnktView Question on Stackoverflow
Solution 1 - JavascriptalexView Answer on Stackoverflow
Solution 2 - JavascriptJasperView Answer on Stackoverflow
Solution 3 - JavascriptUzair FarooqView Answer on Stackoverflow
Solution 4 - JavascriptRicky LeviView Answer on Stackoverflow
Solution 5 - JavascriptMangesh SatheView Answer on Stackoverflow