Sélecteur CSS pour le premier élément avec la classe

947

J'ai un tas d'éléments avec un nom de classe red, mais je n'arrive pas à sélectionner le premier élément avec la class="red"règle CSS suivante:

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

Qu'est-ce qui ne va pas dans ce sélecteur et comment le corriger?

Grâce aux commentaires, j'ai compris que l'élément devait être le premier enfant de son parent à être sélectionné, ce qui n'est pas le cas pour moi. J'ai la structure suivante, et cette règle échoue comme mentionné dans les commentaires:

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

Comment puis-je cibler le premier enfant avec la classe red?

Rajat
la source
pour ceux qui deviennent confus à cause de différents types d'éléments dans mon exemple (p, div), les rendre identiques ne fonctionne pas non plus et le problème existe toujours.
Rajat
4
Je pense que c'est comme ça que le sélecteur du premier enfant aurait dû fonctionner ...
Felix Eve
28
: premier-enfant ne serait pas un très bon nom alors. Si vous aviez un fils, puis une fille, vous n'appelleriez pas votre fille votre premier-né. De même, le premier .home> .red n'est pas le premier enfant de .home, il serait donc inapproprié de l'appeler comme tel.
BoltClock
Non, c'est comme ça que le: premier du genre aurait dû fonctionner
Evan Thompson
@EvanThompson Voilà comment :first-of-type ça marche.
TylerH

Réponses:

1427

Il s'agit de l'un des exemples les plus connus d'auteurs mal compris le :first-childfonctionnement.Introduite en CSS2 , la :first-childpseudo-classe représente le tout premier enfant de son parent . C'est ça. Il y a une idée fausse très répandue selon laquelle il ramasse l'élément enfant qui est le premier à correspondre aux conditions spécifiées par le reste du sélecteur composé. En raison du fonctionnement des sélecteurs (voir ici pour une explication), ce n'est tout simplement pas vrai.

Les sélecteurs de niveau 3 introduisent une :first-of-typepseudo-classe , qui représente le premier élément parmi les frères et sœurs de son type d'élément. Cette réponse explique, avec des illustrations, la différence entre :first-childet :first-of-type. Cependant, comme avec :first-child, il ne regarde aucune autre condition ou attribut. En HTML, le type d'élément est représenté par le nom de la balise. Dans la question, ce type est p.

Malheureusement, il n'y a pas de :first-of-classpseudo-classe similaire pour faire correspondre le premier élément enfant d'une classe donnée. Une solution de contournement Lea Verou et moi avons trouvée pour cela (bien que de manière totalement indépendante) consiste à appliquer d'abord les styles souhaités à tous vos éléments avec cette classe:

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

... puis "annuler" les styles des éléments avec la classe qui vient après le premier , en utilisant le combinateur frère général~ dans une règle prioritaire:

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

Maintenant, seul le premier élément avec class="red" aura une bordure.

Voici une illustration de la façon dont les règles sont appliquées:

<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. Aucune règle n'est appliquée; aucune bordure n'est rendue.
    Cet élément n'a pas de classe red, il est donc ignoré.

  2. Seule la première règle est appliquée; une bordure rouge est rendue.
    Cet élément a la classered , mais il n'est précédé d'aucun élément avec la classe reddans son parent. Ainsi la deuxième règle n'est pas appliquée, seulement la première, et l'élément garde sa frontière.

  3. Les deux règles sont appliquées; aucune bordure n'est rendue.
    Cet élément a la classe red. Il est également précédé d'au moins un autre élément de la classe red. Ainsi, les deux règles sont appliquées et la deuxième borderdéclaration l'emporte sur la première, la "défaisant" pour ainsi dire.

En prime, bien qu'il ait été introduit dans Selectors 3, le combinateur général de frères et sœurs est en fait assez bien pris en charge par IE7 et plus récent, contrairement à :first-of-typeet :nth-of-type()qui ne sont pris en charge que par IE9. Si vous avez besoin d'une bonne prise en charge du navigateur, vous avez de la chance.

En fait, le fait que le combinateur frère soit le seul composant important dans cette technique, et qu'il ait une telle prise en charge du navigateur, rend cette technique très polyvalente - vous pouvez l'adapter pour filtrer les éléments par d'autres choses, en plus des sélecteurs de classe:

  • Vous pouvez l'utiliser pour contourner :first-of-typeIE7 et IE8, en fournissant simplement un sélecteur de type au lieu d'un sélecteur de classe (encore une fois, plus d'informations sur son utilisation incorrecte ici dans une section ultérieure):

    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 */
    }
  • Vous pouvez filtrer par sélecteurs d'attributs ou tout autre sélecteur simple au lieu de classes.

  • Vous pouvez également combiner cette technique prioritaire avec des pseudo-éléments même si les pseudo-éléments ne sont techniquement pas de simples sélecteurs.

