Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Faking the :has() "parent selector" using only CSS

It's long been hailed as the answer to many selector problems, even disputed by some as entirely unnecessary, but the Selectors Level 4 pseudo-class :has(), better known as the parent selector, is the only one from Level 4 that will not be implemented in CSS, according to the latest revision of the W3 Spec. Reason being that it's inefficient and already covered by JavaScript functionality.

Mulling over this, I've been thinking of how to fake this effect using pure CSS. Below I've provided one method, Q&A style, for achieving the effect as an answer, but would like to know if there are other methods. Specifically, more efficient CSS implementations, or responsive CSS implementations.

like image 417
TylerH Avatar asked Jun 24 '14 18:06

TylerH


2 Answers

In Gecko and WebKit, certain selectors can “jump” using <label for> and an associated <input> element positioned anywhere. This works unreliably, but is still kind of fun.

#before {
    left: -9999px;
    position: absolute;
}
#parent {
    padding: 0.5em;
}
#before:hover + #parent {
    background-color: #123;
    color: white;
}
#label {
    border: 0.1em solid #678;
    border-radius: 0.2em;
    display: inline-block;
    padding: 0.5em;
}
<input type="checkbox" id="before">

<div id="parent">
    <label for="before" id="label">Hover over me!</label>
</div>

(You might have to click once if using Chrome.)

like image 151
Ry- Avatar answered Oct 22 '22 07:10

Ry-


JSFiddle Example

For this method, I created a fixed-position container element of static size, with a single child element inside of it, taking up 200px by 200px.

Then, I added two absolutely-positioned elements (.glass1 and .glass2) to simulate glass panes ("You can look, but you can't touch"), and I used z-index on them so that they would cover the remaining space of the container element.

The addition of these glass panes is to simulate the effect of nothing happening unless you hover over the child. Otherwise this method wouldn't imitate parent selector behavior; it would just be a normal :hover implementation.

At this point, it was just a matter of adding a :hover property to the container that did not affect the area covered by the child element.

.container {
    background: lavender;
    top: 0;
    left: 0;
    position: absolute;
    width: 600px;
    height: 400px;
    z-index: 0;
    border: 1px solid black;
}
.glass1 {
    width: 400px;
    height: 200px;
    position: absolute;
    left: 200px;
    top: 0;
    z-index: 2;
}
.glass2 {
    width: 600px;
    height: 200px;
    position: absolute;
    z-index: 3;
    top: 200px;
    left: 0;
}
.hover {
    background: lightblue;
    width: 200px;
    height: 200px;
    margin: 0;
    position: absolute;
}
.container:hover:not(.hover) {
    background: seagreen;
}
<div class="container">
    <div class="hover">Hover over me. Change my parent.</div>
</div>
<div class="glass1"></div>
<div class="glass2"></div>

The obvious problem with this implementation is its static nature and its lack of responsiveness. A more-practiced hand might allow for some measure of responsiveness.

Edit: More efficient version by ZachSaucier

like image 2
TylerH Avatar answered Oct 22 '22 07:10

TylerH