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
Related
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>
I'm having some trouble figuring out how to close a div by clicking anywhere on the screen.
I'm currently toggling an 'active' class in order to display a drop down div, then attempting to remove that class by clicking on the body:
$(document).ready(function () {
$('.navbar a').click(function () {
$(this).next('.navbar-dropdown').toggleClass('active');
});
$(body).click(function() {
if($('.navbar-dropdown').hasClass('active')){
$('.navbar-dropdown').removeClass('active');
}
});
});
<ul class="navbar">
<li>
Link
<div class="navbar-dropdown">
Dropdown Content
</div>
</li>
</ul>
However they are conflicting with each other, so as soon as the class is toggled on, the 'body' click toggles it off at the same time. Have spent some time looking on here and came across this method a few times:
$(document.body).click( function() {
closeMenu();
});
$(".dialog").click( function(e) {
e.stopPropagation();
});
However any attempts to configure this to work correctly seemed to fall on deaf ears!
The click event from the navbar is bubbling up to the body, so both events fire. stopPropagation() is one way to prevent that, but you need to do it in the navbar link's event handler, so it stops that particular event; not in a separate event handler as you had it.
Another change you might consider making is to only assign the body click handler when you need it, instead of firing all the time -- create that handler inside the navbar's click handler, and deactivate it again when it's used:
$(document).ready(function() {
$('.navbar a').click(function(e) {
var myDropdown = $(this).next('.navbar-dropdown');
$('.navbar-dropdown.active').not(myDropdown).removeClass('active'); // close any other open dropdowns
myDropdown.toggleClass('active'); // open this one
$('body').click(function() {
// no need for an if statement here, just use a selector that matches the active elements:
$('.navbar-dropdown.active').removeClass('active');
$('body').off('click'); // cancel the body's click handler when it's used
});
e.stopPropagation(); // prevent the navbar event from bubbling up to the body
});
});
.active {
color: red
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<ul class="navbar">
<li>
Link
<div class="navbar-dropdown">
Dropdown Content
</div>
</li>
<li>
Link 2
<div class="navbar-dropdown">
Dropdown Content 2
</div>
</li>
<li>
Link 3
<div class="navbar-dropdown">
Dropdown Content 3
</div>
</li>
</ul>
(If there's a chance you might need more than one separate click event handler on the body, you can namespace the event so you can control which one you're turning off:
$('body').on("click.myNamespace", function() {
// do other stuff
$('body').off("click.myNamespace")
})
I did the exact thing as you and it works for me. Are you sure you don't have any other event listeners attached? Or maybe a z-index on the menu bringing it underneath other elements?
$(document).click(function(e) {
$(".dialog").text('closed')
});
$(".dialog").click(function(e) {
e.target.innerText = 'open';
e.stopPropagation();
});
.dialog {
width: 200px;
height: 200px;
background: antiquewhite;
text-align: center;
}
<html>
<head>
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
</head>
<body>
<div class="dialog">open</div>
</body>
</html>
In my code, I have added onclick on parent div and want to perform other action on inner div, but clicking on inner div also triggering parent click.
how to stop that?
$(document).on('click', '.child', function(e) {
e.preventDefault();
e.stopPropagation();
console.log('child');
});
function parentfun(sender) {
console.log('parent');
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div class="parent" onclick="parentfun(this)">
parent
<div class="child">child</div>
</div>
Above divs are generated on run time on some other event.
Clicking on child, also trigger parent's click. preventDefault & stopPropagation are not working.
FYI: my question is different than How do I prevent a parent's onclick event from firing when a child anchor is clicked?
What you are actually doing here is binding the click-event to the document, not the child-element. So the event has already bubbled up all the way to the document, it's too late to try to stop the bubbling with stopPropagation.
See here when I change the click-handler to the child instead:
$(".child").on('click', function(e) {
e.preventDefault();
e.stopPropagation();
console.log('child');
});
function parentfun(sender) {
console.log('parent');
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div class="parent" onclick="parentfun(this)">
parent
<div class="child">child</div>
</div>
Edit
As the question changed a bit, here is what you can do (for example) if the elements are created dynamically:
$(document).on('click', '.parent, .child', function(e) {
e.stopPropagation();
if ($(this).is(".child")) {
console.log('child');
} else {
console.log('parent');
}
});
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div class="parent">
parent
<div class="child">child</div>
</div>
Using plain vanilla JS it works as expected:
function logEventTarget(e) {
e.stopPropagation();
console.log(e.target.id);
}
parentDiv.addEventListener('click', logEventTarget)
childDiv.addEventListener('click', logEventTarget)
<div id="parentDiv">
parent
<div id="childDiv">child</div>
</div>
Using an inline event handler won't pass the event to the handler function:
function logEventTarget(e) {
e.stopPropagation();
console.log(e.target.id);
}
childDiv.addEventListener('click', logEventTarget)
<div id="parentDiv" onclick="logEventTarget()">
parent
<div id="childDiv">child</div>
</div>
One of the many reasons you shouldn't use inline event handlers at all. Note that e.stopPropagation() still works for the childDiv.
You can notice that when clicking the chlid element the parent triggers first (that is why parent prints first then child ) because of event capturing which precedes event bubbling. In-order to stop the event capturing phase from parent you can stop propagating that event and then only child event will trigger.
$(document).on('click', '.child', function(e) {
//e.preventDefault();
e.stopPropagation();
console.log('child');
});
$(document).on('click', '.parent', parentfun);
function parentfun(e) {
e.stopPropagation();
console.log('parent');
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div class="parent">
parent
<div class="child">child</div>
</div>
You can also resolve this problem by editing little bit in Your html code
<div class="parent" id="parent-div">
<!-- Just adding parent div text inside span tag -->
<span>parent</span>
<div class="child">child</div>
</div>
now go to jquery code
$('.parent span').on('click',function(){
//only print child text u can do more
alert($('.child').text());
//Change color of child
$('.child').css('color','red');
});
I have a parent div within which elements are added dynamically. I want to find out the class of the element which was clicked, if none of the child element was clicked but parent was clicked then I would like to get the class of parent element.
$(document).ready(function() {
$('.parent').on('click', function(event) {
// event.stopPropagation();
alert($(this).attr('class'));
});
});
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.0/jquery.min.js"></script>
<div class='parent'>Parent//clicking here should alert parent
<ol>
<li class='type1'>NEW</li>//clicking on this should alert type 1
</ol>
</div>
try this:
$(document).ready(function() {
$('.parent').on('click', function(event) {
// event.stopPropagation();
alert($(event.target).attr('class'));
});
});
this will get the target tag of the clicked position.
see the DEMO
I have one drop target div which have some nested div's, i add dragover event to parent div and add 'drag-allow' attrib to nested divs. I want to add custom event fires when mouse enters or leave any child div.
<div id="container">
<div id="page1" drag-allow >
Page #1
</div>
<div id="page2" drag-allow>
Page #2
</div>
<div id="page3" drag-allow>
Page #3
</div>
var ground = document.getElementById('container');
...
ground.addEventListener('dragover', function(e){
evt.preventDefault();
evt.stopPropagation();
evt.target.id; // custom event will fire based on child div's id
}, false);
You need to add two more events like
ground.addEventListener('dragenter', function(e){
if(e.target.nodeType == Node.ELEMENT_NODE && e.target.hasAttribute('drag-allow')){
// this will check drop area is div and must have attribute drag-allow
console.log(e.target);
}
}, false);
ground.addEventListener('dragleave', function(e){
// on leave event fires on every nested div like above event
}, false);