0

I want to add a clickable caption to a table with jQuery. I add it like this:

$("table.table-legend").prepend('<caption class="edit-select"><a href="javascript:;">Select All - None</a></caption>');

This works and I do get a clickable caption, but I can't figure out how to define the function that gets called.

I tried:

$('table caption a').on('click', function () {

and

$('.edit-select a').on('click', function () { 

and a few other ways, but no matter what, my function is not called. What is the proper way to do this?

Larry Martell
  • 3,526
  • 6
  • 40
  • 76
  • 1
    I think the problem you're running into is that you're binding on elements that don't yet exist. To test this theory, try binding to body and filtering by something more specific. `$('body').on('click', '.edit-select a', function () {console.log('it worked');});` – Matt Mar 05 '15 at 20:38
  • What's the point of having a `href="javascript:;"` attribute? Just put a `#` in there. – blex Mar 05 '15 at 20:38
  • That's how I've always seen it done. – Larry Martell Mar 05 '15 at 21:00

2 Answers2

4
$(document).on('click', '.edit-select a', function() {
    // do stuff
});

jQuery is only aware of the elements in the page at the time that it runs, so new elements added to the DOM are unrecognized by jQuery. To combat that use event delegation, bubbling events from newly added items up to a point in the DOM that was there when jQuery ran on page load. Many people use document as the place to catch the bubbled event, but it isn't necessary to go that high up the DOM tree. Ideally you should delegate to the nearest parent that exists at the time of page load.

Keep in mind that elements added to the DOM dynamically during page load can be bound to without relying on bubbling and delegation as long as events are bound to them after the element has been included. Consider the following example -

var newKid = '<div class="kid">Jan</div>';
$('.parent').append(newKid);

$('.kid').click(function() {
    var kidName = $(this).html();
    var message = kidName + ' got clicked<br />';
    $('.whoGotClicked').append(message);
});

var newKid = '<div class="kid">Cindy</div>';
$('.parent').append(newKid);

Who do you think will be added to the 'got clicked' list here? Marsha is included in the original markup and Jan was added to the DOM as the script ran, but prior to the click event being bound to the kid class. Cindy was added to the DOM after the binding occurred. Both Marsha and Jan are handled by the click event while Cindy is left in the cold. Using event delegation

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

will insure that all kids, now and in the future, will get handled.

Community
  • 1
  • 1
Jay Blanchard
  • 34,243
  • 16
  • 77
  • 119
  • 1
    I agree on the solution, but not entirely on your explanation. JQuery will be aware of newly created elements if you select them (`$('#new-el')`) after they were created. Otherwise, you are right ;) – blex Mar 05 '15 at 20:42
  • @blex you're correct - I had the function before the statement that created the caption. I moved it to after that it works with $('.edit-select a') – Larry Martell Mar 05 '15 at 20:47
  • @blex try selecting something after you add it to the DOM dynamically without relying on bubbling. You'll find that jQuery is unaware of that element because *it ran when the page loaded* and is therefore unaware of dynamically added (via JavaScript / jQuery) items. If jQuery were aware of these items there would be no need to perform delegation and rely on bubbling. – Jay Blanchard Mar 05 '15 at 20:50
  • @JayBlanchard Your logic makes sense. But I'm sorry to disagree. Selecting (`$('#elem')`) an element before it was created won't target it. In this case you need to use event delegation as you answered. But if you select it after, it looks for it and finds it. As a proof: [ztadic's js fiddle](http://jsfiddle.net/sygn/8woct477/). – blex Mar 05 '15 at 20:53
  • Ah, but the fiddle uses an [`on()`](http://api.jquery.com/on/) @blex and the prepend occurs before the click event is bound to the anchor tags. Here is an [example](http://jsfiddle.net/mbg3wfro/) showing what I am talking about. – Jay Blanchard Mar 05 '15 at 20:59
  • Now try this: http://jsfiddle.net/mbg3wfro/1/ it works because it looks for it even if it was not there on page load. Trust me, I create js games as my job (lucky me), i always bind events on a ton of newly created elements. I try to avoid event delegation, as it's not as efficient when handling a lot of elements. – blex Mar 05 '15 at 21:04
  • Right @blex, and you have to consider the context here. the element was appended prior to the event being bound to a handler. As my explanation says, jQuery is only aware of what is in the page at the point that it runs. Anything after that has to rely on bubbling and delegation. – Jay Blanchard Mar 05 '15 at 21:07
  • Exactly. Even though, you're going to agree with me, it was not there when the page was rendered. It's just that little glitch in your explanation that bugs me :-) What I mean is that you don't **have to** use event delegation, if you bind the event **after** the element was created. – blex Mar 05 '15 at 21:08
  • Ah, I see what you're saying but I *think* my explanation covers the order. :-) If not, I can clarify. – Jay Blanchard Mar 05 '15 at 21:11
1
$("table.table-legend").prepend('<caption class="edit-select"><a> href="javascript:;">Select All - None</a></caption>');

Is this the actual code or did you just copy a part of it?

You have a typo in the text after the beginning of the anchor tag: href The selector looks fine.

Here is the snippet

EDIT:

The order of the javascript calls is important. You need to assign the click event after the captions have been inserted in the DOM

ztadic91
  • 2,774
  • 1
  • 15
  • 21