使用带有伪 class 的 CSS 后代组合器将样式应用于嵌套子项

Apply styles to nested child using CSS descendant combinator with pseudo-class

我们如何将样式应用于父元素 (P) 的子元素 (C),同时在出现伪 class 状态时使用后代组合器指定它们之间的关系?

一个小场景是父元素.menu-item的子元素a需要在悬停.menu-item时改变颜色。这在下面给出的代码中表示:

.menu {
  list-style-type: none;
}
.menu-item {
  padding: 0.5rem;
}
.menu-item a {
  text-decoration: none;
  color: #222;
}
.menu-item:hover .menu-item a {
  color: #a2b;
}
<ul class="menu">
  <li class="menu-item">
    <a href="#" class="menu-link">Home</a>
  </li>
  <li class="menu-item">
    <a href="#" class="menu-link">About</a>
  </li>        
  <li class="menu-item">
    <a href="#" class="menu-link">Contact</a>
  </li>
</ul>

但是,当编写以下代码片段时未明确指定 a.menu-item 的子级时它会起作用:

.menu-item:hover a {
  color: #a2b;
}

在 CSS 中 明确指定元素之间的关系 时,还有其他方法可以实现吗?

我会直接使用 a:hover 除非你想让整个 menu-item div 在悬停时改变颜色

a:hover {
  color: #a2b;
}

<ul class="menu">
  <li class="menu-item">
    <a href="#" class="menu-link">Home</a>
  </li>
  <li class="menu-item">
    <a href="#" class="menu-link">About</a>
  </li>        
  <li class="menu-item">
    <a href="#" class="menu-link">Contact</a>
  </li>
</ul>
.menu {
  list-style-type: none;
}
.menu-item {
  padding: 0.5rem;
}
.menu-item {
  text-decoration: none;
  color: #222;
}
a:hover {
  color: #a2b;
}

.menu-item:hover .menu-item a { color: #a2b; } 不起作用的原因是层次结构为菜单 > 菜单项 > a:hover

你说:

However, it works when the following code snippet is written without explicitly specifying that a is a child of .menu-item:

.menu-item:hover a { color: #a2b; }

该代码确实没有明确指定 a 是 .menu-item 的子项。它说的是

'here's the color for any a that is a descendant of a hovered menu-item'

在你的情况下,这就是你想要的,因为锚元素中不会有其他嵌套的锚元素,所以你只改变一个的颜色。

这可能是一种更容易维护的表达要求的方式,而不是明确要求 a 是菜单项的子项(而不仅仅是任何后代),因为如果后来有人将锚元素放在 div 例如,当菜单项悬停时,您仍然希望颜色发生变化。

题中给出的其他代码:

.menu-item:hover .menu-item a {
  color: #a2b;
}

是说

when a menu-item is hovered look for all its menu-item descendants and color any of their anchor element descendants.

嵌套菜单项不是您所拥有的结构,因此在悬停的菜单项上没有任何反应。

Is there any other way to achieve this in CSS while explicitly specifying the relationship between elements?

你已经是,使用后代组合器。

组合器表达两个元素之间的关系,每个元素由一个复合选择器表示。复合选择器由一个或多个简单选择器组成,所有代表元素状态的某些方面。选择器不要求您分离元素的状态,以及同一元素与另一个元素的关系。这既有利于它的优雅,也有利于它的损害,因为它造成了许多限制,例如不能从后代遍历到它的祖先,不能从后来的兄弟姐妹遍历到它以前的兄弟姐妹,等等。但这是一个单独的问题。

在选择器.menu-item:hover a中,有两个复合选择器:.menu-item:hovera。三个简单选择器中的每一个都代表一种特定状态,但其中两个通过出现在单个复合选择器中来应用于同一元素。在这种情况下,:hover 伪 class 表示它代表 .menu-item,尽管仅当它处于悬停状态时。

这并不意味着或暗示 a 仅在处于悬停状态时从 .menu-item 下降。相反,它意味着 a 元素只有在它从至少一个与 .menu-item:hover 匹配的元素(换句话说:至少有一个祖先)派生时才会匹配。事实上,如果将 .menu-item:hover 更改为 :hover.menu-item,它仍然会匹配,因为复合选择器中 classes 和伪 classes 之间的顺序无关紧要,他们是平等的。您也可以将其视为“明确地说 a 是处于悬停状态的元素的后代”,并尝试编写类似 :hover.menu-item :hover a 的内容,这显然行不通任何一个。这可能会帮助您理解为什么已经通过使用后代组合器本身明确指定了这种关系。

因此选择器 .menu-item:hover a(并且,推而广之,:hover.menu-item a)意味着

Apply this rule to any a element
that descends from (or: has an ancestor that is) a .menu-item element that's in the :hover state.

并且,由于在使用后代组合子时已经明确指定了关系,因此选择器 .menu-item:hover .menu-item a两个 后代组合子意味着

Apply this rule to any a element
that descends from a .menu-item element
that, itself, descends from another .menu-item element, one that's in the :hover state.

这就是它不起作用的原因。此外,正如 A Haworth 所说,由于您的标记中没有任何嵌套的 .menu-item,因此它根本不会匹配任何 a 元素。

有关组合器和元素状态如何在选择器中工作的更深入的解释,请参阅我对这个问题的回答: