jQuery event delegation html table issue - javascript

I'm having an issue using jQuery event delegation on a html table.
My html table contains a click button on each row which open an additional html window using jQuery. The event works to open the window however the close event does not.
ps: I want to use jQuery event delegation as I add new row in my table after having already bound the above listener.
here is a jsfiddle.
$(".list").on("click", "[data-window-open]", function(e) {
e.preventDefault();
e.stopPropagation();
var targeted_popup_class = $(this).attr('data-window-open');
$('div[data-window="' + targeted_popup_class + '"]:first').toggle();
});
$(".list").on("click", "[data-window-close]", function(e) {
e.preventDefault();
if ($(".window").is(":visible")) {
$(".window").hide();
}
});
.window {
display: none;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<table class="list">
<tr>
<th>name</th>
<th>more</th>
</tr>
<!-- Popup Window 1 -->
<div class="window" data-window="1">
Ryan Sanchez
close
</div>
<tr>
<td>Ryan</td>
<td>click</td>
</tr>
<!-- Popup Window 2 -->
<div class="window" data-window="2">
Pascal Reventon
close
</div>
<tr>
<td>Pascal</td>
<td>click</td>
</tr>
</table>

Change the selectors of the handlers:
$("[data-window-open]").on("click", function(e)
And
$("[data-window-close]").on("click", function(e)

I made the close event as a function which is working with default and added rows.
$(".list").on("click","[data-window-open]", function(e)
{
e.preventDefault();
e.stopPropagation();
var targeted_popup_class = $(this).attr('data-window-open');
$('div[data-window="' + targeted_popup_class + '"]:first').toggle();
closeWindow();
});
function closeWindow()
{
$("[data-window-close]").on("click", function(e)
{
e.preventDefault();
if($(".window").is(":visible"))
{
$(".window").hide();
}
});
}

Related

Cancel/prevent Bootstrap5 collapse when clicking inside links/buttons

I have a table with parent rows, that have hidden child rows. These child rows are shown/hidden using the bootstrap collapse mechanic. Clicking on the parent row toggles the collapse on the child rows. Parent rows and their childs can be dynamically created.
Parent rows can contain links and buttons. I want to cancel/prevent the collapse toggle when clicking on these. A simple minimum example is given below.
A similar question was asked here: stackoverflow - prevent bootstrap collapse from collapsing
The event.stopPropgation(); does not seem to work (see below), not even when targeting the link/button directly (which anyway probably won't help with dynamically created elements).
I've tried to listen to the collapse events from Bootstrap and use the .toggle() method to undo the collapse, but this (obviously) leads to a recursive event.
Is there a simple method to prevent the links/buttons inside the collapse-triggering row from toggling the collapse?
$("#createNewRow").on("click", function() {
var number = Math.round(Math.random() * 100000);
new_row = `
<tr data-bs-toggle="collapse" data-bs-target=".row`+number+`-child">
<td>
This is a dynamically created parent row. Click me to toggle childs.
<a class="child-link" href="!#">A Link!</a>
<button class="child-button">A button!</button>
</td>
</tr>
<tr class="collapse child row`+number+`-child"><td>I'm a child row!</td></tr>
<tr class="collapse child row`+number+`-child"><td>I'm another child row!</td></tr>
<tr class="collapse child row`+number+`-child"><td>I'm yet another child row!</td></tr>
`;
$("#main-table").append(new_row);
});
$(".child-link").on("click", function(e) {
console.log("The link was clicked!");
e.stopPropagation();
});
$(".child-button").on("click", function(e) {
console.log("The button was clicked!");
e.stopPropagation();
});
$(document).on("click", ".child-link", function(e) {
console.log("The link was clicked!");
e.stopPropagation();
});
$(document).on("click", ".child-button", function(e) {
console.log("The button was clicked!");
e.stopPropagation();
});
tr {
background-color: #fcf;
}
.child {
background-color: #efd;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/popper.js/1.16.0/umd/popper.js"></script>
<script src="https://cdn.jsdelivr.net/npm/bootstrap#5.0.1/dist/js/bootstrap.bundle.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap#5.0.1/dist/css/bootstrap.min.css">
<div class="container">
<table class="w-100" id="main-table">
<tr data-bs-toggle="collapse" data-bs-target=".row1-child">
<td>
This is a parent row. Click me to toggle childs.
<a class="child-link" href="!#">A Link!</a>
<button class="child-button">A button!</button>
</td>
</tr>
<tr class="collapse child row1-child"><td>I'm a child row!</td></tr>
<tr class="collapse child row1-child"><td>I'm another child row!</td></tr>
<tr class="collapse child row1-child"><td>I'm yet another child row!</td></tr>
</table>
</div>
<button class="mt-4" id="createNewRow">Create a new set of rows!</button>
This is very good question! The propagation of events is tricky,
You need to conditionally assign an event handler that prevents the Bootstrap collapse events (show and hide) from occurring. The Collapse event will only occur when the parent tr is clicked.
var eventHandler = function (e) {
console.log("The collapse event was prevented!", e);
e.preventDefault();
e.stopPropagation();
}
// initially prevent collapse from toggling
$(".row1-child").on("show.bs.collapse", eventHandler)
// prevent the click event on child links
$(".child-link").on("click", function(e) {
console.log("The link was clicked!");
e.stopPropagation();
e.preventDefault();
});
// prevent the click event on child buttons
$(".child-button").on("click", function(e) {
console.log("The button was clicked!");
e.stopPropagation();
e.preventDefault();
});
// when the parent row is clicked manually toggle the collapsible elements and re-attach event listeners
$("[data-bs-toggle]").on("click", function(e) {
console.log("The parent was clicked! dispatch");
$(".row1-child").off("show.bs.collapse", eventHandler)
$(".row1-child").off("hide.bs.collapse", eventHandler)
$(".row1-child").collapse('toggle')
$(".row1-child").on("show.bs.collapse", eventHandler)
$(".row1-child").on("hide.bs.collapse", eventHandler)
});
Demo

Disabling a click event from the child of a parent div

I am trying to add a on click event to a div with a class parent. Now inside that div I have a div with class child that has its own click event.
How can I manage to disable the click event of the parent function for that child element in order to execute the function of child element itself?
I have tried using pointer-event:none; but it does not seem to be working. I have wrote a jsfiddle for better understanding.
https://jsfiddle.net/arq1epbs/
$(document).on('click', '.parent', function() {
var url = $(this).attr("data-url")
document.location.href = url
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<div class="parent" data-url="www.google.com">
Im the parent
<div class="child">
im the child and I don't want to go to Google
</div>
</div>
Thanks for all the help in advance!
You can use stopPropagation():
$(document).on('click', '.parent', function () {
var url = $(this).attr("data-url")
document.location.href = url
});
$(document).on('click', '.child', function (e) {
e.stopPropagation();
});
As it's not working in the Stack Snippet, here a Fiddle
For reference: stopPropagation()
You can simply call event.stopPropagation() inside child click event, to prevent the event from bubbling up the DOM tree, preventing any parent handlers from being notified of the child click event like:
$(document).on('click', '.parent', function() {
//var url = $(this).attr("data-url")
//document.location.href = url
console.log('Parent Clicked');
});
$(document).on('click', '.child', function(event) {
event.stopPropagation();
console.clear();
console.log('Child Clicked');
});
.parent{background:#99c0c3;width:350px;height:120px;position:relative}
.child{background:#ffde99;width:300px;height:50%;position:absolute;left:50%;top:50%;transform:translate(-50%,-50%)}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<div class="parent" data-url="www.google.com">
Im the parent
<div class="child">
im the child and I don't want to go to Google
</div>
</div>
Just add this line:
$(document).on('click', '.parent', function (e) {
if(e.target !== this) return false; //This line added
var url = $(this).attr("data-url")
document.location.href = url
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<div class="parent" data-url="www.google.com">
Im the parent
<div class="child">
im the child and I don't want to go to Google
</div>
</div>
You can do this in "pure" JS
document.querySelector('div.parent').onclick = evt =>
{
if (!evt.target.matches('div.parent')) return // reject sub elements click
document.location.href = evt.target.dataset.url
}
div.parent { cursor: pointer }
div.child { cursor: default }
<div class="parent" data-url="www.google.com">
Im the parent
<div class="child">
Im the child and I don't want to go to Google
</div>
</div>

Track all elements on page

I'm attempting to track events for all UI elements on a page. The page contains dynamically generated content and various frameworks / libraries. Initially I tracked elements through creating a css class "track" , then adding style "track" to tracked elements. elements are then tracked using :
$('.track').on('click', function() {
console.log('Div clicked' + this.id);
console.log(window.location.href);
console.log(new Date().getTime());
});
As content can be dynamically generated I wanted a method to track these elements also. So tried this using wildcard jQuery operator.
In this fiddle : https://jsfiddle.net/xx68trhg/37/ I'm attempting to track all elements using the jquery '*' selector.
Using jQuery '*' selector appears to fire the event for all elements of given type.
So for this case if is clicked all the click event is fired for all divs. But id is just available for div being clicked.
For the th element the click event is fired twice , what is reason for this ?
Can the source be modified that event is fired for just currently selected event ?
fiddle src :
$(document).ready(function() {
$('*').each(function(i, ele) {
$(this).addClass("tracked");
});
$('.tracked').on('click', function() {
console.log('Div clicked' + this.id);
console.log(window.location.href);
console.log(new Date().getTime());
});
});
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.9.1/jquery.min.js"></script>
<!-- <div id="1" data-track="thisdiv">
Any clicks in here should be tracked
</div>
-->
<div id="1">
Any clicks in here should be tracked 1
</div>
<div id="2">
Any clicks in here should be tracked 2
</div>
<div id="3">
Any clicks in here should be tracked 3
</div>
<th id="th">tester</th>
You can try with:
$(document).ready(function() {
$("body > *").click(function(event) {
console.log(event.target.id);
});
});
$(document).ready(function() {
$("body > *").click(function(event) {
console.log(event.target.id);
});
});
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div id="1">
Any clicks in here should be tracked 1
</div>
<div id="2">
Any clicks in here should be tracked 2
</div>
<div id="3">
Any clicks in here should be tracked 3
</div>
<table>
<tr>
<td>Cols 1</td>
<td id="td">Cols 2</td>
</tr>
</table>
<p id="th">tester</p>
You may want to use event delegation to target the elements you need. Advantage is that this also works for dynamically generated elements. See code for an example of this.
// method to add/set data-attribute and value
const nClicksInit = (element, n = "0") => element.setAttribute("data-nclicked", n);
// add data-attribute to all current divs (see css for usage)
// btw: we can't use the method directly (forEach(nClicksInit))
// because that would send the forEach iterator as the value of parameter n
document.querySelectorAll("div").forEach(elem => nClicksInit(elem));
// add a click handler to the document body. You only need one handler method
// (clickHandling) to handle all click events
document.querySelector('body').addEventListener('click', clickHandling);
function clickHandling(evt) {
// evt.target is the element the event is generated
// from. Now, let's detect what was clicked. If none of the
// conditions hereafter are met, this method does nothing.
const from = evt.target;
if (/^div$/i.test(from.nodeName)) {
// aha, it's a div, let's increment the number of detected
// clicks in data-attribute
nClicksInit(from, +from.getAttribute("data-nclicked") + 1);
}
if (from.id === "addDiv") {
// allright, it's button#addDiv, so add a div element
let newElement = document.createElement("div");
newElement.innerHTML = "My clicks are also tracked ;)";
const otherDivs = document.querySelectorAll("div");
otherDivs[otherDivs.length-1].after(newElement);
nClicksInit(newElement);
}
}
body {
font: 12px/15px normal verdana, arial;
margin: 2em;
}
div {
cursor:pointer;
}
div:hover {
color: red;
}
div:hover:before {
content: '['attr(data-nclicked)' click(s) detected] ';
color: green;
}
#addDiv:hover:after {
content: " and see what happens";
}
<div id="1">
Click me and see if clicks are tracked
</div>
<div id="2">
Click me and see if clicks are tracked
</div>
<div id="3">
Click me and see if clicks are tracked
</div>
<p>
<button id="addDiv">Add a div</button>
</p>
<h3 id="th">No events are tracked here, so clicking doesn't do anything</h3>
You can invoke the stopPropagation and the condition this === e.currentTarget to ensure invoke the handler function of the event source DOM.
And you must know the <th> tag must wrapped by <table>, otherwise it will not be rendered.
$(document).ready(function() {
$('*').each(function(i, ele) {
$(this).addClass("tracked");
});
$('.tracked').on('click', function(e) {
if (this === e.currentTarget) {
e.stopPropagation();
console.log('Div clicked' + this.id);
console.log(window.location.href);
console.log(new Date().getTime());
}
});
});
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.9.1/jquery.min.js"></script>
<!-- <div id="1" data-track="thisdiv">
Any clicks in here should be tracked
</div>
-->
<div id="1">
Any clicks in here should be tracked 1
</div>
<div id="2">
Any clicks in here should be tracked 2
</div>
<div id="3">
Any clicks in here should be tracked 3
</div>
<table>
<th id="th">tester</th>
</table>

Adding click event to markup I can't edit

I am not able to edit any of the following markup and I am trying to add a click event to the .datepick-days-cell element.
<td class="datepick-days-cell onclick="jQuery.datepick._selectDay(this,'#calendar_booking5',1493179200000)" ></td>
I tried the following simple jQuery on click event but had no luck:
$('.datepick-days-cell').on('click', function() {
alert('test click');
});
I'm assuming this does not work because it can't take precedence over the event handler that is bound to the element. Any ideas?
Following worked for me:
<script src="https://code.jquery.com/jquery-1.9.1.min.js"></script>
<script>
$( document ).ready(function() {
$('.datepick-days-cell').on('click',function() {
alert('test click');
});
});
</script>
<table border="1">
<tr>
<td class="datepick-days-cell" onclick="jQuery.datepick._selectDay(this,'#calendar_booking5',1493179200000)" >Test Click</td>
</tr>
</table>
Does this work?
$('.datepick-days-cell').click(function() {
alert('test click');
});

Button within clickable table cell

I have a table cell which is clickable and fires a jQuery event when clicked. Within that cell I also have a button, which has another jQuery event when clicked. The issue is, when the button is clicked both the cell and button events are fired.
For example:
<script>
$(document).ready(function () {
$('#cell').click(function () {
alert('cell clicked');
});
$('#button').click(function () {
alert('button clicked');
});
});
</script>
<table>
<tr>
<td id="cell">
<button id="button">go</button>
</td>
</tr>
</table>
How can I prevent the cell click event being fired when the button is clicked?
You can use stopPropagation() which allow you to stop bubbling of event to the parent dom.
Example
$('#button').click(function (e) {
e.stopPropagation();
alert('button clicked');
});
set the table width to 100% and test it.
Test Code
<script type="text/javascript" src="js/jquery.js"></script>
<script type="text/javascript">
$(function()
{
$('#cell').click(function () {
alert('cell clicked');
});
$('#button').click(function (e) {
e.stopPropagation();
alert('button clicked');
});
});
</script>
<table width="100%">
<tr>
<td id="cell">
<button id="button">go</button>
</td>
</tr>
</table>
stop the event propagation which is called event bubbling to the parent:
$('#button').click(function (e) {
e.stopPropagation();
alert('button clicked');
});
You need to use
stopPropagation
This example should fix it:
$(document).ready(function () {
$('#cell').click(function () {
alert('cell clicked');
});
$('#button').click(function (e) {
e.stopPropagation();
alert('button clicked');
});
});
That should fix it.
$(document).ready(function(){
$('#cell').click(function(event){
if($(event.target).is('#button')){
event.stopPropagation();
}
});
});

Categories