Reputation: 2428
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
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
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