仅使用CSS模拟“父级选择器”的:has()功能

9
长期以来,伪类选择器 :has() ,也称为父级选择器,一直被认为是解决许多选择器问题的答案,甚至有人认为它是完全不必要的。但根据最新版的 W3 规范 ,CSS 不会实现 Level 4 中的任何一个伪类选择器,其原因是效率低下且已经有了 JavaScript 功能的覆盖。
考虑到这一点,我一直在思考如何使用纯 CSS 来模拟此效果。下面以问答形式提供了一种方法来回答这个问题,但想知道是否有其他方法。具体地说,更有效的 CSS 实现或响应式 CSS 实现。

4
请注意,它仍然适用于(并需要符合)CSS外的实现,这意味着例如 querySelectorAll() 等内容可以潜在受益,而无需依赖 jQuery。 - BoltClock
3个回答

12

在Gecko和WebKit中,某些选择器可以使用<label for>和关联的<input>元素进行“跳跃”,这个元素可以放置在任何位置。虽然此方法不太稳定,但还是很有趣的。

#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>

(如果使用Chrome浏览器可能需要单击一次。)


复选框黑客技巧再次出击 - 顺便说一句,在FF中也可以使用。 - Zach Saucier
@ZachSaucier:不过这是另一个问题!我希望我是第一个发现它的人。...可能不是。 - Ry-
很好的回答!我会等一下看看是否还有其他人提出答案;如果没有,这个答案肯定会被选中。 - TylerH
6
这里提供了我对这种行为的一些研究结果以及为什么它在某些浏览器中有效而在其他浏览器中无效的解释,详见此处 - BoltClock

1
这是我在评论中提到的示例 - 与其被悬停元素实际上是一个子元素,它只是一个出现在应该受影响的元素之前的兄弟元素 - 并且使用相邻兄弟组合器来在第一个元素悬停时影响第二个元素。
.hover:hover ~ .container {
    background: red;
}

http://jsfiddle.net/95HKP/5/embedded/result/

由于它不需要额外的定位元素“覆盖”第二个元素,因此它不会遭受上述缺点的影响,例如第二个元素中的实际内容无法通过鼠标光标进行选择或其他形式的交互。只有“触发”元素本身是绝对定位在另一个元素上 - 而不是使用绝对定位,也可以使用其他方法来实现,例如负的margin-bottom
(我还引入了一个“间隔”元素,以防止内容被绝对定位的元素重叠 - 这不一定需要额外的元素,例如使用::before创建的伪元素也可以。)
因此,再次强调,这也不是“父选择器” - 只是通过使用一些定位和其他广泛可用的选择器来实现某种效果。

和 Zach 的版本相同的方法(减去我之前在答案底部链接的填充文本),好在它不是父选择器,否则它就无法回答这个问题(该问题明确要求实现伪造父选择器相同效果的实现)。+1。 - TylerH

1

JSFiddle示例

对于这种方法,我创建了一个固定尺寸的容器元素,里面有一个单独的子元素,占据200像素乘200像素的空间。

然后,我添加了两个绝对定位的元素(.glass1.glass2),模拟玻璃窗格(“你可以看,但不能触摸”),并在它们上使用z-index,使它们覆盖容器元素的剩余空间。

添加这些玻璃窗格是为了模拟除非你悬停在子元素上,否则什么都不会发生的效果。否则,这种方法就不会模仿父选择器行为;它只是一个正常的:hover实现。

此时,只需要在容器中添加一个:hover属性,它不会影响子元素所覆盖的区域即可。

.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>

这种实现的明显问题在于其静态性和缺乏响应能力。更有经验的人可能会允许一定程度的响应能力。
编辑:ZachSaucier的更高效版本

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接