185

I've been having a lot of trouble attaching the click event to a JQuery object before adding it to the DOM.

Basically I have this button that my function returns, then I append it to the DOM. What I want is to return the button with its own click handler. I don't want to select it from the DOM to attach the handler.

My code is this:

createMyButton = function(data) {

  var button = $('<div id="my-button"></div>')
    .css({
       'display' : 'inline',
       'padding' : '0px 2px 2px 0px',
       'cursor' : 'pointer'
     }).append($('<a>').attr({
       //'href' : Share.serializeJson(data),
       'target' : '_blank',
       'rel' : 'nofollow'
     }).append($('<image src="css/images/Facebook-icon.png">').css({
       "padding-top" : "0px",
       "margin-top" : "0px",
       "margin-bottom" : "0px"
     })));

     button.click(function () {
        console.log("asdfasdf");
     });

     return button;     
}

The button that is return is unable to catch the click event. However, if I do this (after the button is added to the DOM):

$('#my-button').click(function () {
    console.log("yeahhhh!!! but this doesn't work for me :(");
});

It works... but not for me, not what I want.

It seems to be related to the fact that the object is not yet a part of the DOM.

Oh! By the way, I'm working with OpenLayers, and the DOM object that I'm appending the button to is an OpenLayers.FramedCloud (Which is not yet a part of the DOM but will be once a couple of events are triggered.)

Brian Tompsett - 汤莱恩
  • 5,753
  • 72
  • 57
  • 129
danielrvt
  • 10,177
  • 20
  • 80
  • 121

10 Answers10

304

Use this. You can replace body with any parent element that exists on dom ready

$('body').on('click', '#my-button', function () {
     console.log("yeahhhh!!! but this doesn't work for me :(");
});

Look here http://api.jquery.com/on/ for more info on how to use on() as it replaces live() as of 1.7+.

Below lists which version you should be using

$(selector).live(events, data, handler); // jQuery 1.3+

$(document).delegate(selector, events, data, handler); // jQuery 1.4.3+

$(document).on(events, selector, data, handler); // jQuery 1.7+

Community
  • 1
  • 1
wirey00
  • 33,517
  • 7
  • 54
  • 65
  • I tried this just now and it doesnt work, it appears that the "on" method must be used with elements that are already in the DOM. "The .on() method attaches event handlers to the currently selected set of elements in the jQuery object" – danielrvt Jun 06 '12 at 19:44
  • It should work. "on" replaces "live" which attaches events to current/future selectors which means any element with the id="my-button" should have that click event attached to it – wirey00 Jun 06 '12 at 19:53
  • Can you make a fiddle for us to see exactly what the problem is? – wirey00 Jun 06 '12 at 20:00
  • I'am so sorry, it works in deed, but with 'hover', not with 'click'... I'm trying to figure out why... – danielrvt Jun 06 '12 at 20:04
  • 6
    Here's a fiddle I made to test out 'on' click. http://jsfiddle.net/X8KcU/1/ – wirey00 Jun 06 '12 at 20:09
  • Thanks @wirey, I see what you did there, but it still wont work for me... I'm starting to think that it has to do with OpenLayers and the FramedCloud... – danielrvt Jun 06 '12 at 20:24
  • I'm a little confused about your variable "button". What exactly are you storing in it? Looks like you are selecting ('
    ') element? Is that what you are trying to get?
    – wirey00 Jun 06 '12 at 20:28
  • Actually what I want is to create a popup with OpenLayers and put a button inside to display information about my map... As the popup is created dynamically, I have no other choice but to create the button on the go. – danielrvt Jun 06 '12 at 20:32
  • Something like this fiddle? http://jsfiddle.net/X8KcU/3/ – wirey00 Jun 06 '12 at 20:51
  • 12
    "You can replace BODY with any parent element that EXISTS ON DOM ready" that's the key, thanks man! – Roberto Alarcon Feb 09 '13 at 22:38
  • BTW @danielrvt this worked for you? because it's not marked as an answer. – Roberto Alarcon Feb 09 '13 at 22:51
  • I just used this and it works. Not sure if it worked for the OP, but it's definitely correct. – sage88 Sep 08 '13 at 21:35
  • 1
    Unfortunately this solution isn't ideal because it requires that I add an ID to the element I'm listening for events from. I'd rather the `createMyButton` function return an element that already has an event listener attached to it. Is that possible in JQuery? – Ajedi32 Dec 13 '13 at 01:05
  • 1
    @Ajedi32 not necessarily - You just need the right css selector to target your element. Can you explain more on what exactly you're trying to do? – wirey00 Dec 13 '13 at 14:17
  • @ᾠῗᵲᄐᶌ I've already solved my problem by waiting until after the element is added to the DOM to attach the event, but the problem was related to this: https://github.com/Ajedi32/html_checkers/blob/2838c5c4caa5338f7be6bbd8ecef598bf3eb7e7e/assets/javascript/checkers.js#L256 I want to bind an event to the `HTMLCheckerPiece` immediately after it is instantiated, even if it hasn't yet been added to the DOM. I don't want to bind the event to all checker pieces, since the event should only be triggered for an individual checker piece when it is clicked. – Ajedi32 Dec 13 '13 at 14:29
  • Can this be used to set a property of an element that has not yet been added? (checking a checkbox in my case) – James Wierzba Aug 29 '16 at 18:12
  • @JamesWierzba not to set a property. This is to bind an event handler to listen for dynamic elements. If you want to set a property of an element you can after it has been added to the DOM. How exactly is the checkbox getting added? – wirey00 Aug 29 '16 at 18:27
  • 1
    Ok I thought so. The checkbox is added dynamically after an ajax call to the server, which responds with the info required to create it. I've done what you suggested and manipulated it after the input was added to the DOM. – James Wierzba Aug 29 '16 at 18:39
  • Using .on() is normally the way to go indeed, but it doesn't always work like it should. I'm currently with FF 47.0 and jQuery 1.12, and the event doesn't fire off on lately loaded DOM contents. The initially loaded DOM contents triggers it though. In this case, you may have to create a specific event handler that listens to the object once loaded live in a callback for example. Attention, it is then easy to multiply even listeners and create a massive duplicate set of calls... – Fabien Haddadi Sep 11 '17 at 04:23
  • What about `.each()` adding to `.on()` – Si8 Oct 02 '17 at 15:20
