How to style the parent element when hovering a child element?
Asked Answered
R

8

245

I know that there does not exist a CSS parent selector, but is it possible to style a parenting element when hovering a child element without such a selector?

To give an example: consider a delete button that when hovered will highlight the element that is about to become deleted:

<div>
    <p>Lorem ipsum ...</p>
    <button>Delete</button>
</div>

By means of pure CSS, how to change the background color of this section when the mouse is over the button?

Realist answered 13/11, 2011 at 20:54 Comment(3)
Possible duplicate of Is there a CSS parent selector?Kamerun
Possible solution: https://mcmap.net/q/119040/-on-hover-of-child-change-background-color-of-parent-container-css-only/3597276Scarcely
No prefect solution with pure CSS to do it. See demo stackoverflow.com/a/77160163Interlocutor
N
225

Original Answer (please review updated answer below)
I know it is an old question, but I just managed to do so without a pseudo child (but a pseudo wrapper).

If you set the parent to be with no pointer-events, and then a child div with pointer-events set to auto, it works:)
Note that <img> tag (for example) doesn't do the trick.
Also remember to set pointer-events to auto for other children which have their own event listener, or otherwise they will lose their click functionality.

div.parent {  
    pointer-events: none;
}

div.child {
    pointer-events: auto;
}

div.parent:hover {
    background: yellow;
}    
<div class="parent">
  parent - you can hover over here and it won't trigger
  <div class="child">hover over the child instead!</div>
</div>

Edit:
As Shadow Wizard kindly noted: it's worth to mention this won't work for IE10 and below. (Old versions of FF and Chrome too, see here)

Updated Answer 2024:
I'm not sure why I havn't used this approach a few years ago, maybe it didn't exist? Anyway, I like it much better as it's explicit and not 'a trick':

.parent {} /* nothing here:) */

.child {
    pointer-events: auto;
}

/*this is the new approach*/
.parent:has(.child:nth-of-type(1):hover) {
    /*you may replace this with an explicit className, e.g. :has(.child1)*/
    background: yellow;
}

.parent:has(.child:nth-of-type(2):hover) {
    background: red;
}
<div class="parent">
  parent - you can hover over here and it won't trigger
  <div class="child">--> hover this child instead!</div>
  <div class="child">--> or hover this child instead!</div>
</div>
Nunci answered 7/5, 2015 at 14:54 Comment(8)
Worth to mention this won't work for IE10 and below. (Old versions of FF and Chrome too, see here)Fredericksburg
Setting pointer events to none also means that event listeners on that element will not work..!Eudemon
@Eudemon you're absolutely right. Although from my experience this usage is usually connected to a single click area/element, so you can assign the click listener to the parentNunci
How about if I have two-level, parent, child1, and child of child1. I added pointer-events none to parent and pointer-events auto to child. I want to hover to child1 except the child of child 1. Demo here: codepen.io/lion5893/pen/WNQgyVxCousteau
this is a thumb up bro, i manage to css the parent when hover the child only, wpPurr
If I have more than one child is there a way to keep the pointer events but not trigger the hover for parent?Ecstatic
if you have two childs, how would you manage to still keep pointerEvents on both child but with only child1 triggering parent hover effect?Granlund
@Granlund your question made me review my answer again - and I edited it with a fresh new approach that also covers your case. Thx!Nunci
R
157

Well, this question is asked many times before, and the short typical answer is: It cannot be done by pure CSS. It's in the name: Cascading Style Sheets only supports styling in cascading direction, not up.

But in most circumstances where this effect is wished, like in the given example, there still is the possibility to use these cascading characteristics to reach the desired effect. Consider this pseudo markup:

<parent>
    <sibling></sibling>
    <child></child>
</parent>

The trick is to give the sibling the same size and position as the parent and to style the sibling instead of the parent. This will look like the parent is styled!

Now, how to style the sibling?

When the child is hovered, the parent is too, but the sibling is not. The same goes for the sibling. This concludes in three possible CSS selector paths for styling the sibling:

parent sibling { }
parent sibling:hover { }
parent:hover sibling { }

These different paths allow for some nice possibilities. For instance, unleashing this trick on the example in the question results in this fiddle:

div {position: relative}
div:hover {background: salmon}
div p:hover {background: white}
div p {padding-bottom: 26px}
div button {position: absolute; bottom: 0}

Style parent image example

Obviously, in most cases this trick depends on the use of absolute positioning to give the sibling the same size as the parent, ánd still let the child appear within the parent.

Sometimes it is necessary to use a more qualified selector path in order to select a specific element, as shown in this fiddle which implements the trick multiple times in a tree menu. Quite nice really.

Realist answered 13/11, 2011 at 20:55 Comment(2)
This is good for highlighting, but won't work if you try change font color, am I right?Marionmarionette
@JahanzebKhan Changing font color is no problem.Realist
O
48

In 2022:

This can be now achieved with CSS only, using the :has pseudo-class and the following expression:

div:has(button:hover) {}

Here's a snippet showcasing the original proposition:

div:has(button:hover) {
  background-color: cyan;      
}
<div>
  <p>Lorem ipsum ...</p>
  <button>Delete</button>
</div>

