Ujwal Ratra
Ujwal Ratra

Reputation: 401

Stop events inside delegate with same handler

I have a table on which I use delegate on tr, each row also has a button. I need to perform some action on this button click. The click event on the button fires when it is clicked but it fires the number of times a user clicked anywhere else in the row plus the one time the button is clicked.

Here's a code snippet

JS

$('.detailTable').delegate('tr', 'click', function() {
  console.log('clicked in row');
  $('#saveButton').click(function(event) {
    console.log('clicked on button');
    return false;
  });
});

HTML

<table class="detailTable">
  <tr>
    <td>first</td>
    <td>
      <button id="saveButton">
        Save
      </button>
    </td>
  </tr>
</table>

Working Fiddle

Upvotes: 0

Views: 103

Answers (4)

Toonijn
Toonijn

Reputation: 394

I don't really get what your question is but some remarks:

I've made this Fiddle with your code and some mild edits.

By delagating an event listener (the one with the button) inside another (the one with the row), there has to be first clicked on the row (or on the button, because it is in the row) only after that the event on the button will be listened to.

If you have multiple rows you have to make sure to not use the same id for every button. In the fiddle I have used a classname which solves that problem.

I suggest using two seperate listeners:

$('.detailTable tr').click(function() {
  console.log('clicked in row');
});

$('.detailTable tr .saveButton').click(function(event) {
  console.log('clicked on button');
  event.stopPropagation();
});

the event.stopPropagation() (jQuery api) prevents the click to bubble up to the row so, when clicking on the button the row will not be clicked.

Upvotes: 0

Jai
Jai

Reputation: 74738

First of all, you didn't mention why you are using event delegation? As this isn't required if you are not creating/loading any new elements after page load via some code or using ajax.

The issue is you have a click event bound on the tr, which in turn binds an event on the button. So each time you click on tr it registers a new event so the callback is fired for each registration. If you click in the tr 3 times then for button the callback will be executed 3 times.

In my opinion you should just use normal click events.


You might do this:

$('.detailTable').delegate('tr', 'click', function() {
  console.log('clicked in row');
}).delegate('#saveButton', 'click', function(e) {
    console.log('clicked on button');
    return false; // does two things e.preventDefault() & e.stopPropagation()
    // and this will stop the event to bubble up to the tr element.
});

Upvotes: 1

jfriend00
jfriend00

Reputation: 707318

First off, you can't have multiple buttons with the same id. That's illegal HTML and will just cause problems for selectors. I'd suggest using a class name instead.

Then, you never want to be binding a new click handler every time something else is clicked. That just gets the click handlers piling up and you get multiple callbacks for each click handler you registered.

Then, unless you're using a really old version of jQuery, you should be using .on() instead of .delegate().

So, here's what I would suggest:

<table class="detailTable">
  <tr>
    <td>first</td>
    <td>
      <button class="saveButton">
        Save
      </button>
    </td>
  </tr>
  <tr>
    <td>first</td>
    <td>
      <button class="saveButton">
        Save
      </button>
    </td>
  </tr>
</table>

Code:

// save button click handlers
$('.detailTable').on('click', '.saveButton', function(e) {
    // don't let button click handler propagate to the row
    // you can remove this e.stopPropagation() line if you want
    // a click to trigger both button and row click handlers
    e.stopPropagation();
    console.log('clicked on save Button');
});

// row click handlers
$('.detailTable').on('click', 'tr', function() {
    console.log('clicked in row');
});

Working demo: https://jsfiddle.net/jfriend00/hy69py9m/


FYI, if you're not dynamically creating elements in this table, then there's no need for delegated event handling and you could just use static selectors to map your event handlers.

Upvotes: 0

janhamburg
janhamburg

Reputation: 25

That is correct behaviour as you register a new handler for the button click each time you click on the row. Its not totally clear for me what you want to achieve.

To get the row from the button click event, you can try this:

$("#saveButton").click(function() {
   var $item = $(this).closest("tr");
});

Upvotes: 0

Related Questions