Stop propagation on parent div, when children are bound through $(body)? - javascript

I have a container <div> that has dynamically appended divs. The divs are given click handles through the body element, like this: $(body).on("click",".divClass",function); (I did it this way so that the bindings on the dynamically added elements can be done all in one place in the code, and to prevent tons of bindings). I need to put a stopPropagation() on the container div, so that the click handle on its parent doesn't get triggered. The problem is, when I put in the stopPropagation on the container div, it stops the child div from triggering (since it is actually the body that has the binding, not the child div)
Here is a fiddle that demonstrates my problem:
$("#A").click(function() {
console.log('This event should not fire when clicking innermost')
});
$("#B").click(function(e) {
e.stopPropagation();
});
$("body").on("click", "#C", function() {
console.log('This event should fire when clicking innermost')
});
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div id="A">
outermost
<div id="B">
middle
<div id="C">
innermost
</div>
</div>
</div>

I want the other children to propagate as normal, only the children in
#B should not propagate.
Taking this into account, your original code wasn't too far off. Seeing you are binding directly to #B in your example, I assume #B is static.
In that case use #B as the static reference instead of body.
$("#A").click(function(e) {
console.log('This event should not fire when clicking innermost')
});
$("#B").click(function(e) {
e.stopPropagation();
});
$("#B").on("click", "#C", function(e) {
console.log('This event should fire when clicking innermost')
});
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div id="A">
outermost
<div id="B">
middle
<div id="C">
innermost
</div>
</div>
</div>

I think this whole problem would be much simpler if you bound the delegated handler to #B in the first place. Then the events don't need to bubble up to the body and stopPropagation will have the desired effect.
$("#A").click(function() {
console.log('This event should not fire when clicking innermost')
});
$("#B").on("click", "#C", function(e) {
e.stopPropagation();
console.log('This event should fire when clicking innermost')
});
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div id="A">
outermost
<div id="B">
middle
<div id="C">
innermost
</div>
</div>
</div>

Related

Show/HIde Elements on click of parent/child

