Access child element relative to generated content

Problem

Let's say I have HTML like so:

<div class="foo">
    <div>Stuff</div>
</div>

And some CSS like so:

.foo > div { display: none; }
.foo:after { /* rules */ }

I would like to be able to do something like

.foo:after:hover > div { display: block; }

to have the user hover over the generated content element and show the child div. Does anyone know how to do this? My attempts so far have been fruitless. Thanks.

Problem courtesy of: Jason

Solution

This question seems to be the opposite of Can I target a :before or :after pseudo-element with a sibling combinator?

In your case, you cannot apply pseudo-classes to pseudo-elements. This also means you cannot choose to apply styles only when a pseudo-element is on :hover but not its generating element. Instead, you can only select a pseudo-element depending on the pseudo-class states of its generating element, and only when this generating element is the subject of the selector.

Also, as mentioned, pseudo-elements are not part of the DOM. Therefore, similarly to how you cannot reach pseudo-elements relatively to real children within the same generating element as shown in my answer to the above question (explanation in the above link):

a[href^="http"] img ~ :after

You cannot select real elements relatively to pseudo-elements within the same generating element:

.foo:after > div

So to sum up, the reason why the following selector won't work:

.foo:after:hover > div

Is twofold:

  1. Pseudo-elements cannot have pseudo-class states, making :after:hover invalid.

  2. The subject of the selector here is not .foo, but div, so :after cannot apply to .foo. Consequently, attempting to select a real element as the subject of a selector after applying a pseudo-element to another selector doesn't make sense.

If you really must not display your div on .foo:hover, but on another child element, then your only way around this without using JavaScript is to make another real element under .foo that comes before the div (not after):

<div class="foo">
    <div></div>
    <div>Stuff</div>
</div>

This is because there is no previous sibling selector; the adjacent sibling selector, which you can use in place of :after > is only for selecting the next sibling:

.foo > div:first-child + div { display: none; }
.foo > div:first-child { /* rules */ }
.foo > div:first-child:hover + div { display: block; }
Solution courtesy of: BoltClock

Discussion

Try this solution: http://jsfiddle.net/QERPq/4/.

It may or may not be what you are after.

HTML:

<div class="foo">pre-hover stuff</div>

CSS:

.foo > div {
    display: none;
    font-size: 200px;
}
.foo:after {
    content: '';
    display: block;
    position: absolute;
    left: 100px;
    top: 100px;
    width: 100px;
    height: 20px;
    background: yellow;
}
.foo:hover:after {
    display: block;
    content: 'Stuff';
    width: 40px;
    height: 40px;
    background: red;
}
Discussion courtesy of: uınbɐɥs

You can't interact with generated content, as it's not present in the DOM (it can be accessed, but, so far at least, it's read-only); the only option you have is to use :hover on .foo itself:

.foo:hover > div { }

You could, of course, style the generated content differently on :hover:

.foo:hover::after { }

But, I suspect, that probably isn't any part of what you want to do.

Also, your selector as written: .foo:after:hover > div I think, would try to style the div child of the generated content; which it can't have.

Discussion courtesy of: David Thomas

You were close. Just ditch the :after portion.

http://jsfiddle.net/QERPq/

UPDATE: http://jsfiddle.net/QERPq/2/ Basically, this says that when you hover over this item, to select the next item (* = catchall) and display it.

Discussion courtesy of: Chad

This recipe can be found in it's original form on Stack Over Flow.