user246114
user246114

Reputation: 51631

Click event handler inadvertently bubbles, even after calling jQuery's stopPropagation()

I'm trying to have different delegate click handlers for different elements within "li" elements in a list. Example:

<ul id='myList'>
  <li>
    <p>First item.</p>
    <button>button1</button>
    <button>button2</button>
  </li>
  <li>
    <p>Second item.</p>
    <button>button1</button>
    <button>button2</button>
  </li>
</ul>

when the user clicks button1 (of any item) I want to grab that event, and stop propagation (same for button2 instances). The user clicking the parent li element would be a different handler:

$('#myList').delegate('li', 'click', function() {
    alert("You clicked a parent <li> item!");
});

$('#myList').delegate('button', 'click', function(event) {
    event.stopPropagation();
    event.stopImmediatePropagation();
    alert("You clicked a button (but which one?)!");
});

So one issue is, how do I have a delegate for button1 instances, and another for button2 instances? The second delegate in the example above does fire when a button is clicked, but event.stopPropagation() doesn't seem to work as the handler for the parent li item still gets called,

------ Update --------------

Also trying to call event.stopImmediatePropagation(), no effect though, parent handler still being called too.

Thank you

Upvotes: 3

Views: 1841

Answers (4)

Martin Belcher - AtWrk
Martin Belcher - AtWrk

Reputation: 4579

The reason is that .delegate() will only execute the click functions once the click event bubbles down through the child elements to myList. It will then perform every delegate that matches, so it is correctly calling both in your example because by the time click arrives at myList it has passed through both a BUTTON and a LI.

Since you bound LI before BUTTON the LI delegate is first in the list that get called, you could just bind the BUTTON ones first and it should work.

Upvotes: 0

Mark
Mark

Reputation: 33571

Maybe in the parent:

$('#myList').delegate('li', 'click', function(event) {
    if (!$(event.target).is('button')) {
        alert("You clicked a parent <li> item!");
    }
});

Upvotes: 1

Marcel Korpel
Marcel Korpel

Reputation: 21763

Indeed, it should work, and I tried to step through the jQuery code, but to no avail. Instead, I made this:

(function(){
  var el = document.getElementsByTagName("li");
  var i = el.length;

  while (i--)
    el[i].onclick = function () {
      alert("You clicked a parent <li> item!");
    };

  el = document.getElementsByTagName("button");
  i = el.length;

  while (i--)
    el[i].onclick = function (event) {
      var e = event || window.event;

      e.stopPropagation();
      e.cancelBubble = true;
      alert("You clicked a button (but which one?)!");
    };

})();

which works the way you want it to work (not tested in IE, though). I'm to tired to look into this further, now, perhaps tomorrow.

To answer your other question: I think you should really add ids or classes to your <button>s, so you can easily test which button got called.

Upvotes: 1

Aaron Butacov
Aaron Butacov

Reputation: 34347

You can try event.stopImmediatePropagation(). Theoretically, what you have should work, so expanding it might help.

Upvotes: 0

Related Questions