I am having issues with my click events due to nested elements and the nature of what I am trying to accomplish.
When I click on the .main, the selected class is added. However when I click on .close, the selected class is removed and then added back again. Is there anyway to get around this?
The reason why I have it setup this way is because I want to maximize my clickable area (the entire element).
$('.main').off('click.a').on('click.a', function() {
$('.child2', $(this)).addClass('selected');
});
$('.close').off('click.B').on('click.B', function() {
$(this).parent().removeClass('selected')
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.6.1/jquery.min.js"></script>
<div class="main">
<div>some content</div>
<div class="child2">
<span class="close">X</span>
</div>
</div>
The issue is because the .close element is a child of the .main. This means that the event fires on .close, and the class is removed. The event then propagates up the DOM to the .main element where it's caught by the other event handler and the class gets added on again.
To prevent the event from propagating up the DOM, call stopPropagation() on it from within the .close click handler:
$('.main').off('click.a').on('click.a', function() {
$(this).find('.child2').addClass('selected');
});
$('.close').off('click.B').on('click.B', function(e) {
e.stopPropagation();
$(this).parent().removeClass('selected')
});
.selected { color: #C00; }
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.6.1/jquery.min.js"></script>
<div class="main">
<div>some content</div>
<div class="child2">
<span class="close">X</span>
</div>
</div>
As a side note, using .off('event').on('event', fn) is a code smell. You may be better off using a single delegated event handler which you can bind only once when the page loads. It would depend on your exact use case, though.
You could double check it's actually the correct item.
$('.main').off('click.a').on('click.a', function(e) {
if (!$(e.target).hasClass('.main')){ return };
$('.child2', $(this)).addClass('selected');
});
$('.close').off('click.B').on('click.B', function(e) {
if (!$(e.target).hasClass('.close')){ return };
$(this).parent().removeClass('selected')
});
I've had similar issues when I wanted an event to go off when I click the whitespace in Table Cells but not the contents itself.. if that makes any sense

Event propagation issue

I have a series of click events which are causing me some propagation issues. The data for this container is loaded in via ajax hence the body on click method. Here's the code I have:
$(function () {
$("body").on("click","#top-div",function(){
console.log("top event");
});
$("body").on("click","#middle-div",function(e){
e.stopPropagation();
});
$("body").on("click","#bottom-div",function(){
console.log("bottom event");
});
});
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div id="top-div">
top
<div id="middle-div">
middle
<div id="bottom-div">
bottom
</div>
</div>
</div>
The top div has an event which the middle one needs to not inherit hence the stopPropagation(). However the bottom one needs to have an event of its own but the stopPropagation() from the middle one is stopping it from executing the event (if I remove the stop propagation the bottom event triggers but coincidentally so does the top)
How can I get around this bubbling issue?
I think what you want to happen is that the propagation is only stopped if it originated from the middle div; if it originated from the bottom div you want the event to bubble all the way to the top.
You need to call stopPropagation conditionally. If the event did not originate on or inside #bottom-div, you want to call it. You can test for this using closest:
if (!$(e.target).closest('#bottom-div').length) {
e.stopPropagation();
}
It is better to use closest for this, rather than testing the element's id directly, because with closest it will continue to work as expected even if there are other elements (a or em, for example) within the various divs.
$(function () {
$("body").on("click","#top-div",function(){
console.log("top event");
});
$("body").on("click","#middle-div",function(e){
if (!$(e.target).closest('#bottom-div').length) {
e.stopPropagation();
}
});
$("body").on("click","#bottom-div",function(){
console.log("bottom event");
});
});
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div id="top-div">
top
<div id="middle-div">
middle
<div id="bottom-div">
bottom
</div>
</div>
</div>
Bubbling works from most nested element to tree root, I think You misunderstanding it in some way. So for example click in bottom-div will:
call event on bottom-div --> call event on middle-div --> call event on top-div.
As I understand the need You want to some how check what div was clicked, so to have control on it just create single event and check clicked target like that:
$("body").on("click","#top-div",function(e){
if ( e.target.id ==='top-div'){
//click in top div
}
if ( e.target.id ==='middle-div'){
//click in middle div
}
if ( e.target.id ==='bottom-div'){
//click in bottom div
}
});
In this proposition You know exactly which div was clicked, but it will always be the most nested one, so target will be middle-div only when will be clicked part of middle-div which is not bottom-div, it will be for example padding space, the same is with top-div it will be target only if click will be in space without child elements.
More about event bubbling You can find in http://maciejsikora.com/standard-events-vs-event-delegation/.
Instead of attaching event to every div. You can consider doing it as follows
check this snippet
$(document).ready(function() {
$("body").on("click", "div", function(event) {
console.log(event.currentTarget.id + "event");
if (event.target.id === "middle-div") {
event.stopPropagation()
}
});
});
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div id="top-div">
hello
<div id="middle-div">
hi
<div id="bottom-div">
bott-mid-v
</div>
</div>
</div>
Hope it helps

How can I force to call dynamically added element,s onclick() first

<div id="outerDiv" onclick="javascript:addEvent()">
<div id="inner">
<a class='editEV' href="javascript:void(0)">event1</a>//*added dynamically from addEvent method.*
</div>
</div>
My script is:
$('#inner').on('click','.editEV',function(){
editEvent();
});
When I click on anchor addEvent() called first then editEvent(), but I want that when I click on div then addEvent() should call and when I click anchor then editEvent.
I am aware about bubbling so that's why I introduce an inner static div to bind listener, but still addEvent() calls first. I am unable to figure out how can I force to call editEvent first.
You are using both - inline event set ( onclick=.. ) and Jquery on. Choose one of them to have more clear code. I would choose jquery on, and solution for this problem is to create two events - one on div, second on edit element, but in second We need to stop bubbling by using e.stopPropagation method. Here full working example:
$(function(){
$("#outerDiv").on("click",function(e){
console.log("Add click");
});
$("#outerDiv").on("click",".editEV",function(e){
console.log("Edit click");
e.stopPropagation();//very important to stop propagate event
});
});
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div id="outerDiv" style="padding:20px; background:grey;">
<div id="inner">
<a class='editEV' >event1</a>
</div>
</div>
Without stopPropagation our event is going up to div and runs its event, stopPropagation avoids propagate event to upper elements.
You also use this if you don't want to alter your code.
$(function(){
$("#outerDiv").on("click",function(e){
addEvent();
});
$("#outerDiv").on("click",".editEV",function(e){
editEvent();
e.stopPropagation();
});
});
e.stopPropagation();
use this.
If you want to read more on .stopPropagation(), look here.

Element on element gets clicked both by Javascript

I've got a div which has multiple elements nested. If I press on the div, the buttons appear in the same DIV. If I press on the button however, the buttons dissapear as well (this is because of a toggleClass on the div)
I've tried returning but that isn't working as well. See below for a JSFiddle
http://jsfiddle.net/p168uLv2/
$(document).on('click', "[data-link=level1]", function() {
console.log("li clicked");
$(this).find(".knoppenbalk").toggleClass("displaynone");
});
$(document).on('click', "[data-link=solo]", function() {
console.log("solo BUTTON clicked");
});
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div data-link="level1" class="limodeknop">
<div class="overlay"></div>
<div class="modeknop">
<div class="titel">
<lrmodenaam>Level 1</lrmodenaam>
<lrsubmode>Tutorial</lrsubmode>
</div>
<div class="knoppenbalk displaynone">
<div data-link="solo" class="solo btn">Solo</div>
</div>
</div>
</div>
This is made with jQuery, and I'm working with Framework7, but I don't think thats an issue.
You should stop the propagation of the events (calling bubbling). You can search tons of articles about it.
http://jsfiddle.net/p168uLv2/1/
$(document).on('click',"[data-link=solo]", function(e){
console.log("solo BUTTON clicked");
e.stopPropagation();
});
This only apply to the child element, to avoid the parent click propagation.
You can stop an event propagating to parent elements with use of:
event.stopPropagation();
You will want to accept event as an argument to your inline functions.
$(document).on('click',"[data-link=solo]", function(e){
e.stopImmediatePropagation();
console.log("solo BUTTON clicked");
})
;

Hovering over child but exclude parent div

I have a child div inside of a parent div with the same class. Right now, if you hover over the child div inside the parent, a border is added to both the child and the parent. I want to make sure that only the div being hovered will get a border applied to it.
$(document).ready(function () {
$(".content").hover(function () {
$(this).parents().removeClass('divHoverBorder');
$(this).siblings().removeClass('divHoverBorder');
$(this).addClass("divHoverBorder");
$(this).parents().find(".addContentItemDiv").hide();
$(this).find(".addContentItemDiv:first").show();
}, function () {
$(this).removeClass("divHoverBorder");
$('.addContentItemDiv').hide();
});
});
<div class="content" style="width: 500px; height: 300px;">
<div class="content" style="width: 500px; height: 200px;">
</div>
</div>
Here is a JSFiddle of what I'm referring to:
JS fiddle
Your main issue is here:
<div class="content" style="width: 500px; height: 300px;">
<div class="content" style="width: 500px; height: 200px;">
</div>
Since both divs have the same class, you're effectively putting a hover event on both of them and your JQuery can't distinguish between them.
Since you indicated you have to do it this way, here you go:
$(document).ready(function () {
$(".content").mouseover(function (e) {
$(this).parents().removeClass('divHoverBorder');
$(this).siblings().removeClass('divHoverBorder');
$(this).addClass("divHoverBorder");
$(this).parents().find(".addContentItemDiv").hide();
$(this).find(".addContentItemDiv:first").show();
var p = $(this).parent('.content');
if (p.length) { p.removeClass('divHoverBorder'); e.stopPropagation(); }
});
$('.content').mouseout(function (e) {
$(this).removeClass("divHoverBorder");
$('.addContentItemDiv').hide();
});
});
Note that you can't use hover. Hover uses the mouseenter and mouseleave events in JQuery which will prevent firing an event when mouse out from child to parent. We must both remove the parent border (on the case of entering the child) and stop propagation to prevent it from reapplying its own border.
Here's an explanation from the JQuery docs about the difference between mouseenter and mouseover:
The mouseenter event differs from mouseover in the way it handles event bubbling. If mouseover were used in this example, then when the mouse pointer moved over the Inner element, the handler would be triggered. This is usually undesirable behavior. The mouseenter event, on the other hand, only triggers its handler when the mouse enters the element it is bound to, not a descendant. So in this example, the handler is triggered when the mouse enters the Outer element, but not the Inner element.
Your updated fiddle: http://jsfiddle.net/tTUaj/10/
Use something like this:
$(".content").hover(function (ev) {
if ($(ev.currentTarget).parent().hasClass(".content")){
//this is a child
} else {
//this is a parent
}
});
I would recommend something like this: http://jsfiddle.net/3SVGe/1/
$(".cell").hover(function () {
$(".cell").removeClass("over");
$(this).addClass("over");
}, function () {
$(this).removeClass("over");
});
Your problem is that when hovering over a subelement, the hover event is triggered on the parent element. This evades the issue.
Alternatively, you could follow a slightly more complex solution, that would allow for hovering on the parent, then the child, then the parent properly. An example is here:
http://jsfiddle.net/3SVGe/2/

Categories