Stop events inside delegate with same handler - javascript

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

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.
});

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");
});

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.

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.

Related

Trigger event on parent originating, bubbling up from child

If I have a <div class="timely"></div> containing a table, in that table is a <th class="prev"><i>previous</i></th>...Chrome dev tools say there is an event listener on the <th>, but Firefox dev tools points out it is not on the <th>, but the parent .timely, which Chrome also points out when viewing what is in the handler for this event.
What I am trying to do is replicate what happens on click, to happen on keyup. It doesn't seem as simple as, in jQuery:
$('.timely th').each(function(){
$(this).on('keyup', function(){
$(this).trigger('click');
});
});
Because the event handler is on the .timely and it is listening from where the event bubbled up from when executing the code.
How can I replicate the click event on keyup of .timely with the context of it bubbling from the <th>?
First, note there's no reason for your each loop (unless you're doing something else in it you haven't shown). Remember that jQuery is set-based, so $("selector").on(...) sets up the handler on all elements in the set.
Re your question: Accept the event argument and use its target property as the element on which to trigger:
$('.timely th').on('keyup', function(e) {
$(e.target).trigger('click');
});
Or if you want to handle it up on .timely instead of on the th in .timely, just change the selector and use the delegating form (assuming you only want this for the th elements):
$('.timely').on('keyup', 'th', function(e) {
$(e.target).trigger('click');
});
It's tricky to get keyup on a th element, of course; the only way immediately coming to mind is to put an input element in the th so the keyup bubbles:
$('.timely th').on('keyup', function(e) {
console.log("keyup");
$(e.target).trigger('click');
});
$('.timely th').on('click', function(e) {
console.log('click');
});
<div class="timely">
<table>
<tbody>
<tr>
<th>
<input type="text">
</th>
</tr>
</tbody>
</table>
</div>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
You haven't said why you want to trigger click on keyup, but if it's just to run the click event handler, I don't recommend doing that. Instead, just use the same function for both the keyup and click handler. You can hook multiple events just by space-delimiting them in .on (.on("click keyup", ...)), or use a named function and refer to it where you hook up your click and keyup handlers.
Chrome dev tools say there is an event listener on the <th>, but Firefox dev tools points out it is not on the <th>, but the parent .timely
If you don't want to see handlers attached to ancestors, untick the Ancestors box in the Event Listeners tab:
But note that in your example code, the handler is definitely on the th, not the ancestor .timely element.
this jquery magic may not will work fast
$('.timely th').each(function(){
$(this).on('keyup', function(){
$(this).trigger('click');
});
});
you can abstract function that you want use on click and on keyup
function clickKeyupHandler (e) {
// ...
}
// somewhere when you use it use same functions to handle click and keyup
// $('.timely').on('click', clickKeyupHandler);
$('.timely').on('keyup', 'th', clickKeyupHandler);

DataTable How to prevent row click in columns containing hyperlinks

I am dealing with a javascript datatable with clickable rows. Each row when clicked forwards the browser to a different page.
A column of the row contains a hyperlink. When the hyperlin is clicked I don't want the row click event to be fired or managed.
Here is how I have implemented the row click event
$(tableId+' tbody').on( 'click', 'tr', function (e) {
window.location.href = $(this).attr('url');
});
When you add an event listener to a parent, the event by default "bubbles" downwards and attaches the event to all children. So in this case you setting the event on tr (parent) and the event will bubble down and attach itself to the td (children). So one of these td from what I understand contains your href (hyperlink) and so to block the click event which originally was set on the parent, you have to set another event listener specifically of the event type click within the function for this event you simply need state event.stopPropagation() and it will override the tr click event which bubbled down.
document.addEventListener('DOMContentLoaded',()=>{
//SET EVENT ON PARENT(TR), EVENT BUBBLES BY DEFAULT DOWNWARDS THEREFORE TD's WILL INHERIT
document.getElementById('parent').addEventListener('click',()=>window.location.href = "wherever");
//ADDING ANOTHER EVENT LISTENER OF THE SAME TYPE (CLICK) WHICH BLOCKS THE BUBBLING PROPOGATION DOWNARDS
document.getElementById('myhyperlink').addEventListener('click',(e)=>event.stopPropagation());
});
If you want to prevent clicking on anchor from redirecting a page, you have to simply catch the click event and disable it's default behaviour.
$("table td a").click(function(e){
e.preventDefault();
});
You have to just prevent default behaviour, if you would also stop propagation of event, your other event listener wouldn't get called. See more info here: What's the difference between event.stopPropagation and event.preventDefault?
If your goal is to disable redirect of anchor links only when the javascript is enabled, another solution might be to set onclick attribute that would disable default redirect. However, this seems not to work in webkit based browsers.
and example
Here is jsFiddle with example
Use preventDefault()
$(tableId+' tbody').on( 'click', 'tr', function (e) {
e.preventDefault();
window.location.href = $(this).attr('url');
});
You can write code following ways:
$(tableId+' tbody').on( 'click', 'tr', function (e) {
e.preventDefault();
window.location.href = $(this).attr('url');
});
you have to disable row click for that particular column
$('tableId+' tbody'').on('click', 'td', function () {
if ($(this).index() == 4 ) { // provide index of your column in which you prevent row click here is column of 4 index
return;
}
// you can do additional functionality by clicking on this column here
});

