551

What I want to do is when a certain div is hovered, it'd affect the properties of another div.

For example, in this JSFiddle demo, when you hover over #cube it changes the background-color but what I want is that when I hover over #container, #cubeis affected.

div {
  outline: 1px solid red;
}

#container {
  width: 200px;
  height: 30px;
}

#cube {
  width: 30px;
  height: 100%;
  background-color: red;
}

#cube:hover {
  width: 30px;
  height: 100%;
  background-color: blue;
}
<div id="container">
  <div id="cube">
  </div>
</div>
Penny Liu
  • 7,720
  • 5
  • 40
  • 66
Trufa
  • 35,711
  • 41
  • 118
  • 180

6 Answers6

1202

If the cube is directly inside the container:

#container:hover > #cube { background-color: yellow; }

If cube is next to (after containers closing tag) the container:

#container:hover + #cube { background-color: yellow; }

If the cube is somewhere inside the container:

#container:hover #cube { background-color: yellow; }

If the cube is a sibling of the container:

#container:hover ~ #cube { background-color: yellow; }
Mike
  • 13,530
  • 2
  • 15
  • 25
  • 123
    Don't forget the general sibling combinator `~` for 'cube is somewhere after container in the DOM and shares a parent' – robertc Dec 21 '10 at 18:50
  • 4
    That's pretty cool. Is there some source where I can find more information about that ? Is it supported by all browser, is it CSS3 ? Would be great to have some more info about that. Thanks so much! – Anonymous Oct 19 '11 at 10:35
  • 3
    +1 Great answer @Mike. What if `#container` is next to `#cube`, i.e. `#container` follows `#cube`? – PeterKA Oct 02 '14 at 04:58
  • 4
    What to do if the hovered element is inside the container(that we want to be effected) ??? For example: #cube:hover #container{Some CSS Effects} – Hanzallah Afgan Mar 03 '15 at 15:19
  • @Hanzllah thats not possible with CSS currently, you would need to use JavaScript to alter the elements parent (#container) – Mike Mar 03 '15 at 17:27
  • How to apply hover effects to an element that adjoins another element eg is it possible in a table to use hover on one `td` to change the background-color of another `td` on the same row? – I Like Mar 22 '16 at 03:23
  • How t handle on mouse leave? this is hodling the css style when u leave the mouse from the element,anyone has any idea? – mzonerz Jul 13 '16 at 08:27
  • 2
    Good answer !! What about if I want to change the parent when I hover the child. I think there is no selector for that. – Mikel Mar 08 '17 at 11:49
  • I like this! What if we want to affect two siblings at the same time, though? E.g. not just their parent div but different things themselves? – Elliptica Jan 31 '19 at 00:46
  • 2
    @Elliptica You would just need to use a comma; `#container:hover ~ #cube, #container:hover ~ #rectangle { background-color: yellow; }` – Jake Apr 16 '19 at 23:11
  • what if parent of container ? – Freddy Daniel Nov 11 '19 at 06:08
  • hello, this answer didn't work for me. i have two elements in a flex container and i want one to be over the other one (zindex) but one after the other ( then , so i used the solution with '+' and the one with '~' and none worked – manouna Oct 05 '20 at 21:15
  • 1
    Note that the general sibling selector `~` only applies if the element on the left comes before the element on the right. `.foo:hover ~ bar` will not match if `bar` comes before `foo` in the DOM. – Rod Nov 05 '20 at 22:36
  • any idea how to do this using Tailwind CSS? – Justin Dec 28 '20 at 17:02
  • I tired all nothing worked @taruf – user123456 Jan 12 '21 at 09:20
  • here is my html https://stackoverflow.com/questions/65678531/why-my-css-hover-selector-is-not-working – user123456 Jan 12 '21 at 09:20
  • if If the cube is a somewhere of the container: – user123456 Jan 12 '21 at 12:36
  • @justin if If the cube is a somewhere outside the container – user123456 Jan 12 '21 at 21:47
47

In this particular example, you can use:

#container:hover #cube {
    background-color: yellow;   
}

