565
<html>
<head>
    <script type="text/javascript" src="jquery.js"></script>
    <script type="text/javascript">

        $(document).ready(function() {

            $("button").click(function() {
                $("h2").html("<p class='test'>click me</p>")
            });   

            $(".test").click(function(){
                alert();
            });
        });

    </script>
</head>
<body>
    <h2></h2>
    <button>generate new element</button>
</body>
</html>

I was trying to generate a new tag with class name test in the <h2> by clicking the button. I also defined a click event associated with test. But the event doesn't work.

Can anyone help?

Cᴏʀʏ
  • 105,112
  • 20
  • 162
  • 194
cheng
  • 6,596
  • 6
  • 25
  • 27

20 Answers20

800

The click() binding you're using is called a "direct" binding which will only attach the handler to elements that already exist. It won't get bound to elements created in the future. To do that, you'll have to create a "delegated" binding by using on().

Delegated events have the advantage that they can process events from descendant elements that are added to the document at a later time.

Source

Here's what you're looking for:

var counter = 0;

$("button").click(function() {
    $("h2").append("<p class='test'>click me " + (++counter) + "</p>")
});

// With on():

$("h2").on("click", "p.test", function(){
    alert($(this).text());
});
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.8.3/jquery.min.js"></script>
<h2></h2>
<button>generate new element</button>

The above works for those using jQuery version 1.7+. If you're using an older version, refer to the previous answer below.


Previous Answer:

Try using live():

$("button").click(function(){
    $("h2").html("<p class='test'>click me</p>")
});   


$(".test").live('click', function(){
    alert('you clicked me!');
});

Worked for me. Tried it with jsFiddle.

Or there's a new-fangled way of doing it with delegate():

$("h2").delegate("p", "click", function(){
    alert('you clicked me again!');
});

An updated jsFiddle.