44

I am really surprised that no one has posted this yet

$(document).on('click','#my-butt', function(){
   console.log('document is always there');
}) 

If you are unsure about what elements are going to be on that page at that time just attach it to document.

Note: this is sub-optimal from performance perspective - to get maximum speed one should try to attach to the nearest parent of element that is going to be inserted.

Matas Vaitkevicius
  • 58,075
  • 31
  • 238
  • 265
23

Try this.... Replace body with parent selector

$('body').on('click', '#my-button', function () {
    console.log("yeahhhh!!! but this doesn't work for me :(");
});
Vins
  • 8,849
  • 4
  • 33
  • 53
  • 4
    I see you answered first with a simple typo, so my +1 is for you for the intention. Great solution. Thanks! – whoan Jul 04 '15 at 15:24
9

Try:

$('body').on({
    hover: function() {
        console.log("yeahhhh!!! but this doesn't work for me :(");
    },
    click: function() {
        console.log("yeahhhh!!! but this doesn't work for me :(");
    }
},'#my-button');

jsfiddle example.

When using .on() and binding to a dynamic element, you need to refer to an element that already exists on the page (like body in the example). If you can use a more specific element that would improve performance.

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.

Src: http://api.jquery.com/on/

j08691
  • 204,283
  • 31
  • 260
  • 272
  • This is right, however it will only work with the hover event and not with the click event... – danielrvt Jun 06 '12 at 19:59
  • I'm not sure I understand what you mean. .on() definitely works with the click event. – j08691 Jun 06 '12 at 20:00
  • I know... its so rare... when I use: $('body').on('click hover', '#my-button', function () {console.log('hello');}) it works, but only on hover, also if I remove the "click" from above it will work with hover, but if I leave "click" it doesn't do anything... :( – danielrvt Jun 06 '12 at 20:08
  • I don't believe you can do that with on(). Maybe with bind you had that option, but with on() you have to do something like `$('body').on({ hover: function() { }, click: function() { } }` – j08691 Jun 06 '12 at 20:16
  • See my updated answer and example. – j08691 Jun 06 '12 at 20:25
8

You have to append it. Create the element with:

var $div = $("<div>my div</div>");
$div.click(function(){alert("clicked")})
return $div;

Then if you append it will work.

Take a look at your example here and a simple version here.

andrewb
  • 5,200
  • 6
  • 36
  • 54
Marco Allori
  • 3,198
  • 33
  • 25
  • I did something like this, and it worked fine. – andrewb Jun 16 '15 at 06:51
  • I said it worked fine, so no issue. This is my preferred solution, as it means the handlers are dynamic like the content. Basically treating the content as a partial view with its own controller, which I really like. – andrewb Jun 16 '15 at 08:17
1

Complement of information for those people who use .on() to listen to events bound on inputs inside lately loaded table cells; I managed to bind event handlers to such table cells by using delegate(), but .on() wouldn't work.

I bound the table id to .delegate() and used a selector that describes the inputs.

e.g.

HTML

<table id="#mytable">
  <!-- These three lines below were loaded post-DOM creation time, using a live callback for example -->
  <tr><td><input name="qty_001" /></td></tr>
  <tr><td><input name="qty_002" /></td></tr>
  <tr><td><input name="qty_003" /></td></tr>
</table>

jQuery

$('#mytable').delegate('click', 'name^=["qty_"]', function() {
    console.log("you clicked cell #" . $(this).attr("name"));
});
Fabien Haddadi
  • 1,814
  • 17
  • 22
0

Does using .live work for you?

$("#my-button").live("click", function(){ alert("yay!"); }); 

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

EDIT

As of jQuery 1.7, the .live() method is deprecated. Use .on() to attach event handlers. Users of older versions of jQuery should use .delegate() in preference to .live().

http://api.jquery.com/on/

PsychoDUCK
  • 1,103
  • 3
  • 17
  • 35
  • 1
    .live() is deprecated and replacced with .on() – frenchie Jun 06 '12 at 18:58
  • 1
    This won't work if my-button is loaded dynamically. – j08691 Jun 06 '12 at 19:02
  • @j08691 yes it will, that's the point of `live`, `delegate`, and `on`. – jbabey Jun 06 '12 at 19:03
  • 1
    @jbabey- Read the documentation on on(). If his element is created dynamically, you have to bind to an existing page element. "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()." – j08691 Jun 06 '12 at 19:05
  • @j08691 is right, if the element is created dynamically it won't work. – danielrvt Jun 06 '12 at 19:33
0

On event

$('#my-button').on('click', function () {
    console.log("yeahhhh!!! but this doesn't work for me :(");
});

Or add the event after append

Jorge
  • 17,896
  • 19
  • 80
  • 126
0

jQuery .on method is used to bind events even without the presence of element on page load. Here is the link It is used in this way:

 $("#dataTable tbody tr").on("click", function(event){
    alert($(this).text());
 });

Before jquery 1.7, .live() method was used, but it is deprecated now.

Nishu Tayal
  • 20,106
  • 8
  • 49
  • 101
-3

Maybe bind() would help:

button.bind('click', function() {
  alert('User clicked');
});
ZERO
  • 133
  • 3