9

How could hover be prevented for parent element if we hover over it's children?

Here is the code

const parent = document.getElementById('parent')
parent.onmouseover = function testAlert(e) {
  /* alert('parent') */
}
const childRight = document.getElementById('child-right')
childRight.onmouseover = function f(e) {
  e.stopPropagation()
  /* alert('child-right') */
}
const childLeft = document.getElementById('child-left')
childLeft.onmouseenter = function f(e) {
  e.stopPropagation()
  /* alert('child-right') */
}
#parent {
  background: green;
  width: 100px;
  height: 100px;
  position: relative;
  margin: 0 auto;
}

#parent:hover {
  background: rgba(0, 0, 0, 0.8);
}

#child-left {
  background: red;
  width: 50px;
  height: 50px;
  position: absolute;
  top: 0;
  left: -50px;
}

#child-right {
  background: red;
  width: 50px;
  height: 50px;
  position: absolute;
  top: 50px;
  left: 100px;
}
<div id="parent">
  <div id="child-left"></div>
  <div id="child-right"></div>
</div>

https://jsfiddle.net/3tjcsyov/48/

You can see that if you hover over red rects the green one is also hovered if CSS behavior considered. Yes, we can use stopPropagation but it only prevents js handlers execution for parent element while CSS behavior remains unchanged.

Temani Afif
  • 245,468
  • 26
  • 309
  • 415
Igor Chernega
  • 611
  • 3
  • 7
  • 18
  • 1
    Can you add the behaviour using JavaScript instead of SCSS? – Jack Bashford Feb 09 '19 at 07:47
  • 2
    To hover over a child, you have to be hovering over the parent. I'm not aware that that state can be prevented. – Taplar Feb 09 '19 at 07:47
  • 1
    Please **do not use external code sites** unless SO's snippet functionality is not sufficient. – connexo Feb 09 '19 at 07:51
  • Thx @JackBashford That is a solution. I don't really like it though, but still it works at the end of the day – Igor Chernega Feb 09 '19 at 07:59
  • 1
    Please post your code in the question itself. – AndrewL64 Feb 09 '19 at 08:02
  • can you describe your final need, probably we will have to find a better way – Temani Afif Feb 09 '19 at 08:14
  • Well the final need is to provide hover behavior only when parent element is hovered which also includes some styling of it's children. If children are hovered then no effect is expected. But to say the truth I am interested in in depth investigation to figure out what could be done with the stuff in different cases. So I do welcome any discussion around the topic – Igor Chernega Feb 09 '19 at 08:24
  • 1
    @IgorChernega _"Well the final need is to provide hover behavior only when parent element is hovered which also includes some styling of it's children. If children are hovered then no effect is expected"_ The code at this answer https://stackoverflow.com/a/54604384/ meets the requirement, correct? – guest271314 Feb 09 '19 at 08:32
  • this is till too broad :) by final need, I meant a real use case. You think the solution is to disable the hover on child element but probably we can tackle this differently – Temani Afif Feb 09 '19 at 08:32
  • 2
    @TemaniAfif The question is not too broad. This answer https://stackoverflow.com/a/54604384/ meets the requirement described by OP – guest271314 Feb 09 '19 at 08:34
  • @guest271314 I didn't mean the question is too broad. the question is fine .. I was talking about his need. He have a requirement in mind and he's thinking that preventing the hover is the way to go (which is fine and the question is ok) But I am wondering what he want to achieve at the end because probably we don't even need to deal with this hover problem (probably a classic XY problem that we are overcomplicating) – Temani Afif Feb 09 '19 at 08:36
  • @TemaniAfif The stated requirement is clear based on the question and the content of this comment posted by OP https://stackoverflow.com/questions/54604252/prevent-hover-bubbling?noredirect=1#comment96004600_54604252 – guest271314 Feb 09 '19 at 08:38
  • @IgorChernega SO is not a place for general discussions, instead provide one or several question for your use case. – Asons Feb 09 '19 at 08:38
  • @IgorChernega For discussions, this chat room might be a place: https://chat.stackoverflow.com/rooms/29074/html-css-webdesign – Asons Feb 09 '19 at 08:42
  • @LGSon Why would OP have to provide anything other than the requirement that they already have at the actual question? Whether OP attempts to change the stated requirement or not from this point forward is a different matter. – guest271314 Feb 09 '19 at 08:42
  • 1
    @guest271314 OP states in a comment, "_I am interested in in depth investigation to figure out what could be done with the stuff in different cases_", which is why I added mine. – Asons Feb 09 '19 at 08:43
  • @guest271314 well nevermind ;) .. I was trying to help differently but this will turn to a never ending discussion which is not the purpose of SO. let's stop it here. – Temani Afif Feb 09 '19 at 08:44
  • 1
    @LGSon Yes, that is a different matter than the actual expected output described at the OP and comment. For that thread, yes, chat would be a more appropriate venue. And yes, OP would need to present use and test cases, which they have thus far failed to do. For the current actual question only code is necessary. The requirement has been achieved thus far by at least one [answer](https://stackoverflow.com/a/54604384/) using CSS alone, which should resolve the actual question. – guest271314 Feb 09 '19 at 08:46
  • I've added a bunch of dupe links that shows different scenarios... – Asons Feb 09 '19 at 09:08

2 Answers2

9

If you set the children's pointer-events to none, you can achieve this without any JavaScript.

#parent {
  background: green;
  width: 100px;
  height: 100px;
  position: relative;
  margin: 0 auto;
}

