1

Scenario

I have a list of users, within each list item is a <header> and a <div> wrapper - they are siblings:

<li class="user">
    <header>
        // ...
    </header>
    <div class="user-wrapper">
        // ...
    </div>
</li>

I am toggling the div wrapper when the user header is clicked.

What currently works:

// the handler is called
$('li.user > header').live('click', function () {
    $(this).next('.user-wrapper').toggle();
});

As live() has been deprecated and I am using jQuery 1.7.2, I want to use on().

What does not work:

// Direct style
$('li.user > header').on('click', function () {
    $(this).next('.user-wrapper').toggle();
});

// Delegated style
$('li.user').on('click', 'header', function () {
    $(this).next('.user-wrapper').toggle();
});

In either on() scenario, the anonymous handler function is not being called.

Question

First... why is on() choking?

Second... if/when it works, using the delegate style, can i still reference the header's sibling div in the same manner as above?

one.beat.consumer
  • 9,414
  • 11
  • 55
  • 98
  • 1
    Are the `li.user` elements dynamic as well? In that case you have to select an ancestor for `.on`: `$('ancestor').on('click', 'li.user > header', ...)`. – Felix Kling Jul 18 '12 at 18:57
  • Both work here http://jsfiddle.net/meVe4/2/ – João Paulo Macedo Jul 18 '12 at 18:57
  • Thanks for the replies. I think I found it. It has to do with knockout. The LI items look static, but I think with one particular binding they are being treated different. I'll post my conclusion here shortly. @thecodeparadox helped me put together a fiddle. – one.beat.consumer Jul 18 '12 at 20:51

2 Answers2

3

[EDITED]

For delegate event handling the syntax of .on() is:

// 'element' must be static and an ancestor to 'target'.
$(element).on(eventName, target, handlerFunction);

Your delegate scenario above should work, assuming your li.user tags are static at the time of binding.

$('li.user').on('click', 'header', function () {
    $(this).next('.user-wrapper').toggle();
});

If you test this in jsFiddle, it works as is. It seems like your li.user elements are being created dynamically.

If li.user is dynamically created then use a different (static) parent selector. If your list ul is always present, for example:

// HTML
<ul class="user-list">
  <li class="user">
    <header>
        // ...
    </header>
    <div class="user-wrapper">
        // ...
    </div>
  </li>
</ul>

// JavaScript
$('ul.user-list').on('click', 'li.user > header', function() {
  $(this).next('.user-wrapper').toggle();
});
thecodeparadox
  • 86,271
  • 21
  • 138
  • 164
  • Right. It's common to use the `document` object, e.g. `$(document).on('click','myselector',function(){});` – GriffeyDog Jul 18 '12 at 18:35
  • 1
    @GriffeyDog if we know about any static parent element of target, then using `document` as container should avoid. – thecodeparadox Jul 18 '12 at 18:36
  • Agreed, use the most specific static container you can. – GriffeyDog Jul 18 '12 at 18:43
  • @thecodeparadox your code and mine are entirely the same... both `on()` scenarios I listed are NOT firing the `function()` handler. Am I blind to something? – one.beat.consumer Jul 18 '12 at 18:47
  • @thecodeparadox my understanding is that "direct style" places the handler on all headers directly under the list item, where the "delegate style" places a single handler on the list item itself, and waits for click events on the header children to bubble up. Both syntaxes in my example should work, no? – one.beat.consumer Jul 18 '12 at 18:49
  • @one.beat.consumer `delegate event` handler will bind event to all element that are still present in DOM at page load and will come to DOM in future(dynamically). – thecodeparadox Jul 18 '12 at 18:55
  • @thecodeparadox - `jabbr.net` is back up `/join jquery`. i'll be here for a bit. – one.beat.consumer Jul 18 '12 at 19:15
  • @thecodeparadox thank you for talking with me. it turns out knockout.js was being used to template the `li` tags from a json collection. The first time the collection reloaded, the .on() bindings broke. – one.beat.consumer Jul 18 '12 at 21:15
0

Thanks

Lots of gratitude to @thecodeparadox - his answer explains the issue best and is marked as such. My answer below just explains the details of how/why the list items became dynamic.

Further explanation

Knockout.js is being used to template the li elements. This alone was not the issue as the li elements were created before the .on() bindings occurred and are still treated as "static".

The killer was a ko.computed() property on the knockout model. The property reloads the collection here/there. From the moment the collection is reloaded, the scaffolding of the li items is redone and all the .on() break.

one.beat.consumer
  • 9,414
  • 11
  • 55
  • 98