协慌网

登录 贡献 社区

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

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

.red:first-child {
    border: 5px solid red;
}
<p class="red"></p>
<div class="red"></div>

这个选择器有什么问题,我该如何纠正?

感谢这些评论,我发现该元素必须是其父母的第一个孩子才能被选中,这与我的情况不同。我有以下结构,这条规则失败,如评论中所述:

.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孩子?

答案

这是作者误解如何:first-child工作的最着名的例子之一。 在 CSS2 中引入 :first-child伪类表示其父的第一个子级。而已。有一种非常普遍的误解,即它会选择第一个匹配复合选择器其余部分指定的条件的子元素。由于选择器的工作方式(参见此处的解释),这根本不是真的。

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

不幸的是,没有类似的:first-of-class用于匹配给定类的第一个子元素的第一:first-of-class伪类。 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"的第一个元素才会有边框。

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

<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类,所以它被跳过了。

  2. 只应用第一条规则; 呈现红色边框。
    此元素具有red类,但在其父元素之前没有任何具有red类的元素。因此,不应用第二个规则,仅应用第一个规则,并且元素保持其边界。

  3. 这两条规则都适用; 没有渲染边框。
    此元素具有red类。它之前还有至少一个其他元素为red 。因此,应用了两个规则,并且第二个border声明覆盖了第一个,从而 “撤消” 它,可以这么说。

作为奖励,虽然它是在 Selectors 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 定位器一起使用。

值得一提的是,Selectors 4 引入了对:nth-child()表示法的扩展 (最初是一个全新的伪类,名为:nth-match() ),它允许你使用类似于:nth-child(1 of .red)代替假想的.red:first-of-class 。作为一个相对较新的提案,还没有足够的可互操作的实现,以便它可以在生产站点中使用。希望这会很快改变。与此同时,我建议的解决方法应该适用于大多数情况。

请记住,这个答案假设问题是查找具有给定类的每个第一个子元素。 对于整个文档中的复杂选择器的第 n 次匹配,既没有伪类也没有通用的 CSS 解决方案 - 是否存在解决方案在很大程度上取决于文档结构。 jQuery 提供:eq() :first:last和更多用于此目的,但再次注意它们的功能与:nth-child()等有很大不同 。使用 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

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

例如,如果您只想在原始标记中选择p

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

... 然后你不能使用.red:first-of-type (相当于.red:nth-of-type(1) ),因为每个元素是它的第一个(也是唯一的)类型( pdiv分别),所以两者都将由选择器匹配。

当某个类的第一个元素也是它的第一个类型时 ,伪类将起作用,但这只是巧合 。 Philip 的回答证明了这种行为。在此元素之前粘贴在相同类型的元素中的那一刻,选择器将失败。采用更新的标记:

<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元素。

: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将是红色然后。

更新:

我不知道为什么 martyn 删除了他的答案,但他有解决方案, :nth-of-type选择器:

<html>
<head>
<style type="text/css">
.red:nth-of-type(1)
{
    border:5px solid red;
} 
</style>
</head>

<body>
    <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>
</body>
</html>

Martyn 的学分。更多信息,例如这里 。请注意,这是一个 CSS 3 选择器,因此并非所有浏览器都能识别它(例如 IE8 或更早版本)。

正确答案是:

.red:first-child, :not(.red) + .red { border:5px solid red }

第一部分:如果元素是第一个到它的父级并且具有类 “红色”,它将获得边界。
第二部分:如果 “.red” 元素不是第一个到它的父元素,而是紧跟一个没有类 “.red” 的元素,它也应该得到所述边界的荣誉。

小提琴或它没有发生。

Philip Daubmeier 的答案虽然被接受了,但却不正确 - 请参阅随附的小提琴。
BoltClock 的答案可行,但不必要地定义和覆盖样式
(特别是否则会继承不同边框的问题 - 你不想声明其他边框:none)

编辑:如果你有几次非红色的 “红色”,每个 “第一” 红色将获得边框。为了防止这种情况,人们需要使用 BoltClock 的答案。看小提琴

这是作者误解如何:first-child工作的最着名的例子之一。 在 CSS2 中引入 :first-child伪类表示其父的第一个子级。而已。有一种非常普遍的误解,即它会选择第一个匹配复合选择器其余部分指定的条件的子元素。由于选择器的工作方式(参见此处的解释),这根本不是真的。

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

不幸的是,没有类似的:first-of-class用于匹配给定类的第一个子元素的第一:first-of-class伪类。 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"的第一个元素才会有边框。

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

<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类,所以它被跳过了。

  2. 只应用第一条规则; 呈现红色边框。
    此元素具有red类,但在其父元素之前没有任何具有red类的元素。因此,不应用第二个规则,仅应用第一个规则,并且元素保持其边界。

  3. 这两条规则都适用; 没有渲染边框。
    此元素具有red类。它之前还有至少一个其他元素为red 。因此,应用了两个规则,并且第二个border声明覆盖了第一个,从而 “撤消” 它,可以这么说。

作为奖励,虽然它是在 Selectors 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 定位器一起使用。

值得一提的是,Selectors 4 引入了对:nth-child()表示法的扩展 (最初是一个全新的伪类,名为:nth-match() ),它允许你使用类似于:nth-child(1 of .red)代替假想的.red:first-of-class 。作为一个相对较新的提案,还没有足够的可互操作的实现,以便它可以在生产站点中使用。希望这会很快改变。与此同时,我建议的解决方法应该适用于大多数情况。

请记住,这个答案假设问题是查找具有给定类的每个第一个子元素。 对于整个文档中的复杂选择器的第 n 次匹配,既没有伪类也没有通用的 CSS 解决方案 - 是否存在解决方案在很大程度上取决于文档结构。 jQuery 提供:eq() :first:last和更多用于此目的,但再次注意它们的功能与:nth-child()等有很大不同 。使用 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

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

例如,如果您只想在原始标记中选择p

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

... 然后你不能使用.red:first-of-type (相当于.red:nth-of-type(1) ),因为每个元素是它的第一个(也是唯一的)类型( pdiv分别),所以两者都将由选择器匹配。

当某个类的第一个元素也是它的第一个类型时 ,伪类将起作用,但这只是巧合 。 Philip 的回答证明了这种行为。在此元素之前粘贴在相同类型的元素中的那一刻,选择器将失败。采用更新的标记:

<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元素。

: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将是红色然后。

更新:

我不知道为什么 martyn 删除了他的答案,但他有解决方案, :nth-of-type选择器:

<html>
<head>
<style type="text/css">
.red:nth-of-type(1)
{
    border:5px solid red;
} 
</style>
</head>

<body>
    <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>
</body>
</html>

Martyn 的学分。更多信息,例如这里 。请注意,这是一个 CSS 3 选择器,因此并非所有浏览器都能识别它(例如 IE8 或更早版本)。

正确答案是:

.red:first-child, :not(.red) + .red { border:5px solid red }

第一部分:如果元素是第一个到它的父级并且具有类 “红色”,它将获得边界。
第二部分:如果 “.red” 元素不是第一个到它的父元素,而是紧跟一个没有类 “.red” 的元素,它也应该得到所述边界的荣誉。

小提琴或它没有发生。

Philip Daubmeier 的答案虽然被接受了,但却不正确 - 请参阅随附的小提琴。
BoltClock 的答案可行,但不必要地定义和覆盖样式
(特别是否则会继承不同边框的问题 - 你不想声明其他边框:none)

编辑:如果你有几次非红色的 “红色”,每个 “第一” 红色将获得边框。为了防止这种情况,人们需要使用 BoltClock 的答案。看小提琴