77

In my HTML below, when I hover on the <a> element I want to change the colour of the <h1> element using only CSS. Is there a way to achieve this?

<h1>Heading</h1>
<a class="button" href="#"></a>

What if I wrap a div around it with an id in it?

<div id="banner">
    <h1>Heading</h1>
    <a class="button" href="#"></a>
</div>

Will this help?

isherwood
  • 58,414
  • 16
  • 114
  • 157
nasty
  • 6,797
  • 9
  • 37
  • 52
  • The div won't help, unless you are okay with changing the header color while hovering the div itself (not the anchor specifically). – bfavaretto Sep 25 '12 at 00:43

10 Answers10

136

You can make a sibling that follows an element change when that element is hovered, for example you can change the color of your a link when the h1 is hovered, but you can't affect a previous sibling in the same way.

h1 {
    color: #4fa04f;
}
h1 + a {
    color: #a04f4f;
}
h1:hover + a {
    color: #4f4fd0;
}
a:hover + h1 {
    background-color: #444;
}
<h1>Heading</h1>
<a class="button" href="#">The &quot;Button&quot;</a>
<h1>Another Heading</h1>

We set the color of an H1 to a greenish hue, and the color of an A that is a sibling of an H1 to reddish (first 2 rules). The third rule does what I describe -- changes the A color when the H1 is hovered.

But notice the fourth rule a:hover + h1 only changes the background color of the H1 that follows the anchor, but not the one that precedes it.

This is based on the DOM order, and it's possible to change the display order of elements, so even though you can't change the previous element, you could make that element appear to be after the other element to get the desired effect.
Note that doing this could affect accessibility, since screen readers will generally traverse items in DOM order, which may not be the same as the visual order.


Edit

This should now be possible using the has selector, in the browsers that support it.
See the comments in the CSS below.
I will edit again in the future; currently my Chrome and Safari browsers are not yet at versions that support it.

h1 {
    color: #4fa04f;
}
h1 + a {
    color: #a04f4f;
}
h1:hover + a {
    color: #4f4fd0;
}
a:hover + h1 {
    background-color: #444;
}
/* Select an H1 heading that has an <a>nchor as a sibling */
h1:has(+ a) {
    background-color: cyan;
}

/* Select an H1 heading that has a currently-hovered <a>nchor as a sibling */
h1:has(+ a:hover) {
    background-color: yellow;
}
<h1>Heading</h1>
<a class="button" href="#">The &quot;Button&quot;</a>
<h1>Another Heading</h1>
Stephen P
  • 14,422
  • 2
  • 43
  • 67
25

There is no CSS selector that can do this (in CSS3, even). Elements, in CSS, are never aware of their parent, so you cannot do a:parent h1 (for example). Nor are they aware of their siblings (in most cases), so you cannot do #container a:hover { /* do something with sibling h1 */ }. Basically, CSS properties cannot modify anything but elements and their children (they cannot access parents or siblings).

You could contain the h1 within the a, but this would make your h1 hoverable as well.

You will only be able to achieve this using JavaScript (jsFiddle proof-of-concept). This would look something like:

$("a.button").hover(function() {
    $(this).siblings("h1").addClass("your_color_class");
}, function() {
    $(this).siblings("h1").removeClass("your_color_class");
});
Cat
  • 66,919
  • 24
  • 133
  • 141
  • I was looking at this link. Any idea it'll work? http://net.tutsplus.com/tutorials/html-css-techniques/the-30-css-selectors-you-must-memorize/ – nasty Sep 25 '12 at 00:26
  • 1
    @Uds Afraid not. Those are standard CSS selectors (of which I'm already aware), and none of those allow selection of parent of sibling. (You can do `h1 + a` to select all `a`s which follow `h1`s, but this is not what you want.) Basically, a CSS property can only modify an element or its children, not siblings or parents. – Cat Sep 25 '12 at 00:28
  • The [general sibling combinator](http://www.w3.org/TR/css3-selectors/#general-sibling-combinators) would allow the selection of any sibling, **but** the first element on the selector must come before the second one on the markup (just like with the adjacent sibling combinator). So neither will solve the OP's problem, as you said. – bfavaretto Sep 25 '12 at 00:40
  • Looks like our wishes for a parent selector will soon be answered with the [level 4 subject selector](http://dev.w3.org/csswg/selectors4/#subject) – steveax Sep 25 '12 at 00:58
  • -1 because there are mistakes in both the answer and comment : "Nor are they aware of their siblings" is untrue and would deserve an edit to be fixed (they're aware of their siblings as long as they're specified after in the HTML). "You can do h1 + a to select all as which follow h1s" > is untrue because this is the "immediate sibling" selector, not the "general sibling". – Laurent S. Jun 18 '15 at 07:46
10

#banner:hover h1 {
  color: red;
}

#banner h1:hover {
  color: black;
}

a {
  position: absolute;
}
<div id="banner">
  <h1>Heading</h1>
  <a class="button" href="#">link</a>
</div>

The Fiddle: http://jsfiddle.net/joplomacedo/77mqZ/

The a element is absolutely positioned. Might not be perfect for your exisiting structure. Let me know, I might find a workaround.

isherwood
  • 58,414
  • 16
  • 114
  • 157
João Paulo Macedo
  • 15,297
  • 4
  • 31
  • 41
  • Tricky! and pretty cool. I can see how it might not work out for all situations, but cool anyway. – Stephen P Sep 25 '12 at 00:59
  • ;) Thanks. Trying to figure out a better way. – João Paulo Macedo Sep 25 '12 at 01:02
  • 2
    This is a good solution, but becomes problematic with more than just the `h1` and `a` in the `div`. – Cat Sep 25 '12 at 01:23
  • Yes, but the div is only there to help in this case - at least from what I understood from the question. And even if it weren't a workaround using the same principle is very possible. – João Paulo Macedo Sep 25 '12 at 01:31
  • But using javascript might probably just be the better idea. Easier to understand - more intuitive, no tricks. Until css allows to target previous siblings, I'll probably just use javascript in these cases. – João Paulo Macedo Sep 25 '12 at 01:33
  • Sheer greatness! Still valid after 5 years. Thanks for this eye opener – i used it for 4 columns that should change their width on hover – and it works like a charm. – tillinberlin Sep 18 '17 at 21:08