Cᴏʀʏ
  • 105,112
  • 20
  • 162
  • 194
  • 9
    As of jQuery 1.7, the `.live()` method is deprecated. Use `.on()` to attach event handlers. – Dave Jarvis Nov 05 '12 at 03:03
  • 71
    It needs to be `$(document).on('click', '.test', function() {` or bound to some other parent to be equal to `.live` Only saying because it came up in a recent [question](http://stackoverflow.com/questions/16225336/catch-change-event-on-input-field-dynamically-added-to-jquery-datatables-table/16225561#16225561) – Blake Plumb Apr 25 '13 at 22:16
  • 2
    @BlakePlumb: I reviewed the jQuery documentation; you are right! I will update my answer. Thanks for the note! – Cᴏʀʏ Apr 25 '13 at 22:35
  • 1
    Make sure the clickable parent you choose is an element that is rendered on page load and not also dynamically generated - yes, it's obvious but if you're not thinking clearly it might foul you up for a hot minute like it did for me! – Dylan Glockler Dec 09 '21 at 15:08
  • Blake Plumb is right, If you read https://api.jquery.com/on/ it says: > Event handlers are bound only to the currently selected elements; they must exist at the time your code makes the call to `.on()`. [...] > Delegated event handlers have the advantage that they can process events from descendant elements that are added to the document at a later time. [...] This element could be the container element [...], or `document` if the event handler wants to monitor all bubbling events in the document. – noraj Aug 25 '22 at 12:35
266

Use the jQuery's .on() method with delegated events

$(".staticParent").on("click", ".dynamicChild", function(evt) {
  // Existent or future .dynamicChild element was clicked

  console.log($(this));            // $ Object {.dynamicChild}
  console.log(this);               // Element .dynamicChild
  console.log(evt.currentTarget);  // Element .dynamicChild
  console.log(evt.target);         // Element .dynamicChild or any descendant
  console.log(evt.delegateTarget); // Element .staticParent delegator
});

The .on() method allows you to delegate any desired event handler to:
current elements or future elements added to the DOM at a later time.

Additional notice: from jQuery v1.7+ the .on() method is preferred over of the deprecated .live().

Event delegation in JavaScript

For technical reference, here's how to properly achieve Event delegation using plain JavaScript:

document.querySelectorAll(".staticParent").forEach((elStaticParent) => {
  elStaticParent.addEventListener("click", (evt) => {
    const elDynamic = evt.target.closest(".dynamicChild");
    if (!elDynamic) return; // .dynamicChild not clicked. Do nothing.

    // Existent or future .dynamicChild element was clicked

    console.log(elDynamic);         // Element .dynamicChild
    console.log(evt.target);        // Element .dynamicChild or any descendant
    console.log(evt.currentTarget); // Element .staticParent delegator
  });
});

For developers switching from jQuery to plain JavaScript — the difference in the Event argument (evt in the above examples) are often confusing. $.Event in jQuery is a constructed custom Object which provides properties like delegateTarget to easily access the static parent that delegated the event (and many others). In JavaScript the Event argument does not provide such property. Instead, you access it with Event.currentTarget (which also differs in jQuery).
In JavaScript (still at this date) we need to add an additional check (often as an if statement) to detect the desired dynamic-child which received an Event, and that's by using the Event.target.closest("someSelector"). If such element is retrieved, that means that either itself, its children, or any deeper descendant bubbled such an Event. This approach is crucial when assigning an Event to an element like i.e: <button class="dynamicBtn" type="button">Click me <i class="icon-star"></i></button>, it'll make sure to properly pass the check even if the pointer's target was the button's icon Element.
Some implementations suggest the use of i.e: evt.target && evt.target.matches("someSelector") which are bug prone on the long run (add a child to the dynamic element and watch your JS fail). Same goes for suggestions like const elDynamicBtn = evt.target; // Your dynamic element which are also wrong and inaccurate.
Without going into deeper technical details and to conclude, make sure to understand the differences in order to construct a rock-solid implementation.

Roko C. Buljan
  • 196,159
  • 39
  • 305
  • 313
  • 62
    This worked for me. Funny none of the answers above point out the fact that a non-dynamic parent selector has to be used. No wonder '.on' was not working for me earlier. – aces. Feb 20 '13 at 17:33
  • 4
    This one also worked for me where the ones above did not. Thanks! – Rich Apr 07 '13 at 10:20
  • 5
    This is the only one which worked for me as I bind event to the dynamic element and not the static parent. PERFECT ANSWER . – Rajshekar Reddy Jan 06 '14 at 07:34
  • 5
    `$('#parent')` this was the one I was missing and that made the behaviour of on() clear, thx :) – Avatar May 27 '14 at 06:29
  • Why is the second method preferable? – Jonah Jul 09 '16 at 17:55
  • click event for dynamic elements will be triggered after several times of clicking. any idea?! jQuery version = 2.2.3 – Sirwan Afifi Aug 15 '17 at 13:52
  • @SirwanAfifi nope, you're clearly doing something wrong. Start by checking if there's some setTimeout involved or some other element that prevents the click event to bubble to your element. – Roko C. Buljan Aug 15 '17 at 14:00
  • @RokoC.Buljan add this `document.querySelector('body').addEventListener('click', function (event) { if (event.target.tagName.toLowerCase() === 'li') { console.log('this works after 2 clicks'); } }, false);` to [this](https://github.com/wesbos/JavaScript30/blob/master/06%20-%20Type%20Ahead/index-FINISHED.html) and see the result. – Sirwan Afifi Aug 15 '17 at 15:01
  • 1
    This should be way up there as the best answer. The missing existing parent element touch had me for a while. – spice Jun 15 '18 at 21:59
  • @aces you may also just use `document` as a static parent: `$(document).on(...)` – Adam Jul 12 '19 at 06:27
  • @Adam Noo. that's not ideal. You should actually not bloat `document` with too many listeners (unless you really have), rather point your delegated parent as the first static parent. – Roko C. Buljan Jul 12 '19 at 20:47
  • The best answer! And also flagged out .Live(). Its very important to remember .on supersedes delegate & live – user3833732 May 03 '20 at 21:13
  • Aside from this working perfectly, another thing that tripped me up is: it doesn't work if you wrap the jquery in a `$(document).ready(function () { });` – justiceorjustus Jul 23 '20 at 21:45
158

Reason:

In jQuery, Click() event Direct binding which attaches the event handler to the element only if the particular element(Html code) exists on the page(after page loads).

Dynamic elements are created with the help of javascript or jquery(not in Html).

It won't consider the future elements(Dynamic) which are created after the page gets loaded.

So the normal click event won't fire on the dynamic element.

Solution :

To overcome this, we should use on() function. on() can delegate the event for both the current and future elements.

Delegated events have the advantage that can attach the handler to the elements which are being added to the document in the future.

Note : delegate(),live() and on() functions have the advantages over the DOM elements. As of JQuery 1.7 delegate() and live() are deprecated(Don't use these).

on() only Can delegate the event for both current and future elements.

So, Your code should be like this

Remove the code from $(document).ready:

$(".test").click(function(){

  alert();

});

Change into:

$(document).on('click','.test',function(){

  alert('Clicked');

});
Prabhagaran
  • 3,620
  • 1
  • 19
  • 19
  • 2
    Excelent answer for situation, when the parent is not existing and is creating dynamically too. – west44 Jun 30 '15 at 12:07
  • 6
    How is it possible that all the above answers have not mentioned the most important line `$(document).on('click','.test',function()` ? This should be the checked answer! Thx – Adam Jul 12 '19 at 06:28
33

Add this function in your js file. It will work on every browser

$(function() {
    $(document).on("click", '#mydiv', function() {
        alert("You have just clicked on ");
    });
});
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div id='mydiv'>Div</div>
Abhishek
  • 401
  • 4
  • 4
13

You need to use .live for this to work:

$(".test").live("click", function(){
   alert();
});

or if you're using jquery 1.7+ use .on:

$(".test").on("click", "p", function(){
   alert();
});
amurra
  • 15,221
  • 4
  • 70
  • 87
  • Read the DOCS about delegated events please. Where are your event delegated elements? The other example you showed (using .on()) is not the exact way to replace `.live()` in any case. – Roko C. Buljan Apr 09 '13 at 15:11
13

Change

 $(".test").click(function(){

To

 $(".test").live('click', function(){

LIVE DEMO

jQuery .live()

qwertymk
  • 34,200
  • 28
  • 121
  • 184
8

Try .live() or .delegate()

http://api.jquery.com/live/

http://api.jquery.com/delegate/

Your .test element was added after the .click() method, so it didn't have the event attached to it. Live and Delegate give that event trigger to parent elements which check their children, so anything added afterwards still works. I think Live will check the entire document body, while Delegate can be given to an element, so Delegate is more efficient.

More info:

http://www.alfajango.com/blog/the-difference-between-jquerys-bind-live-and-delegate/

DSKrepps
  • 641
  • 1
  • 7
  • 15
  • as of jQuery 1.71, `.live()` is deprecated and `delegate()` has been superseded by the [.on()](http://api.jquery.com/on/) method – T J Aug 22 '14 at 17:32
7

I found two solutions at the jQuery's documentation:

First: Use delegate on Body or Document

E.g:

 $("body").delegate('.test', 'click', function(){
 ...
  alert('test');
 });

Why?

Answer: Attach a handler to one or more events for all elements that match the selector, now or in the future, based on a specific set of root elements. link: http://api.jquery.com/delegate/

Second: Put the your function at the "$( document )", using "on" and attach it to the element that you want to trigger this. The first parameter is the "event handler", the second: the element and the third: the function. E.g:

 $( document ).on( 'click', '.test', function () {
 ...
  alert('test');
 });

Why?

Answer: Event handlers are bound only to the currently selected elements; they must exist on the page at the time your code makes the call to .on(). To ensure the elements are present and can be selected, perform event binding inside a document ready handler for elements that are in the HTML markup on the page. If new HTML is being injected into the page, select the elements and attach event handlers after the new HTML is placed into the page. Or, use delegated events to attach an event handler, as described next ... link: https://api.jquery.com/on/

user13500
  • 3,817
  • 2
  • 26
  • 33
user3425150
  • 73
  • 1
  • 4
7

Best way to apply event on dynamically generated content by using delegation.

$(document).on("eventname","selector",function(){
    // code goes here
});

so your code is like this now

$(document).on("click",".test",function(){
    // code goes here
});
Jay Patel
  • 5,783
  • 1
  • 17
  • 20
4
$(.surrounding_div_class).on( 'click', '.test', function () {
alert( 'WORKS!' );
});

Will only work if the DIV with the class .surrounding_div_class is the immediate parent to the object .test

If there is another object in the div that will be filled it wont work.

4

An alternate and more succinct alternative (IMHO) is to use a raw javascript function that responds to an on click event, then pass the target element back to jQuery if you like. The advantage of this approach is that you can dynamically add your element anywhere, and the click handler will 'just work', and you need not concern yourself with delegating control to parent elements, and so on.

Step 1: Update the dynamic html to fire an onclick event. Be sure to pass the 'event' object as an argument


    $("button").click(function() {
        $("h2").html("<p class='test' onclick='test(event)'> click me </p>")
    });

Step 2: Create the test function to respond to the click event


    function test(e){
        alert();
    });

Optional Step 3: Given you are using jQuery I'm assuming it will be useful to get a reference back to the source button


    function test(e){
        alert();

        // Get a reference to the button
        // An explanation of this line is available here
        var target = (e.target)? e.target : e.srcElement;

        // Pass the button reference to jQuery to do jQuery magic
        var $btn = $(target);

    });

Nick Mitchell
  • 1,207
  • 13
  • 24
4

If you have a dinamically added link to some container or the body:

var newLink= $("<a></a>", {
        "id": "approve-ctrl",
        "href": "#approve",
        "class": "status-ctrl",
        "data-attributes": "DATA"
    }).html("Its ok").appendTo(document.body);

you can take its raw javascript element and add an event listener to it, like the click:

newLink.get(0).addEventListener("click", doActionFunction);

No matter how many times you add this new link instance you can use it as if you where using a jquery click function.

function doActionFunction(e) {
    e.preventDefault();
    e.stopPropagation();

    alert($(this).html());
}

So you will receive a message saying

Its ok

It has better performance than other alternatives.

Extra: You could gain better performance avoiding jquery and using plain javascript. If you are using IE up to version 8 you should use this polyfill to use the method addEventListener

if (typeof Element.prototype.addEventListener === 'undefined') {
    Element.prototype.addEventListener = function (e, callback) {
      e = 'on' + e;
      return this.attachEvent(e, callback);
    };
  }
EliuX
  • 11,389
  • 6
  • 45
  • 40
4

The problem you have is that you're attempting to bind the "test" class to the event before there is anything with a "test" class in the DOM. Although it may seem like this is all dynamic, what is really happening is JQuery makes a pass over the DOM and wires up the click event when the ready() function fired, which happens before you created the "Click Me" in your button event.

By adding the "test" Click event to the "button" click handler it will wire it up after the correct element exists in the DOM.

<script type="text/javascript">
    $(document).ready(function(){                          
        $("button").click(function(){                                  
            $("h2").html("<p class='test'>click me</p>")                          
            $(".test").click(function(){                          
                alert()                          
            });       
        });                                     
    });
</script>

Using live() (as others have pointed out) is another way to do this but I felt it was also a good idea to point out the minor error in your JS code. What you wrote wasn't wrong, it just needed to be correctly scoped. Grasping how the DOM and JS works is one of the tricky things for many traditional developers to wrap their head around.

live() is a cleaner way to handle this and in most cases is the correct way to go. It essentially is watching the DOM and re-wiring things whenever the elements within it change.

VK Da NINJA
  • 510
  • 7
  • 19
Marc LaFleur
  • 31,987
  • 4
  • 37
  • 63
3

.live function works great.

It is for Dynamically added elements to the stage.

$('#selectAllAssetTypes').live('click', function(event){
                    alert("BUTTON CLICKED");
                    $('.assetTypeCheckBox').attr('checked', true);
                });

Cheers, Ankit.

Ankit Tanna
  • 1,779
  • 8
  • 32
  • 59
  • 3
    Live is deprecated as of Jquery 1.7 in favour of .on. It has even been removed as of Jquery 1.9. http://api.jquery.com/live/ – Liam Apr 08 '13 at 13:29
3

The Jquery .on works ok but I had some problems with the rendering implementing some of the solutions above. My problem using the .on is that somehow it was rendering the events differently than the .hover method.

Just fyi for anyone else that may also have the problem. I solved my problem by re-registering the hover event for the dynamically added item:

re-register the hover event because hover doesn't work for dynamically created items. so every time i create the new/dynamic item i add the hover code again. works perfectly

$('#someID div:last').hover(
    function() {
       //...
    },
    function() {
       //...
    }
);
Liam
  • 27,717
  • 28
  • 128
  • 190
  • 1
    `hover` is a wrapper for `mouseenter` and `mouseleave` events (http://api.jquery.com/mouseenter/), these in turn are wrappers for `.on('mouseenter', handler)`. **So this uses** `.on` underneath! So I can't see how this is making much of a difference to using `.on`. Sounds to me like your missing something else here. – Liam Apr 08 '13 at 13:32
3

I'm working with tables adding new elements dynamically to them, and when using on(), the only way of making it works for me is using a non-dynamic parent as:

<table id="myTable">
    <tr>
        <td></td> // Dynamically created
        <td></td> // Dynamically created
        <td></td> // Dynamically created
    </tr>
</table>

<input id="myButton" type="button" value="Push me!">

<script>
    $('#myButton').click(function() {
        $('#myTable tr').append('<td></td>');
    });

    $('#myTable').on('click', 'td', function() {
        // Your amazing code here!
    });
</script>

This is really useful because, to remove events bound with on(), you can use off(), and to use events once, you can use one().

Timbergus
  • 3,167
  • 2
  • 36
  • 35
3

I couldn't get live or delegate to work on a div in a lightbox (tinybox).

I used setTimeout successfullly, in the following simple way:

$('#displayContact').click(function() {
    TINY.box.show({html:'<form><textarea id="contactText"></textarea><div id="contactSubmit">Submit</div></form>', close:true});
    setTimeout(setContactClick, 1000);
})

function setContactClick() {
    $('#contactSubmit').click(function() {
        alert($('#contactText').val());
    })
}
3

Also you can use onclick="do_something(this)"inside element

Daryn K.
  • 671
  • 2
  • 12
  • 23
2

You CAN add on click to dynamically created elements. Example below. Using a When to make sure its done. In my example, i'm grabbing a div with the class expand, adding a "click to see more" span, then using that span to hide/show the original div.

$.when($(".expand").before("<span class='clickActivate'>Click to see more</span>")).then(function(){
    $(".clickActivate").click(function(){
        $(this).next().toggle();
    })
});
Daryl H
  • 624
  • 8
  • 8
0

Use 'on' as click gets bind to the elements already present.

For e.g

$('test').on('click',function(){
    alert('Test');
})

This will help.

Abhijit
  • 242
  • 1
  • 7