小编典典

具有类的第一个元素的 CSS 选择器

css

我有一堆带有类名的元素red,但我似乎无法class="red"使用以下 CSS 规则选择第一个元素:

.home .red:first-child {
    border: 1px solid red;
}
<div class="home">
    <span>blah</span>
    <p class="red">first</p>
    <p class="red">second</p>
    <p class="red">third</p>
    <p class="red">fourth</p>
</div>

这个选择器有什么问题,我该如何纠正它以定位第一个具有 class 的孩子red


阅读 195

收藏
2022-04-27

共1个答案

小编典典

这是作者误解:first-child工作原理的最著名例子之一。在 CSS2 中引入:first-child伪类代表其父级的第一个子级。就是这样。有一个非常普遍的误解,即它会选择第一个匹配复合选择器其余部分指定的条件的子元素。由于选择器的工作方式(请参阅此处以获取解释),这根本不是真的。

选择器级别 3 引入了一个:first-of-type伪类,它表示其元素类型的兄弟元素中的第一个元素。这个答案:first-child用插图解释了和之间的区别:first-of-type。但是,与 一样:first-child,它不查看任何其他条件或属性。在 HTML 中,元素类型由标签名称表示。在问题中,该类型是p.

不幸的是,没有类似:first-of-class的伪类来匹配给定类的第一个子元素。在首次发布此答案时,新发布的 FPWD 选择器级别 4 引入了一个:nth-match()伪类,围绕现有的选择器机制设计,正如我在第一段中提到的那样,通过添加一个选择器列表参数,您可以通过它提供其余的的复合选择器以获得所需的过滤行为。近年来,此功能被纳入其:nth-child()自身,选择器列表作为可选的第二个参数出现,以简化事情并避免在:nth-match()整个文档中匹配的错误印象(请参阅下面的最后注释)。

当我们等待跨浏览器支持时(说真的,已经将近 10 年了,在过去的 5 年中只有一个实现),Lea Verou和我独立开发的一种解决方法(她首先做到了!)是首先将您想要的样式应用于该类的所有元素:

/* 
 * Select all .red children of .home, including the first one,
 * and give them a border.
 */
.home > .red {
    border: 1px solid red;
}

…然后使用覆盖规则中的通用兄弟组合符“撤消”具有第一个之后的类的元素的样式:~

/* 
 * Select all but the first .red child of .home,
 * and remove the border from the previous rule.
 */
.home > .red ~ .red {
    border: none;
}

现在只有第一个元素class="red"有边框。

以下是如何应用规则的说明:

.home > .red {
    border: 1px solid red;
}

.home > .red ~ .red {
    border: none;
}
<div class="home">
  <span>blah</span>         <!-- [1] -->
  <p class="red">first</p>  <!-- [2] -->
  <p class="red">second</p> <!-- [3] -->
  <p class="red">third</p>  <!-- [3] -->
  <p class="red">fourth</p> <!-- [3] -->
</div>
  1. 不适用任何规则;不渲染边框。
    这个元素没有 class red,所以它被跳过了。
  2. 只应用第一条规则;呈现红色边框。
    该元素具有 class red,但red其父级中没有任何具有该 class 的元素。因此不应用第二条规则,只应用第一条规则,并且元素保持其边界。
  3. 两条规则都适用;不渲染边框。
    这个元素有类red。它的前面还有至少一个与 class 相同的元素red。因此,两个规则都适用,第二个border声明覆盖第一个,从而“撤消”它,可以这么说。

作为奖励,虽然它是在 Selectors 3 中引入的,但通用兄弟组合器实际上得到了 IE7 和更新版本的很好的支持,这:first-of-type:nth-of-type()只有 IE9 及更高版本才支持。如果您需要良好的浏览器支持,那么您很幸运。

事实上,兄弟组合器是该技术中唯一重要的组件,并且它具有如此惊人的浏览器支持,这使得该技术非常通用 - 除了类选择器之外,您还可以将其用于通过其他东西过滤元素:

  • 您可以在 IE7 和 IE8 中使用它来解决:first-of-type问题,只需提供类型选择器而不是类选择器(同样,在后面的问题中更多关于其不正确用法的内容):

```css
article > p {
/ Apply styles to article > p:first-of-type, which may or may not be :first-child /
}

article > p ~ p {
/ Undo the above styles for every subsequent article > p /
}
```

  • 您可以按属性选择器或任何其他简单选择器而不是类进行过滤。

  • 即使伪元素在技术上不是简单的选择器,您也可以将这种覆盖技术与伪元素结合使用。

请注意,为了使其正常工作,您需要提前知道其他兄弟元素的默认样式是什么,以便您可以覆盖第一条规则。此外,由于这涉及覆盖 CSS 中的规则,因此您无法使用与Selectors APISelenium的 CSS 定位器一起使用的单个选择器来实现相同的目的。

最后一点,请记住,此答案假定问题正在寻找具有给定类的任意数量的第一个子元素。对于整个文档中复杂选择器的第 n 次匹配,既没有伪类,也没有通用的 CSS 解决方案——解决方案是否存在很大程度上取决于文档结构。jQuery为此提供了:eq(),:first等,但再次注意它们的功能与et al非常不同。使用 Selectors API,您可以使用获取第一个匹配项::last:nth-child()document.querySelector()

var first = document.querySelector('.home > .red');

document.querySelectorAll()与索引器一起使用以选择任何特定匹配:

var redElements = document.querySelectorAll('.home > .red');
var first = redElements[0];
var second = redElements[1];
// etc

尽管Philip Daubmeier.red:nth-of-type(1)最初接受的答案中的解决方案有效(最初由Martyn编写,但后来被删除),但它的行为并不像您期望的那样。

例如,如果您只想选择p此处:

<p class="red"></p>
<div class="red"></div>

…那么你不能使用.red:first-of-type(equivalent to .red:nth-of-type(1)),因为每个元素都是它的类型中的第一个(也是唯一一个)(pdiv分别),所以两者都将被选择器匹配。

当某个类的第一个元素也是其类型的第一个元素时,伪类将起作用,但这只是巧合。这种行为在菲利普的回答中得到了证明。当您在该元素之前插入相同类型的元素时,选择器将失败。从问题中提取标记:

<div class="home">
  <span>blah</span>
  <p class="red">first</p>
  <p class="red">second</p>
  <p class="red">third</p>
  <p class="red">fourth</p>
</div>

应用一个规则.red:first-of-type会起作用,但是一旦你添加另一个p没有类的规则:

<div class="home">
  <span>blah</span>
  <p>dummy</p>
  <p class="red">first</p>
  <p class="red">second</p>
  <p class="red">third</p>
  <p class="red">fourth</p>
</div>

…选择器将立即失败,因为第一个.red元素现在是第二个 p元素。

2022-04-27