5

It is indeed possible to achieve this with only a few lines of CSS and some basic Flexbox understanding.

As Stephen P said in his answer, the adjacent sibling combinator does select immediately following siblings. To achieve what the OP asked, you could use two flex approaches:

Approach 1 (using "flex-flow" shorthand property)

.flex-parent {
  display: flex;
  flex-flow: column-reverse wrap
}

.flex-child-1:hover + .flex-child-2 {
  color: #FF3333;
}
<div class="flex-parent">
    <a class="flex-child-1">Hover me</a>  
    <h1 class="flex-child-2">I am changing color</h1>
</div>

Approach 2 (using "order" property and multiple children)

.flex-parent {
  display: flex;
  flex-direction: column;
}

.flex-child-1 {
  order: 2;
}

.flex-child-2 {
  order: 1;
}

.flex-child-3 {
  order: 3;
}

.flex-child-1:hover+.flex-child-2 {
  color: #FF3333;
}
<div class="flex-parent">
  <h1 class="flex-child-3">I am not changing color</h1>
  <a class="flex-child-1">Hover me</a>
  <h1 class="flex-child-2">I am changing color</h1>
</div>

Bonus:

CodePen Bonus

isherwood
  • 58,414
  • 16
  • 114
  • 157
MFB
  • 66
  • 1
  • 3
4

http://plnkr.co/edit/j5kGIav1E1VMf87t9zjK?p=preview

<!DOCTYPE html>
<html>

  <head>
    <link rel="stylesheet" href="style.css">
    <script src="script.js"></script>
    <style>
      ul:hover > li
      {
        opacity: 0.5;
      }
      ul:hover li:hover 
      {
        opacity: 1;
      }
    </style>
  </head>

  <body>
    <h1>Hello Plunker!</h1>
    <ul>
      <li>Hello</li>
      <li>Hello</li>
      <li>Hello</li>
      <li>Hello</li>
      <li>Hello</li>
    </ul>
  </body>

</html>

here is an example how it can be done in pure css , hope it helps somebody

3

Try this one-line pure CSS solution:

.parent:hover .child:not(:hover) {
  /* this style affects all the children *except* the one you're hovering over */
  color: red;
}

More info here: https://codyhouse.co/nuggets/styling-siblings-on-hover

Fred K
  • 13,249
  • 14
  • 78
  • 103
  • This is a good answer if you don't mind all of the children being hovered if you're hovering the parent but not a child. – martinedwards Apr 26 '22 at 13:58
1

Change the H1 tag into a link, style it the same as the normal text maybe? And then use this,

a:link {color:#FF0000;}      
a:hover {color:#FF00FF;}

And it should work when you hover :) you can also make it specific by containing it in a div and then targeting it like this:

.exampledivname a:link {color:#FF0000;}      
.exampledivname a:hover {color:#FF00FF;}

This should help.

bfavaretto
  • 71,580
  • 16
  • 111
  • 150
adamalexanderw
  • 1,143
  • 2
  • 15
  • 28
1

Someone helped me with this so I thought I would share here as well.

In your first example that is indeed impossible with pure CSS. However, when you wrap it with a parent container you then have the ability to do a bunch of stuff with hovering children.

#banner:hover>h1{
  color:red;
}
h1:hover{
  color:black !important;
}
#banner{
  display:inline-block;
}
.button{
  display:inline-block;
  font-size:24px;
  width:100%;
  border:1px solid black;
  text-align:center;
}
h1{
  padding:0;
  margin:0;
}
<div id="banner">
    <h1>Heading</h1>
    <a class="button" href="#">Button!</a>
</div>

The parent just controls the children who aren't currently being hovered. You then can set hover states for individual elements and classes to make sibling selection possible without JS.

Here is a more advanced example of this in action

https://codepen.io/levyA/pen/gOrdaLJ

0

For set styles in sibling elements you can use ~ character in first case when h1 hovered set color for a tag and in second case when a is hovered, change background color of h1 section

h1:hover ~ a {
    color: #e34423;
}
a:hover ~ h1 {
    background-color: #eee;
}
UnUsuAL
  • 74
  • 6
0

This might work, I've recently used this idea to stop sibling elements in an animation.

h1 { color: inherit; }

#banner:hover { color: your choice; }
   
54ka
  • 3,501
  • 2
  • 9
  • 24