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/
Related
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
I have an element which I need to hide when clicking anywhere on the page, even on elements which have e.stopPropagation().
I've tried
$(document).on("click",function(){
$("#mydiv").hide();
});
Which works but doesn't work on an element which has the following:
$(document).on("click","#someDiv",function(e){
e.stopPropagation();
});
How can I make my element hide even on #someDiv without removing the stopPropagation. The reason I need this to be flexible is that I'm writing a plugin which anyone can use and will not know anything about the click events or element ids on the page.
Is there a way I can bind my hiding of an element to all clicks? Happy with a JS or jQuery solution.
I know it must be possible as I've seen other plugins do this.
Thanks in advance for any suggestions!
Probably the quickest and most compatible solution with your existing code is to swap out the #mydiv hiding listener to use the vanilla JS addEventListener leveraging the useCapture optional third argument to grab the event before any inner elements have had a chance for their own listeners to fire. See it in action in the snippet below:
document.addEventListener("click",function(){
$("#mydiv").hide();
}, true);
$("#someDiv").on("click",function(e){
console.log('some div clicked!');
e.stopPropagation();
});
.container {
height: 100vh;
width: 100vw;
background-color: pink;
}
#mydiv {
background-color: cyan;
}
#someDiv {
background-color: lavender;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<div class="container">
<div id="mydiv">Hide me!</div>
<div id="someDiv">I'll stop propagation!</div>
</div>
you could do something like adding an additional EventListener to all elements on your page by using
$('*').each( () => {
$(this).on("click", () => {
/* hide your div here */
})
};
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
I need to disable contextmenu only inside a specific div #wrapperand its children, and not the whole page. This is what I'm doing:
document.addEventListener('contextmenu', function (event) {
console.log(event.target.id);
if (event.target.id === 'wrapper') {
event.preventDefault();
}
});
.. but it doesn't seem to work.
You're approaching this the wrong way: you're adding the listener to the document, which may be ok, but it's easier to add it to the element itself, and you are checking event.target.id, which is the ID of the current clicked element (e.g. a children of your wrapper), not the wrapper.
To make this work you can easily do something like this instead:
var myWrapper = document.getElementById('wrapper');
myWrapper.addEventListener('contextmenu', function(e) {
e.preventDefault();
}, true);
This is a solution that work fine
<div id="wrapper" oncontextmenu="return false">
#wrapper
<div class="childds">
</div>
The code you have in your question works perfectly. One possibility of why the context menu still showed up is that you in fact clicked on a child of #wrapper, instead of clicking on the element itself:
HTML
<div id="wrapper">
#wrapper
<div class="inner">
#wrapper .inner
</div>
</div>
Working example demonstrating this issue on JSFiddle.
You can overcome this by attaching the event handler to the desired element directly instead. This way, right-click events on child-elements will bubble up to #wrapper, and thus fire the event as expected:
JavaScript
document.getElementById('wrapper').addEventListener('contextmenu', function (event) {
event.preventDefault();
});
Working example on JSfiddle.
I am trying to prevent click event from firing on parent <div> if user starts click on child <div>, then moves mouse out of child <div> but not leaving parent <div> while still holding it down, and then releases on parent.
Both parent and child have jQuery click events assigned with event.stopPropagation().
What is expected is that there is no click event fired at all if I release mouse outside the <div> I initially pressed mouse down.
At least this is how it works on Firefox and Safari works but not on Chrome.
Buttons work as they should, divs don't. Maybe there's some magic I could copy from buttons and apply to the div?
http://jsfiddle.net/zd7L68b2
<div class="outer">
<button class="inner">
</button>
<div class="inner">
</div>
</div>
$('.outer').click(function(e){
console.log('Clicked outer');
});
$('.outer').moused(function(e){
console.log('Clicked outer');
});
$('.inner').click(function(e){
e.stopPropagation();
console.log('Clicked inner');
});
Strictly speaking, all mouse events are detected by the browser whether we choose to access them or not.
To solve your problem, instead of monitoring click events, I would monitor mousedown and mouseup events separately. In my simple code snippet, a boolean variable named innerClick is toggled true if the mousedown event occurred inside the required target, but no other code is fired. In a separate mouseup listener, a conditional checks whether the mouse is released inside the required target AND whether the innerClick boolean is set true (indicating the last mousedown was made there too). The code you need to execute, or a call to a function block containing it, is placed inside that conditional. In my snippet, a call is made to console.log() to demonstrate specificity.
The mouseup listener has to also then toggle innerClick to false;
let innerClick = false;
document.addEventListener('mousedown', event => {
if (event.target.id == "red") {
// mouse down on red box;
innerClick = true;
} // end if;
}); // end mousedown event
document.addEventListener('mouseup', event => {
if (innerClick && event.target.id == "red") {
//mouse up inside red;
console.log("exclusively inside red");
// call or place code for red click here;
} else {
console.log("nope!");
} // end if-else;
//reset innerClick boolean;
innerClick = false;
}); // end mouseup event
.container {
width: 300px;
height: 100px;
background: yellow;
display: flex;
align-items:center;
justify-content: center;
}
.inner {
width: 30%;
height: 30%;
background: red;
border: 1px solid black;
}
<div id="yellow" class="container">
<div id="red" class="inner"></div>
</div>
Note the mousedown and mouseup listeners are set on document rather than the element. This has no computational overhead but allows limitless combinations of elements to be monitored in two simple code blocks (each with its own conditional block where the relevant code can be placed, or called from).
Tested and working in Chrome and Firefox. Another advantage of using core JS is wider, bug-free, support.
You could try binding mousedown() if that would keep your functionality the way you want it:
http://api.jquery.com/mousedown/
I tried it on your jsfiddle and it worked. However, you lose the full click, you get just the mousedown ("half" a click so to speak).
Like this: http://jsfiddle.net/zd7L68b2/4/
$('.inner').mousedown(function(e){
e.stopPropagation();
console.log('Clicked inner');
});