NineCattoRules
NineCattoRules

Reputation: 2428

How can I trigger a button inside a table using jQuery?

I'm trying to trigger a button (inside one of my table) when the user clicks on a <tr> tag object, as below:

    $(document).ready(function(){
      $('body').on('click', 'tr', function(){
        var tridx = $(this).data('track-id');
        $('td div#play'+tridx).click();
      });
    });
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<table class="sortable">
      <thead>
        <tr>
          <th data-sort="nope">id</th>
          <th data-sort="name">Artist</th>
          <th data-sort="name">Title</th>
          <th data-sort="name">Genre</th>
          <th data-sort="duration">Duration</th>
          <th data-sort="date">Release</th>
        </tr>
      </thead>
      <tbody>						
    	<tr data-track-id="252">			
    		<td>
    			<div data-track-name="1" data-track-id="252" id="play252" class="playbtn252" style="display: none;"></div>
    			<div id="countlist252" data-count-list="1" style="display: block;">1</div>
    		</td>
            <td>Simon Deoro</td>
            <td>1</td>
            <td>1</td>
            <td>3:47</td>
            <td>2016-12-06</td>
        </tr>
        <!-- more tr -->
      </tbody>
    </table>

The tridx is correct, however I get this error from console:

Uncaught RangeError: Maximum call stack size exceeded

What's wrong in my code, I cannot understand it seems everything correct to me?

Upvotes: 2

Views: 1062

Answers (2)

wally
wally

Reputation: 3592

The problem is your function call (handling the click event) going recursive.

jQuery's .on(...) is capturing the click event for the specified selector (tr in your case) and everything beneath it. So the click event of the button inside the tr also fires the anonymous function you've defined.

In your scenario this is not desirable because the user's click triggers another click event, triggering another click event, and so on.

Every call to a function from inside a function generates a new entry on the call stack, which eventually grows too big and falls over. (That's the symptom you're seeing.) The stack shrinks back down only when a function actually returns... (which is never happening for you).

There are various ways you could avoid this problem. Possibly simplest: just forget having a click handler on the button itself (as the button's click triggers the containing tr click event anyway).

Alternatively you could tell jQuery to stop applying the click event up the DOM tree...

Quoting the jQuery documentation on this subject:

By default, most events bubble up from the original event target to the document element.

At each element along the way, jQuery calls any matching event handlers that have been attached. A handler can prevent the event from bubbling further up the document tree (and thus prevent handlers on those elements from running) by calling event.stopPropagation().

Any other handlers attached on the current element will run however.

To prevent that, call event.stopImmediatePropagation(). (Event handlers bound to an element are called in the same order that they were bound.)

Cite: http://api.jquery.com/on/


On a semi-related note, I'd strongly recommend structuring your jQuery code in a way which makes separation of event handlers vs private methods vs public methods as clear as possible. There's a variety of "recommendations" (I certainly can't say standards!) on how to do it - my personal favourite is here: https://stackoverflow.com/a/5947280/817132

Using this method, I create a "namespace" for all the UI code (assuming it's only a small codebase) and have one "setup" function (invoked by onReady) which connects all the event handlers to their relevant selectors. Those event handlers are as minimal as possible, mostly invoking private functions containing the real logic.

As a matter of personal preference, I avoid anonymous functions as much as possible. I attach references to functions instead.


On a semi-semi related note ... if you've not already, I would strongly recommend getting to grips with your browser's developer tools. (My personal favourite is that which ships with Chrome.)

Specifically look into breakpoints and learn how to step line by line (and in and out of functions) while using watches.

For example: https://developers.google.com/web/tools/chrome-devtools/javascript/add-breakpoints

Upvotes: 3

iCollect.it Ltd
iCollect.it Ltd

Reputation: 93571

As I mentioned in comment, the cause is recursively generating a click event on an element inside the same TR.

Just check that the click event is not from the play button before clicking the play button:

$(document).ready(function(){
  $('body').on('click', 'tr', function(e){
    var tridx = $(this).data('track-id');
    var $button = $('td div#play'+tridx);
    if (!$button.is(e.target))
    {
        $button.click();
    }
  });
});

Upvotes: 2

Related Questions