Browser support can generally be seen as evergreen. See support here.

Otti answered 21/9, 2022 at 9:8 Comment(8)
I would really LOVE to use it in production! I really hope Firefox will accept this in the future. When it comes to just cosmetic hover effects, only Firefox users will miss this impression, so if you have some cool stuff to show off, it'll always better to use it anyway :-) But when it's more critical in UI way, you should avoid it (for now unfortunately)Wozniak
Per Bugzilla, they plan to work on it “in the first half of this year” [2023], so maybe Gecko will finally join in for complete support.Hovel
wow You are amazing . ChatGPT even didn't know that in 2023. In CSS and SCSS, there is no direct way to select and change the style of a parent element based on the hover state of its child element. The CSS cascade works in a top-down manner, and there is currently no parent selector or a way to traverse back up the DOM tree in CSS.Mena
I hope that Firefox's implementation (which currently doesn't support it even if :has() selector is enabled) soon supports that, as it makes it much easier for me to darken my video player while the user is hovering over an end screen item.Labyrinthodont
@Labyrinthodont don't expect that will happen soon. In Firefox 118, even the experimental flag is enabled, :has is just partially supported. see demo stackoverflow.com/a/77160163 ;Interlocutor
I believe Firefox is just doing like this.. body div:has(p button) . First, find body div. say there are 200 matches. then check each of them, whether p button is under them. filtered out 150 matches. so remaining 50 matches is the result. base on this logic, :has with :hover never happens. This is one time and no triggering from inside like :hover. (The true way is to bottom-up. when the element inside :has(....) appears, check the parent matching the selector or not. )Interlocutor
@ChesterFung It's currently in development and on Mozilla Connect a developer said they aim to fully deliver the feature before the end of 2023. So if they manage to hit that goal it will be soon.Labyrinthodont
Firefox fully supports the selector now.Bowes
S
20

Another, simpler "alternate" approach (to an old question)..
would be to place elements as siblings and use:

Adjacent Sibling Selector (+) or General Sibling Selector (~)

<div id="parent">
  <!-- control should come before the target... think "cascading" ! -->
  <button id="control">Hover Me!</button>
  <div id="target">I'm hovered too!</div>
</div>
#parent {
  position: relative;
  height: 100px;
}

/* Move button control to bottom. */
#control {
  position: absolute;
  bottom: 0;
}

#control:hover ~ #target {
  background: red;
}

enter image description here

Demo Fiddle here.

Stanton answered 30/12, 2017 at 20:12 Comment(1)
worth noting that this only works it #targets comes after #control in the DOM (i.e. you can't affect previous siblings, only following siblings)Trestlework
Z
13

there is no CSS selector for selecting a parent of a selected child.

you could do it with JavaScript

Zakarias answered 13/11, 2011 at 20:55 Comment(2)
true. something like $(child).hover(function(){$(this).closest(parentSelector).addClass('hoverClass')}, function(){$(this).closest(parentSelector).removeClass('hoverClass')});Brewhouse
@AamirAfridi That's not pure JS though, you have to use jQuery with that.Jaymie
H
11

As mentioned previously "there is no CSS selector for selecting a parent of a selected child".

So you either:


Here is the example for the javascript/jQuery solution

On the javascript side:

$('#my-id-selector-00').on('mouseover', function(){
  $(this).parent().addClass('is-hover');
}).on('mouseout', function(){
  $(this).parent().removeClass('is-hover');
})

And on the CSS side, you'd have something like this:

.is-hover {
  background-color: red;
}
Horsley answered 3/12, 2015 at 11:21 Comment(0)
P
4

This solution depends fully on the design, but if you have a parent div that you want to change the background on when hovering a child you can try to mimic the parent with a ::after / ::before.

<div class="item">
    design <span class="icon-cross">x</span>
</div>

CSS:

.item {
    background: blue;
    border-radius: 10px;
    position: relative;
    z-index: 1;
}
.item span.icon-cross:hover::after {
    background: DodgerBlue;
    border-radius: 10px;
    display: block;
    position: absolute;
    z-index: -1;
    top: 0;
    left: 0;
    right: 0;
    bottom: 0;
    content: "";
}

See a full fiddle example here

Palpebrate answered 23/4, 2018 at 9:2 Comment(0)
C
-12

This is extremely easy to do in Sass! Don't delve into JavaScript for this. The & selector in sass does exactly this.

http://thesassway.com/intermediate/referencing-parent-selectors-using-ampersand

Cambrel answered 16/3, 2016 at 0:16 Comment(3)
-1 Sass does not add more functionality to css, it just makes writing it easier. Anything you can do in sass can be done in css and visa versa.Nelda
@Nelda Given variables and mixins, I would say Sass adds an awful lot to CSS. You can do it all in CSS, in the same way you could still write sophisticated software using plain old C. Modern languages add a lot without enabling something that couldn't be done before. Same with Sass and CSS.Patriarchy
@Cobus Kruger Your partially right, but you can't make that comparison. Whereas languages like c++ c# and javascript may be based off C, they do not turn into C before runtime. Sass is converted in css before runtime therefore your never truly loading a sass stylesheet, just a css one.Nelda

© 2022 - 2024 — McMap. All rights reserved.