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
, #cube
is 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;
}
_x000D_
<div id="container">
<div id="cube">
</div>
</div>
_x000D_
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:
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 {_x000D_
display: flex;_x000D_
flex-direction: row-reverse;_x000D_
width: 9rem;_x000D_
}_x000D_
.rating div {_x000D_
flex: 1;_x000D_
align-self: flex-end;_x000D_
background-color: black;_x000D_
border: 0.1rem solid white;_x000D_
}_x000D_
.rating div:hover {_x000D_
background-color: lightblue;_x000D_
}_x000D_
.rating div[data-rating="1"] {_x000D_
height: 5rem;_x000D_
}_x000D_
.rating div[data-rating="2"] {_x000D_
height: 4rem;_x000D_
}_x000D_
.rating div[data-rating="3"] {_x000D_
height: 3rem;_x000D_
}_x000D_
.rating div[data-rating="4"] {_x000D_
height: 2rem;_x000D_
}_x000D_
.rating div[data-rating="5"] {_x000D_
height: 1rem;_x000D_
}_x000D_
.rating div:hover ~ div {_x000D_
background-color: lightblue;_x000D_
}
_x000D_
<div class="rating">_x000D_
<div data-rating="1"></div>_x000D_
<div data-rating="2"></div>_x000D_
<div data-rating="3"></div>_x000D_
<div data-rating="4"></div>_x000D_
<div data-rating="5"></div>_x000D_
</div>
_x000D_
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?
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.
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 {_x000D_
width: 200px;_x000D_
height: 30px;_x000D_
border: 1px solid var(--c);_x000D_
--c:red;_x000D_
}_x000D_
#container:hover {_x000D_
--c:blue;_x000D_
}_x000D_
#container > div {_x000D_
width: 30px;_x000D_
height: 100%;_x000D_
background-color: var(--c);_x000D_
}
_x000D_
<div id="container">_x000D_
<div>_x000D_
</div>_x000D_
</div>
_x000D_
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:
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 {_x000D_
--c:red;_x000D_
width:400px;_x000D_
display:flex;_x000D_
border:1px solid var(--c);_x000D_
justify-content:space-between;_x000D_
padding:5px;_x000D_
background:linear-gradient(var(--c),var(--c)) 0 50%/100% 3px no-repeat;_x000D_
}_x000D_
.box {_x000D_
width:30%;_x000D_
background:var(--c);_x000D_
box-shadow:0px 0px 5px var(--c);_x000D_
position:relative;_x000D_
}_x000D_
.box:before {_x000D_
content:"A";_x000D_
display:block;_x000D_
width:15px;_x000D_
margin:0 auto;_x000D_
height:100%;_x000D_
color:var(--c);_x000D_
background:#fff;_x000D_
}_x000D_
_x000D_
/*Hover*/_x000D_
.container:hover {_x000D_
--c:blue;_x000D_
}
_x000D_
<div class="container">_x000D_
<div class="box"></div>_x000D_
<div class="box"></div>_x000D_
</div>
_x000D_
As we can see above, we only need one CSS declaration in order to change many properties of different elements.
Only this worked for me:
#container:hover .cube { background-color: yellow; }
Where .cube
is CssClass of the #cube
.
Tested in Firefox, Chrome and Edge.
Source: Stackoverflow.com