Notez que pour que cela fonctionne, vous devrez savoir à l'avance quels seront les styles par défaut pour vos autres éléments frères afin de pouvoir remplacer la première règle. De plus, comme cela implique de remplacer les règles en CSS, vous ne pouvez pas obtenir la même chose avec un seul sélecteur à utiliser avec l' API Selectors ou les localisateurs CSS de Selenium .

Il convient de mentionner que Selectors 4 introduit une extension de la :nth-child()notation (à l'origine une pseudo-classe entièrement nouvelle appelée:nth-match() ), qui vous permettra d'utiliser quelque chose comme :nth-child(1 of .red)au lieu d'une hypothétique.red:first-of-class . Étant une proposition relativement récente, il n'y a pas encore suffisamment d'implémentations interopérables pour qu'elle soit utilisable sur les sites de production. Espérons que cela changera bientôt. En attendant, la solution de contournement que j'ai suggérée devrait fonctionner pour la plupart des cas.

Gardez à l'esprit que cette réponse suppose que la question recherche chaque premier élément enfant ayant une classe donnée. Il n'y a ni pseudo-classe ni même une solution CSS générique pour la nième correspondance d'un sélecteur complexe à travers le document entier - si une solution existe dépend fortement de la structure du document. jQuery fournit :eq(), :first, :lastet plus à cet effet, mais notez encore que ils fonctionnent très différemment :nth-child()et al . En utilisant l'API Selectors, vous pouvez soit utiliser document.querySelector()pour obtenir la toute première correspondance:

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

Ou utilisez document.querySelectorAll()avec un indexeur pour choisir une correspondance spécifique:

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

Bien que la .red:nth-of-type(1)solution dans la réponse initialement acceptée par les travaux de Philip Daubmeier (qui a été Martyn mais supprimée depuis), elle ne se comporte pas comme vous vous y attendriez.

Par exemple, si vous souhaitez uniquement sélectionner le pdans votre balisage d'origine:

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

... alors vous ne pouvez pas utiliser .red:first-of-type(équivalent à .red:nth-of-type(1)), car chaque élément est le premier (et le seul) de son type ( pet divrespectivement), donc deux seront mis en correspondance par le sélecteur.

Lorsque le premier élément d'une certaine classe est également le premier de son type , la pseudo-classe fonctionnera, mais cela ne se produit que par coïncidence . Ce comportement est démontré dans la réponse de Philip. Au moment où vous collerez un élément du même type avant cet élément, le sélecteur échouera. Prendre le balisage mis à jour:

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

L'application d'une règle avec .red:first-of-typefonctionnera, mais une fois que vous en aurez ajouté une autre psans la classe:

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

... le sélecteur échouera immédiatement, car le premier .redélément est maintenant le deuxième p élément.

BoltClock
la source
15
Alors, y a-t-il un moyen d'émuler :last-of-class? Sélectionnez le dernier élément d'une classe.
Rocket Hazmat
1
@Rocket: Pas que je puisse voir :(
BoltClock
4
Belle technique avec le frère. Il peut être utile de noter que cela fonctionne également avec plusieurs combinateurs frères et soeurs, par exemple p ~ p ~ p sélectionnera le troisième élément et au-delà: jsfiddle.net/zpnnvedm/1
Legolas
2
@BoltClock Oui, désolé, je suis venu ici pour une solution et je ne me souciais pas tellement du cas spécifique OP. En fait, je ne peux pas comprendre pourquoi cela general sibling selector ~fonctionne comme un :not(:first-of-*)pour un type spécifique, je suppose qu'il devrait appliquer la règle à chaque élément correspondant, mais il n'est appliqué que pour la première correspondance, même s'il y a 3 correspondances possibles ou plus.
Gerard Reches
2
Selon caniuse, l' :nth-child(1 of .foo)extension a déjà été implémentée sur Safari 9.1+. (Je n'ai pas vérifié cependant)
Danield
330

Le :first-childsélecteur est destiné, comme son nom l'indique, à sélectionner le premier enfant d'une balise parent. Les enfants doivent être intégrés dans la même balise parent. Votre exemple exact fonctionnera (venez de l'essayer ici ):

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

Peut-être que vous avez imbriqué vos tags dans différents tags parents? Vos balises de classe sont-elles redvraiment les premières balises sous le parent?

Notez également que cela ne s'applique pas uniquement à la première balise de ce type dans tout le document, mais à chaque fois qu'un nouveau parent est enroulé autour de lui, comme:

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

firstet thirdsera alors rouge.

Mise à jour:

Je ne sais pas pourquoi martyn a supprimé sa réponse, mais il avait la solution, le :nth-of-typesélecteur:

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

Crédits à Martyn . Plus d'infos par exemple ici . Sachez qu'il s'agit d'un sélecteur CSS 3, donc tous les navigateurs ne le reconnaîtront pas (par exemple IE8 ou plus ancien).

Philip Daubmeier
la source
C'était peut-être de ma faute - comme je l'avais fait remarquer à Martyn, un sélecteur CSS3 comme celui-ci :nth-of-typesouffre du léger problème de ne pas fonctionner du tout dans une version actuelle d'IE.
Már Örlygsson
25
Je suis devenu un peu confus en lisant ceci. Strictement .red:nth-of-type(1)sélectionnera tout élément qui (a) est le premier enfant de son type d'élément, et (b) a la classe "rouge". Donc, si dans l'exemple, le premier <p> n'avait pas la classe "rouge", il ne serait pas sélectionné. Alternativement, si le <span> et le premier <p> avaient tous les deux la classe "rouge", ils seraient tous les deux sélectionnés. jsfiddle.net/fvAxn
David
7
@Dan Mundy: :first-of-typeest équivalent à :nth-of-type(1), donc bien sûr cela fonctionne aussi. Il peut également échouer de la même manière pour la même raison que celle indiquée dans ma réponse.
BoltClock
4
@David Je suis sûr que :nth-of-typesignifie "élément / tag" quand il dit "type". Il ne prend donc en compte que l'élément, pas les choses comme les classes.
gcampbell
2
@gcampbell: Oui, c'est exactement ce que cela signifie, et c'est pourquoi cette réponse est erronée. La raison pour laquelle le mot "type" a été choisi est de ne pas coupler les sélecteurs avec HTML / XML, car toutes les langues n'ont pas un concept de "balises" qui définissent les éléments.
BoltClock
74

La bonne réponse est:

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

Partie I: Si l'élément est le premier à son parent et a la classe "rouge", il doit avoir une bordure.
Partie II: Si l'élément ".red" n'est pas le premier à son parent, mais suit immédiatement un élément sans classe ".red", il mérite également l'honneur de ladite bordure.

Violon ou ça n'est pas arrivé.

La réponse de Philip Daubmeier, bien qu'acceptée, n'est pas correcte - voir violon ci-joint.
La réponse de BoltClock fonctionnerait, mais définit et écrase inutilement les styles
(en particulier un problème où il hériterait autrement d'une bordure différente - vous ne voulez pas en déclarer d'autre à border: aucun)

EDIT: Dans le cas où vous avez "rouge" suivant plusieurs fois non rouge, chaque "premier" rouge obtiendra la bordure. Pour éviter cela, il faudrait utiliser la réponse de BoltClock. Voir violon

SamGoody
la source
2
Cette réponse n'est pas fausse, le sélecteur est correct pour le balisage donné, mais je dois répéter que la situation mentionnée dans la modification est la raison pour laquelle je déclare qu'un remplacement est nécessaire au moins lorsque vous ne pouvez pas garantir la structure du balisage - simplement parce qu'un .redsuit un :not(.red)ne fait pas toujours le premier .redparmi ses frères et sœurs. Et si la frontière doit être héritée, il s'agit simplement de déclarer border: inheritplutôt que border: nonedans la règle de remplacement.
BoltClock
3
Cette solution est la meilleure OMI car vous pouvez facilement faire de même pour le dernier enfant.
A1rPun
@ A1rPun Comment changez-vous cela pour le dernier enfant?
Willem
@Willem Passez simplement first-childà last-child, mais je vois après avoir testé que cela ne fait pas toujours l'affaire.
A1rPun
3
Je suis désolé, mais il ne correspond pas au "premier élément enfant qui a la classe .red", il correspond à la place au "premier élément s'il a la classe .red et le premier élément .red après un élément non .red". Ces deux ne sont pas équivalents. Violon: jsfiddle.net/Vq8PB/166
Gunchars
19

vous pourriez utiliser first-of-typeounth-of-type(1)

.red {
  color: green;  
}

/* .red:nth-of-type(1) */
.red:first-of-type {
  color: 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>

Nerdroid
la source
6
Cela ne fonctionnera pas si vous avez un <p></p>entre le spanet votre premier pélément avec la redclasse. Regardez ce JSFiddle .
Francisco Romero
1
J'ai vu maintenant qu'il reproduit dans le dernier exemple de sa réponse le même comportement que j'ai signalé ici. Quand j'ai essayé votre exemple et "joué" un peu, j'ai remarqué ce comportement et j'ai voulu le signaler pour que les futurs lecteurs soient au courant.
Francisco Romero
13

Les réponses ci-dessus sont trop complexes.

.class:first-of-type { }

Cela sélectionnera le premier type de classe. Source MDN

Gabriel Fair
la source
4
Je ne trouve aucune référence au premier type, et le premier de type ne s'applique qu'au nom de la balise; êtes-vous sûr que cette réponse est correcte?
Matt Broatch du
1
first-type a été renommé first-of-type
Gabriel Fair
7
Cela ne fonctionne pas avec les classes. Voilà comment cela devrait fonctionner, car la sélection du nième d'une classe serait plus utile que la sélection du nième d'une balise. Mais ': premier du type' n'a jamais fonctionné que par tagName. S'il se trouve que «premier de la classe» et «premier de la balise» font référence au même élément, ce qu'ils font souvent, il est facile de se tromper en pensant que cela fonctionne de cette façon; puis se demander ce qui est cassé quand on arrive à un cas où ils ne le font pas.
Evan Thompson
2
Je ne sais pas pourquoi ce n'est pas la réponse sélectionnée. Cela fait exactement ce que le PO demandait et fonctionne parfaitement. SMH.
Bernesto
1
@Bernesto, lorsque vous avez un cas où vous avez plusieurs éléments du même "type", disons <span>, et quelques-uns ont la classe highlight. Le .highlight:first-of-typesélecteur sélectionnera la première instance du "type" avec la classe sélectionnée, dans cet exemple la première <span>, et non la première instance de la classe highlight. Ce n'est que si cette première instance de span a également le surlignage de classe que le style sera implémenté. ( codepen.io/andrewRmillar/pen/poJKJdJ )
Sl4rtib4rtf4st
9

Pour correspondre à votre sélecteur, l'élément doit avoir un nom de classe redet doit être le premier enfant de son parent.

<div>
    <span class="red"> <!-- MATCH -->
</div>

<div>
    <span>Blah</span>
    <p class="red"> <!-- NO MATCH -->
</div>

<div>
    <span>Blah</span>
    <div><p class="red"></div> <!-- MATCH -->
</div>
Chetan Sastry
la source
c'est correct et cela semble être mon problème, mais existe-t-il un moyen de sélectionner le premier élément avec une classe, qu'il soit précédé ou non par d'autres éléments?
Rajat
Si vous aviez regardé la réponse de Martyn, vous n'aviez pas à demander ceci :)
Philip Daubmeier
9

Étant donné que les autres réponses couvrent ce qui ne va pas, je vais essayer l'autre moitié, comment y remédier. Malheureusement, je ne sais pas si vous avez une seule solution CSS ici, du moins pas à laquelle je peux penser . Il existe cependant d'autres options ...

  1. Attribuez une firstclasse à l'élément lorsque vous le générez, comme ceci:

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

    CSS:

    .first.red {
      border:5px solid red;
    }

    Ce CSS ne fait correspondre les éléments qu'aux deux first et aux redclasses.

  2. Alternativement, faites la même chose en JavaScript, par exemple, voici ce que jQuery vous utiliseriez pour cela, en utilisant le même CSS que ci-dessus:

    $(".red:first").addClass("first");
Nick Craver
la source
J'ai proposé une solution CSS uniquement il y a quelque temps ; Je l'ai reproduit ici comme une réponse canonique.
BoltClock
1
Tant qu'il n'y a rien de tel :first-of-class, je suggère également d'ajouter une classe supplémentaire au premier élément. Semble la solution la plus simple pour l'instant.
Kai Noack du
7

J'ai eu celui-ci dans mon projet.

div > .b ~ .b:not(:first-child) {
	background: none;
}
div > .b {
    background: red;
}
<div>
      <p class="a">The first paragraph.</p>
      <p class="a">The second paragraph.</p>
      <p class="b">The third paragraph.</p>
      <p class="b">The fourth paragraph.</p>
  </div>

Yener Örer
la source
5

Selon votre problème mis à jour

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

que diriez-vous

.home span + .red{
      border:1px solid red;
    }

Cela sélectionnera la classe home , puis la durée de l'élément et enfin tous les éléments .red qui sont placés immédiatement après les éléments de la portée.

Référence: http://www.w3schools.com/cssref/css_selectors.asp

StinkyCat
la source
3

Vous pouvez utiliser nth-of-type (1) mais assurez-vous que le site n'a pas besoin de prendre en charge IE7, etc., si tel est le cas, utilisez jQuery pour ajouter la classe de corps, puis recherchez l'élément via la classe de corps IE7, puis le nom de l'élément, puis ajoutez dans le style nième enfant.

Jamie Paterson
la source
3

Vous pouvez changer votre code en quelque chose comme ça pour le faire fonctionner

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

Cela fait le travail pour vous

.home span + .red{
      border:3px solid green;
    }

Voici une référence CSS de SnoopCode à ce sujet.

Prabhakar Undurthi
la source
3

J'utilise ci-dessous CSS pour avoir une image de fond pour la liste ul li

#footer .module:nth-of-type(1)>.menu>li:nth-of-type(1){
  background-position: center;
  background-image: url(http://monagentvoyagessuperprix.j3.voyagesendirect.com/images/stories/images_monagentvoyagessuperprix/layout/icon-home.png);
  background-repeat: no-repeat;
}
<footer id="footer">
  <div class="module">
    <ul class="menu ">
      <li class="level1 item308 active current"></li>
      <li> </li>
    </ul> 
  </div>
  <div class="module">
    <ul class="menu "><li></li>
      <li></li> 
    </ul>
  </div>
  <div class="module">
    <ul class="menu ">
      <li></li>
      <li></li>
    </ul>
  </div>
</footer>

li bing zhao
la source
2

Essaye ça :

    .class_name > *:first-child {
        border: 1px solid red;
    }
Loy
la source
2

Pour une raison quelconque, aucune des réponses ci-dessus ne semble aborder le cas du véritable premier et seul premier enfant du parent.

#element_id > .class_name:first-child

Toutes les réponses ci-dessus échoueront si vous souhaitez appliquer le style uniquement à l'enfant de première classe dans ce code.

<aside id="element_id">
  Content
  <div class="class_name">First content that need to be styled</div>
  <div class="class_name">
    Second content that don't need to be styled
    <div>
      <div>
        <div class="class_name">deep content - no style</div>
        <div class="class_name">deep content - no style</div>
        <div>
          <div class="class_name">deep content - no style</div>
        </div>
      </div>
    </div>
  </div>
</aside>
Yavor Ivanov
la source
1
Votre réponse est essentiellement déjà contenue dans les deux premières réponses les plus élevées à cette question et n'ajoute rien. De plus, votre utilisation de la :first-childpseudo-classe est trompeuse car elle ajoute .class_nameavant de ne pas changer la façon dont elle fonctionne et la fait simplement agir comme un filtre. Si vous avez déjà un div, <div class="class_name">First content that need to be styled</div>votre sélecteur ne correspondra pas, car ce n'est pas le premier enfant.
j08691
Je ne pense pas que ce soit aussi simple pour quelqu'un qui a besoin d'informations. Et oui, vous avez raison sur l'affaire. Cela sert un cas plus spécifique. C'est pourquoi j'ai pensé que cela pourrait être utile.
Yavor Ivanov
C'est exactement ce dont j'avais besoin, précis et concis. Merci @YavorIvanov
quantme
1

Essayez ceci simple et efficace

 .home > span + .red{
      border:1px solid red;
    }
Ami
la source
-1

Je crois que l'utilisation du sélecteur relatif + pour sélectionner les éléments placés immédiatement après fonctionne le mieux ici (comme peu l'ont suggéré auparavant).

Il est également possible pour ce cas d'utiliser ce sélecteur

.home p:first-of-type

mais ce n'est pas le sélecteur d'élément.

Ici, vous avez une belle liste de sélecteurs CSS: https://kolosek.com/css-selectors/

Nesha Zoric
la source
-2

Essayez cette solution:

 .home p:first-of-type {
  border:5px solid red;
  width:100%;
  display:block;
}
<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>

Lien CodePen

Santosh Khalse
la source
La question est " Sélecteur CSS pour le premier élément avec la classe ", pas " Sélecteur CSS pour le premier élément avec le nom de la balise ".
GeroldBroser réintègre Monica