This example only works since cube is a child of container. For more complicated scenarios, you'd need to use different CSS, or use JavaScript.

TylerH
  • 19,065
  • 49
  • 65
  • 86
Emmett
  • 13,097
  • 12
  • 52
  • 78
43

Using the sibling selector is the general solution for styling other elements when hovering over a given one, but it works only if the other elements follow the given one in the DOM. What can we do when the other elements should actually be before the hovered one? Say we want to implement a signal bar rating widget like the one below:

Signal bar rating widget

This can actually be done easily using the CSS flexbox model, by setting flex-direction to reverse, so that the elements are displayed in the opposite order from the one they're in the DOM. The screenshot above is from such a widget, implemented with pure CSS.

Flexbox is very well supported by 95% of modern browsers.

.rating {
  display: flex;
  flex-direction: row-reverse;
  width: 9rem;
}
.rating div {
  flex: 1;
  align-self: flex-end;
  background-color: black;
  border: 0.1rem solid white;
}
.rating div:hover {
  background-color: lightblue;
}
.rating div[data-rating="1"] {
  height: 5rem;
}
.rating div[data-rating="2"] {
  height: 4rem;
}
.rating div[data-rating="3"] {
  height: 3rem;
}
.rating div[data-rating="4"] {
  height: 2rem;
}
.rating div[data-rating="5"] {
  height: 1rem;
}
.rating div:hover ~ div {
  background-color: lightblue;
}
<div class="rating">
  <div data-rating="1"></div>
  <div data-rating="2"></div>
  <div data-rating="3"></div>
  <div data-rating="4"></div>
  <div data-rating="5"></div>
</div>
Dan Dascalescu
  • 110,650
  • 40
  • 276
  • 363
  • Could this be edited so the highlight doesn't disappear when moving the mouse from 1 bar to the other? The flashing is a little distracting. – Cerbrus Sep 09 '15 at 06:23
  • @Cerbrus: Added a solution that doesn't hide the hover when the mouse is between bars. The downside is the width of the bars is no longer equal. – Dan Dascalescu Sep 09 '15 at 09:22
  • 1
    Try this in your first snippet: on `.rating div`, remove the margin, and add `border-right: 4px solid white;` – Cerbrus Sep 09 '15 at 09:47
  • 1
    Flex direction (not well supported for IE) OR 1) black by default 2) all blue on mouse over the container 3) black for next sibling on bar hover :) – Stephane Mathis Oct 16 '15 at 10:30
  • 1
    I made this fiddle which (at least for me) made it a little more aparent on what was going on here. https://jsfiddle.net/maxshuty/cj55y33p/3/ – maxshuty Jun 15 '17 at 12:27
  • That's a great idea! For instances where you only need one element to trigger a hover in the dom before it I think it would be better to use the flexbox `order` property so you don't have to maintain the entire list in reverse order if only one element needs to trigger the hover. – TimE May 20 '21 at 06:24
10

Only this worked for me:

#container:hover .cube { background-color: yellow; }

Where .cube is CssClass of the #cube.

Tested in Firefox, Chrome and Edge.

Igor Ivancha
  • 3,231
  • 4
  • 28
  • 39
CyberHawk
  • 848
  • 2
  • 11
  • 27
8

Big thanks to Mike and Robertc for their helpful posts!

If you have two elements in your HTML and you want to :hover over one and target a style change in the other the two elements must be directly related--parents, children or siblings. This means that the two elements either must be one inside the other or must both be contained within the same larger element.

I wanted to display definitions in a box on the right side of the browser as my users read through my site and :hover over highlighted terms; therefore, I did not want the 'definition' element to be displayed inside the 'text' element.

I almost gave up and just added javascript to my page, but this is the future dang it! We should not have to put up with back sass from CSS and HTML telling us where we have to place our elements to achieve the effects we want! In the end we compromised.

