Search code examples
javascriptjqueryjquery-events

Stop JS-event propagation on table row click


I have the following HTML-table:

<table>
  <tr>
    <td>
      Row 1
    </td>
    <td>
      <!-- Hidden JSF-button -->
      <button class="hide" />
    </td>
  </tr>
</table>

When I click on a tr, I trigger a click on the hidden button with JS:

$('table tr').on('click', function(e) {
  var $button = $(this).find('button');  
  console.log('trigger click on', $button);
  
  $button.trigger('click');
});

The click event propagates up, and will cause a never ending loop (can be seen here: http://codepen.io/anon/pen/kDxHy)

After some searching on Stack Overflow, it is clear that the solution is to call event.stopPropagation,
like this (can be seen here: http://codepen.io/anon/pen/BnlfA):

$('table tr').on('click', function(e) {
  var $button = $(this).find('button');
  
  // Prevent the event from bubbling up the DOM tree.
  $button
    .off('click')
    .on('click', function(e) {
      console.log('click');
      e.stopPropagation();
    
    return true;
  });
  
  console.log('trigger click on', $button);
  
  $button.trigger('click');
});

The solution above works. But it feels like a hack, and I don't like it.

Do I have to register a click handler on the on the $button just to call event.stopPropagation? Are there any better way(s) of preventing the click event from bubbling?


Solution

  • A better overall solution would be not to trigger the click event at all but simply have a separate function that you can call and that encapsulates the event handler logic.

    function doSomething(someArg) {
        //do something
    }
    
    $('button').click(function () {
        doSomething(/*pass whatever you want*/);
    });
    
    $('table tr').on('click', function () {
        doSomething(/*pass whatever you want*/);
    });
    

    Please note that doSomething doesn't have an event handler signature such as (e). That's generally a good practice to pass just what's needed to the function to do it's own work, not the whole event object. That will simplify the implementation of doSomething and decouple it completely from the event object.

    EDIT:

    Sorry, but I need to manually trigger the button click when the user clicks on a tr. Please see my comment to @magyar1984 question.

    In this case you probably found the best solution already, which is to attach an event listener on the button and call stopPropagation. You could also look into getting all click event handlers by using the events data on the element, but beware that you would then be relying on jQuery internals so the code could break in future releases.

    Have a look at this question to know how to retrieve the event handlers.