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

新手上路,请多包涵

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

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

这个选择器有什么问题,我该如何纠正它以针对第一个具有 red 类的孩子?

原文由 Rajat 发布,翻译遵循 CC BY-SA 4.0 许可协议

阅读 2.2k
2 个回答

这是作者误解 :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. 不适用任何规则;不渲染边框。

这个元素没有类 red ,所以它被跳过了。

  1. 只应用第一条规则;呈现红色边框。

此元素具有类 red ,但它的父级中没有任何具有类 red 的元素。因此不应用第二条规则,只应用第一条规则,并且元素保持其边界。

  1. 两条规则都适用;不渲染边框。

该元素具有类 red 。它前面还有至少一个其他元素,其类为 red 。因此,这两个规则都适用,第二个 border 声明覆盖第一个,从而“撤消”它,可以这么说。

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

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

  • 您可以使用它来解决 :first-of-type 在 IE7 和 IE8 中,只需提供类型选择器而不是类选择器(同样,在后面部分的问题中更多关于它的不正确用法):
    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 provides :eq() , :first , :last and more for this purpose, but note again that they function very differently from :nth-child() et al .使用 Selectors API,您可以使用 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


尽管 .red:nth-of-type(1) Philip Daubmeier 工作的原始接受答案中的解决方案(最初由 Martyn 编写,但此后被删除),但它的行为方式并不符合您的预期。

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

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

…那么您不能使用 .red:first-of-type (相当于 .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 元素。

原文由 BoltClock 发布,翻译遵循 CC BY-SA 4.0 许可协议

:first-child 选择器,顾名思义,用于选择父标签的第一个子标签。所以这个例子会起作用(只是 在这里 试过):

 <body>
    <p class="red">first</p>
    <div class="red">second</div>
</body>

但是,如果您将标签嵌套在不同的父标签下,或者您的标签 red 不是父标签下的第一个标签,这将不起作用。

还要注意,这不仅适用于整个文档中的第一个这样的标签,而且每次都有一个新的父级包裹在它周围,例如:

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

firstthird 将是红色的。

对于您的情况,您可以使用 :nth-of-type 选择器:

 .red:nth-of-type(1)
{
    border:5px 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>

感谢 Martyn ,他删除了包含这种方法的答案。更多信息例如 这里。请注意,这是一个 CSS 3 选择器,因此并非所有浏览器都能识别它(例如 IE8 或更早版本)。

原文由 Philip Daubmeier 发布,翻译遵循 CC BY-SA 4.0 许可协议

撰写回答
你尚未登录,登录后可以
  • 和开发者交流问题的细节
  • 关注并接收问题和回答的更新提醒
  • 参与内容的编辑和改进,让解决方法与时俱进
推荐问题