While the actual HTML elements in the file must be either nested or contained in a single element to be valid :hover targets to each other, the css position attribute can be used to display any element where ever you want. I used position:fixed to place the target of my :hover action where I wanted it on the user's screen regardless to its location in the HTML document.

The html:

<div id="explainBox" class="explainBox"> /*Common parent*/

  <a class="defP" id="light" href="http://en.wikipedia.or/wiki/Light">Light                            /*highlighted term in text*/
  </a> is as ubiquitous as it is mysterious. /*plain text*/

  <div id="definitions"> /*Container for :hover-displayed definitions*/
    <p class="def" id="light"> /*example definition entry*/ Light:
      <br/>Short Answer: The type of energy you see
    </p>
  </div>

</div>

The css:

/*read: "when user hovers over #light somewhere inside #explainBox
    set display to inline-block for #light directly inside of #definitions.*/

#explainBox #light:hover~#definitions>#light {
  display: inline-block;
}

.def {
  display: none;
}

#definitions {
  background-color: black;
  position: fixed;
  /*position attribute*/
  top: 5em;
  /*position attribute*/
  right: 2em;
  /*position attribute*/
  width: 20em;
  height: 30em;
  border: 1px solid orange;
  border-radius: 12px;
  padding: 10px;
}

In this example the target of a :hover command from an element within #explainBox must either be #explainBox or also within #explainBox. The position attributes assigned to #definitions force it to appear in the desired location (outside #explainBox) even though it is technically located in an unwanted position within the HTML document.

I understand it is considered bad form to use the same #id for more than one HTML element; however, in this case the instances of #light can be described independently due to their respective positions in uniquely #id'd elements. Is there any reason not to repeat the id #light in this case?

Igor Ivancha
  • 3,231
  • 4
  • 28
  • 39
rick
  • 81
  • 1
  • 2
7

Here is another idea that allow you to affect other elements without considering any specific selector and by only using the :hover state of the main element.

For this, I will rely on the use of custom properties (CSS variables). As we can read in the specification:

Custom properties are ordinary properties, so they can be declared on any element, are resolved with the normal inheritance and cascade rules ...

The idea is to define custom properties within the main element and use them to style child elements and since these properties are inherited we simply need to change them within the main element on hover.

Here is an example:

#container {
  width: 200px;
  height: 30px;
  border: 1px solid var(--c);
  --c:red;
}
#container:hover {
  --c:blue;
}
#container > div {
  width: 30px;
  height: 100%;
  background-color: var(--c);
}
<div id="container">
  <div>
  </div>
</div>

Why this can be better than using specific selector combined with hover?

I can provide at least 2 reasons that make this method a good one to consider:

  1. If we have many nested elements that share the same styles, this will avoid us complex selector to target all of them on hover. Using Custom properties, we simply change the value when hovering on the parent element.
  2. A custom property can be used to replace a value of any property and also a partial value of it. For example we can define a custom property for a color and we use it within a border, linear-gradient, background-color, box-shadow etc. This will avoid us reseting all these properties on hover.

Here is a more complex example:

.container {
  --c:red;
  width:400px;
  display:flex;
  border:1px solid var(--c);
  justify-content:space-between;
  padding:5px;
  background:linear-gradient(var(--c),var(--c)) 0 50%/100% 3px no-repeat;
}
.box {
  width:30%;
  background:var(--c);
  box-shadow:0px 0px 5px var(--c);
  position:relative;
}
.box:before {
  content:"A";
  display:block;
  width:15px;
  margin:0 auto;
  height:100%;
  color:var(--c);
  background:#fff;
}

/*Hover*/
.container:hover {
  --c:blue;
}
<div class="container">
<div class="box"></div>
<div class="box"></div>
</div>

As we can see above, we only need one CSS declaration in order to change many properties of different elements.

Temani Afif
  • 180,975
  • 14
  • 166
  • 216