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

标签 css css-selectors

我有一堆带有类名的元素 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>

这个选择器有什么问题,我如何更正它以定位第一个 child 的类 red ?

最佳答案

这是作者误解 :first-child 如何工作的最著名的例子之一。 Introduced in CSS2:first-child 伪类代表 其父 的第一个 child 。就是这样。有一个非常普遍的误解,即它选择第一个匹配复合选择器其余部分指定的条件的子元素。由于选择器的工作方式(参见 here 的解释),这根本不是真的。
Selectors level 3 introduces a :first-of-type pseudo-class ,表示其元素类型的兄弟元素中的第一个元素。 This answer 用插图解释了 :first-child:first-of-type 之间的区别。但是,与 :first-child 一样,它不会查看任何其他条件或属性。在 HTML 中,元素类型由标签名称表示。在问题中,该类型是 p
不幸的是,没有类似的 :first-of-class 伪类用于匹配给定类的第一个子元素。在首次发布此答案时, the newly published FPWD of Selectors level 4 introduced an :nth-match() pseudo-class 围绕现有选择器机制设计,正如我在第一段中提到的那样,通过添加选择器列表参数,您可以提供复合选择器的其余部分以获得所需的过滤行为。近年来,这个功能是 subsumed into :nth-child() itself ,选​​择器列表作为可选的第二个参数出现,以简化事情并避免 :nth-match() 在整个文档中匹配的错误印象(请参阅下面的最后说明)。
当我们等待 cross-browser support 时(说真的,已经将近 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;
}
...然后使用 the general sibling combinator ~ 在覆盖规则中使用 attribute selectors 来“撤消”具有第一个类之后的元素的样式:
/* 
 * 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>

  • 没有应用规则;不渲染边框。
    这个元素没有类 red ,所以它被跳过了。
  • 只应用第一条规则;呈现红色边框。
    该元素具有 red 类,但它的父元素中没有任何具有 red 类的元素。因此不应用第二条规则,只应用第一条规则,元素保持其边框。
  • 两条规则都适用;不渲染边框。
    该元素的类为 red 。它前面还至少有一个其他元素,其类为 red 。因此,两个规则都被应用,第二个 border 声明覆盖了第一个,从而“撤消”了它,可以这么说。

  • 作为奖励,虽然它是在 Selectors 3 中引入的,但是一般的兄弟组合器实际上在 IE7 和更新版本中得到了很好的支持,不像 :first-of-type:nth-of-type() 只被 IE9 支持。如果您需要良好的浏览器支持,那么您很幸运。
    事实上,兄弟组合器是该技术中唯一重要的组件,并且它具有如此惊人的浏览器支持,这使得该技术非常通用——除了类选择器之外,您还可以调整它以通过其他东西过滤元素:
  • 您可以使用它来解决 IE7 和 IE8 中的 :first-of-type,只需提供一个类型选择器而不是类选择器(同样,在后面的问题中更多地介绍了它的错误用法):
     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 */
     }
    
  • 您可以通过 pseudo-elements 或任何其他简单的选择器而不是类进行过滤。
  • 您也可以将此覆盖技术与 Selectors API 结合使用,即使伪元素在技术上不是简单的选择器。

  • 请注意,为了使其工作,您需要提前知道其他同级元素的默认样式是什么,以便您可以覆盖第一条规则。此外,由于这涉及 CSS 中的覆盖规则,因此您无法通过与 Seleniumthey function very differently from :eq() et al 的 CSS 定位器一起使用的单个选择器来实现相同的功能。
    最后一点,请记住,此答案假定问题正在寻找具有给定类的任意数量的第一个子元素。对于整个文档中复杂选择器的第 n 次匹配,既没有伪类,也没有通用 CSS 解决方案——解决方案是否存在在很大程度上取决于文档结构。为此,jQuery 提供了 :first:last:nth-child() 等,但请再次注意 Philip Daubmeier 。使用 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
    

    尽管 Martyn 最初接受的答案中的 .red:nth-of-type(1) 解决方案有效(最初由 ojit_a 编写,但后来被删除),但它的行为并不像您期望的那样。
    例如,如果您只想在此处选择 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 元素。

    关于带有类的第一个元素的 CSS 选择器,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/2717480/

    相关文章:

    html - 使用过滤器 :FlipH in IE only 后呈现锯齿状字体

    javascript - 通过模式获取定义的 CSS 类

    html - 我需要有关第 n 个 child 的帮助

    html - 如何定位其父级中任何类型的第一个标题

    html - CSS 选择器 - 确定从最后一个 child 开始的偶数/奇数 child

    css - Sass/Compass - 覆盖 css 以减慢速度

    javascript - 如何获取或 console.log @keyframes css 动画的颜色?

    javascript - 更改 :before selector from javascript

    HTML5 属性值规范化

    javascript - nth-child 不处理动态生成的 DOM