How to prioritize Nested JavaScript events?

I have an edit button inside a table cell.
The table cell event has an "click" event, And the edit button who is inside a cell has another click event set to it.
Is there a way to prioritize the button event over the table cell event?
I tried using a different z-index for the button, but it didn't work.
The button cannot go outside the the table as its closest tr holds id data which is crucial for the proper event to function.
You can call event.stopPropagation() as part of the click handler for the button, to prevent the click event from bubbling up to the table cell. Documentation: http://api.jquery.com/event.stopPropagation/
Inside the click handler on the button, you just need to call event.stopPropagation().
element.onclick= function(event) {
// ...
event.stopPropagation();
// ...
}
Look into a tutorial on bubbling and capturing events, such as http://javascript.info/tutorial/bubbling-and-capturing

How to select check box without selecting the whole row (see body)?

Say, I have an html:
<div class="row">
<input type="checkbox">
</div>
When user clicks on the whole row, it becomes highlighted (selected class added by an onClick event). I attach onClick event to elements with class .row.
When user clicks on checkbox (which is inside .row), this checkbox becomes selected. But row should not be highlighted.
Is it possible to exclude the area of the checkbox from the area of the .row for an onClick event?
UPDATE
Here is what I have now: http://jsfiddle.net/saAGU/
I don't want class to be toggled when I click exactly on checkbox.
UPDATE 2
Here is the working solution with jQuery for future use: http://jsfiddle.net/saAGU/2/
Yes, it's possible. The right way to do it is putting a onclick event on the checkbox, capture the event and stop its propagation.
Something like this:
function checkboxClick (e) {
e.stopPropagation();
}
Please tell me if it worked
add click event to every checkbox and stop its propogation .this should work -
e.stopPropagation in event handler for checkbox click
http://jsfiddle.net/saAGU/3/
If you capture the event, and look at the event.toElement, you can see if they clicked on the .row or something else.
http://jsfiddle.net/saAGU/1/
$('.row').click(function(event){
if (event.toElement !== this) // Did the user click on the row directly?
return;
$(this).toggleClass('selected');
});

jquery tablerow click function

I have a function that allows users to edit a row in a table by opening the data in a modal. Right now the whole row will fire the modal. What I want is to isolate the last td that contains a link that fires a function that readies the data for processing.
What happens is that when that last td is clicked the modal opens AND the ready function fires. I'm trying to stop the modal from opening.
I also have a mouseover row highlighting on each row of the table.
This is how I associate the table rows with the modal function:
$("#table").find("tr").click(function(e) {
//code to open model
}
This is how I fire the ready function:
$("#table tr td:last-child a").click(function(e) {
//code to open model
}
i believe you need to call e.stopPropogation() to prevent the event from propogating:
$("#table tr td:last-child a").click(function(e) {
//code to open model
e.stopPropagation();
return false;
}
Explanation:
Events propagate from the most "specific" element up to their parents. This is called bubbling, and in this case, your second modal is being fired because the click event is bubbling up from the A element to the TR element.
I believe all events propagate and bubble. Return false simply cancels the default action of the A element, which is to follow the link... it doesn't stop the event object.
Here's a good explanation of the whole thing.
Simply add "return false;":
$("#table tr td:last-child a").click(function(e) {
// whatever should happen when clicking the anchor
return false;
}
This prevents the event from propagating to the surrounding tr.
why instead of finding the link inside the last cell of any row in the table just assign a id or a class to your link
like ...
and then add the display : block property from the css which makes the anchor to fill the whole area it is contained in.
then simply
$('.anchorclass').click(function(){
// do something here
});

Categories