#parent:hover {
  background: rgba(0, 0, 0, 0.8);
}

#child-left {
  background: red;
  width: 50px;
  height: 50px;
  position: absolute;
  top: 0;
  left: -50px;
}

#child-right {
  background: red;
  width: 50px;
  height: 50px;
  position: absolute;
  top: 50px;
  left: 100px;
}

#child-left,
#child-right {
  pointer-events: none;
}
<div id="parent">
  <div id="child-left"></div>
  <div id="child-right"></div>
</div>

https://jsfiddle.net/bepLktoj/

Andy Hoffman
  • 18,436
  • 4
  • 42
  • 61
1

The use of pointer-events:none with #child-left and #child-right selectors should be sufficient in the simple case where you only want hover styling to apply if the #parent is hovered (and no hover styling applied when either child is hovered). Achieving that would be as simple as adding this to your style-sheet:

#child-left,
#child-right {
  pointer-events: none;
}

For the more advanced case that you're interested in (where distinct hover styles are required for each of the elements in this "parent child scenario") my understanding is that scripting will be required here, given that styling is directed from parent to child, rather than child to parent.

One solution to achieve this would be to introduce a .hover modifier class to mimic the built in :hover selector. This .hover class would contain the unique styling you want applied to a specific element. Your script would in turn add or remove the hover class based on mouse interaction.

A simple script to achieve what you require would be to add/remove the hover class to the event#target element that is supplied with the Event object (passed to the mouseover and mouseout event):

const parent = document.getElementById('parent');

/*
Add mouse over and mouse out event listeners to 
add/remove hover class from the event's target element
*/

parent.addEventListener('mouseover', (event) => {
  /* 
  event.target is the actual element that triggers this 
  mouse event (ie the #parent or either of the children)
  */
  event.target.classList.add('hover'); 
})

parent.addEventListener('mouseout', (event) => {
  /* 
  event.target is the actual element that triggers this 
  mouse event (ie the #parent or either of the children)
  */     
  event.target.classList.remove('hover'); 
})
#parent {
  background: green;
  width: 100px;
  height: 100px;
  position: relative;
  margin: 0 auto;
}

#child-right {
  background: red;
  width: 50px;
  height: 50px;
  position: absolute;
  top: 50px;
  left: 100px;
}

#child-left {
  background: red;
  width: 50px;
  height: 50px;
  position: absolute;
  top: 0;
  left: -50px;  
}

/*
Introduce hover modified class, which is toggled
via javascript and substitutes the native CSS 
:hover selector. I've explicity defined a selector 
for each element however via SCSS this can be 
simplified */
#parent.hover,
#child-left.hover,
#child-right.hover {
  background: rgba(0,0,0,0.8);
}
<div id="parent">
  <div id="child-left"></div>
  <div id="child-right"></div>
</div>
Dacre Denny
  • 29,664
  • 5
  • 45
  • 65