Selector CSS para el primer elemento con clase

947

Tengo un montón de elementos con un nombre de clase red, pero parece que no puedo seleccionar el primer elemento class="red"usando la siguiente regla CSS:

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

¿Qué hay de malo en este selector y cómo lo corrijo?

Gracias a los comentarios, descubrí que el elemento tiene que ser el primer hijo de su padre para ser seleccionado, que no es el caso que tengo. Tengo la siguiente estructura, y esta regla falla como se menciona en los comentarios:

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

¿Cómo puedo apuntar al primer niño con clase red?

Rajat
fuente
para aquellos que se están confundiendo debido a diferentes tipos de elementos en mi ejemplo (p, div), hacerlos iguales tampoco funciona y el problema aún existe.
Rajat
44
Creo que así es como debería haber funcionado el selector de primer hijo ...
Felix Eve
28
: primer hijo no sería un muy buen nombre entonces. Si tuviera un hijo y luego una hija, no llamaría a su hija su primogénito. Del mismo modo, el primer .home> .red no es el primer hijo de .home, por lo que sería inapropiado llamarlo como tal.
BoltClock
No, es como: tipo primero de debería haber funcionado
Evan Thompson
@EvanThompson Así es como :first-of-type lo hace el trabajo.
TylerH

Respuestas:

1427

Este es uno de los ejemplos más conocidos de autores que no entienden cómo :first-childfunciona. Introducido en CSS2 , la :first-childpseudoclase representa el primer hijo de su padre . Eso es. Existe una idea errónea muy común de que recoge cualquier elemento hijo que sea el primero en coincidir con las condiciones especificadas por el resto del selector compuesto. Debido a la forma en que funcionan los selectores (ver aquí una explicación), eso simplemente no es cierto.

El nivel 3 de selectores introduce una :first-of-typepseudoclase , que representa el primer elemento entre hermanos de su tipo de elemento. Esta respuesta explica, con ilustraciones, la diferencia entre :first-childy :first-of-type. Sin embargo, al igual que con :first-child, no considera otras condiciones o atributos. En HTML, el tipo de elemento está representado por el nombre de la etiqueta. En la pregunta, ese tipo es p.

Desafortunadamente, no hay una :first-of-classpseudoclase similar para hacer coincidir el primer elemento hijo de una clase dada. Una solución que Lea Verou y yo se nos ocurrió para esto (aunque de forma totalmente independiente) es aplicar primero los estilos deseados a todos sus elementos con esa clase:

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

... luego "deshacer" los estilos para los elementos con la clase que viene después de la primera , usando el combinador general de hermanos~ en una regla primordial:

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

Ahora solo el primer elemento con class="red" tendrá un borde.

Aquí hay una ilustración de cómo se aplican las reglas:

<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. No se aplican reglas; No se representa ningún borde.
    Este elemento no tiene la clase red, por lo que se omite.

  2. Solo se aplica la primera regla; se representa un borde rojo.
    Este elemento tiene la clase red, pero no está precedido por ningún elemento con la clase reden su elemento primario. Por lo tanto, la segunda regla no se aplica, solo la primera, y el elemento mantiene su borde.

  3. Ambas reglas se aplican; No se representa ningún borde.
    Este elemento tiene la clase red. También está precedido por al menos otro elemento con la clase red. Por lo tanto, se aplican ambas reglas, y la segunda borderdeclaración anula la primera, "deshaciéndola", por así decirlo.

Como beneficio adicional, aunque se introdujo en Selectores 3, el combinador de hermanos general está bastante bien soportado por IE7 y más reciente, a diferencia :first-of-typey :nth-of-type()solo soportado por IE9 en adelante. Si necesita un buen soporte para el navegador, tiene suerte.

De hecho, el hecho de que el combinador de hermanos es el único componente importante en esta técnica, y tiene un soporte de navegador tan sorprendente, hace que esta técnica sea muy versátil: puede adaptarlo para filtrar elementos por otras cosas, además de los selectores de clase:

  • Puede usar esto para evitar :first-of-typeen IE7 e IE8, simplemente proporcionando un selector de tipo en lugar de un selector de clase (nuevamente, más sobre su uso incorrecto aquí en una sección posterior):

    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 */
    }
  • Puedes filtrar por selectores de atributos o cualquier otro selector simple en lugar de clases.

  • También puede combinar esta técnica primordial con pseudoelementos aunque los pseudoelementos técnicamente no son simples selectores.

Tenga en cuenta que para que esto funcione, necesitará saber de antemano cuáles serán los estilos predeterminados para sus otros elementos hermanos para poder anular la primera regla. Además, dado que esto implica anular reglas en CSS, no puede lograr lo mismo con un solo selector para usar con la API de selectores o Selenium los localizadores CSS de .

Vale la pena mencionar que Selectores 4 introduce una extensión de la :nth-child()notación (originalmente una pseudoclase completamente nueva llamada :nth-match()), que le permitirá usar algo como :nth-child(1 of .red)en lugar de una hipótesis.red:first-of-class . Al ser una propuesta relativamente reciente, todavía no hay suficientes implementaciones interoperables para que pueda utilizarse en los sitios de producción. Esperemos que esto cambie pronto. Mientras tanto, la solución alternativa que he sugerido debería funcionar para la mayoría de los casos.

Tenga en cuenta que esta respuesta supone que la pregunta busca cada primer elemento secundario que tenga una clase determinada. No hay una pseudoclase ni una solución genérica de CSS para la enésima combinación de un selector complejo en todo el documento : si existe una solución depende en gran medida de la estructura del documento. jQuery proporciona :eq(), :first, :lasty más para este propósito, pero de nuevo nota que funcionan de manera muy diferente a partir de :nth-child()et al . Usando la API de selectores, puede usar document.querySelector()para obtener la primera coincidencia:

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

O use document.querySelectorAll()con un indexador para elegir una coincidencia específica:

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

Aunque la .red:nth-of-type(1)solución en la respuesta original aceptada por Philip Daubmeier funciona (que fue originalmente escrita por Martyn pero eliminada desde entonces), no se comporta de la manera que esperarías.

Por ejemplo, si solo deseaba seleccionar el pen su marcado original:

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

... entonces no puede usar .red:first-of-type(equivalente a .red:nth-of-type(1)), porque cada elemento es el primero (y único) de su tipo ( py divrespectivamente), por lo que ambos lo que el selector hará coincidir .

Cuando el primer elemento de una determinada clase es también el primero de su tipo , la pseudoclase funcionará, pero esto solo sucede por coincidencia . Este comportamiento se demuestra en la respuesta de Philip. En el momento en que pegue un elemento del mismo tipo antes de este elemento, el selector fallará. Tomando el marcado actualizado:

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

Aplicar una regla con .red:first-of-typefuncionará, pero una vez que agregue otra psin la clase:

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

... el selector fallará inmediatamente, porque el primer .redelemento ahora es el segundo p elemento.

BoltClock
fuente
15
Entonces, ¿hay alguna forma de emular :last-of-class? Seleccione el último elemento de una clase.
Rocket Hazmat
1
@Rocket: No es que pueda ver :(
BoltClock
44
Buena técnica con el hermano. Vale la pena señalar que esto también funciona con múltiples combinadores de hermanos, por ejemplo, p ~ p ~ p seleccionará el tercer elemento y más allá: jsfiddle.net/zpnnvedm/1
Legolas
2
@BoltClock Sí, lo siento, vine aquí por una solución y no me importó mucho el caso específico de OP. En realidad, no puedo entender por qué general sibling selector ~funciona como un :not(:first-of-*)para un tipo específico, supongo que debe aplicar la regla a cada elemento coincidente, pero se aplica solo para la primera coincidencia, incluso cuando hay 3 o más coincidencias posibles.
Gerard Reches
2
Según caniuse, la :nth-child(1 of .foo)extensión ya se ha implementado en Safari 9.1+. (No he comprobado sin embargo)
Danield
330

El :first-childselector está destinado, como su nombre lo indica, para seleccionar el primer elemento secundario de una etiqueta principal. Los hijos deben estar incrustados en la misma etiqueta principal. Su ejemplo exacto funcionará (solo lo probé aquí ):

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

¿Quizás haya anidado sus etiquetas en diferentes etiquetas principales? ¿Son sus etiquetas de clase redrealmente las primeras etiquetas debajo del padre?

Tenga en cuenta también que esto no solo se aplica a la primera etiqueta de este tipo en todo el documento, pero cada vez que se envuelve un nuevo padre, como:

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

firsty thirdserá rojo entonces.

Actualizar:

No sé por qué Martin eliminó su respuesta, pero tenía la solución, el :nth-of-typeselector:

.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éditos a Martyn . Más información, por ejemplo, aquí . Tenga en cuenta que este es un selector CSS 3, por lo tanto, no todos los navegadores lo reconocerán (por ejemplo, IE8 o anterior).

Philip Daubmeier
fuente
Pudo haber sido mi culpa, como le señalé a Martyn que un selector CSS3 tiene :nth-of-typeel pequeño problema de no funcionar en ninguna versión actual de IE.
Már Örlygsson
25
Me confundí un poco al leer esto. Estrictamente .red:nth-of-type(1)seleccionará cualquier elemento que (a) sea el primer hijo de su tipo de elemento, y (b) tenga la clase "rojo". Entonces, si en el ejemplo, el primer <p> no tuviera la clase "rojo", no se seleccionaría. Alternativamente, si el <span> y el primer <p> ambos tenían la clase "rojo", ambos serían seleccionados. jsfiddle.net/fvAxn
David
77
@Dan Mundy: :first-of-typees equivalente a :nth-of-type(1), por lo que, por supuesto, también funciona. También puede fallar, de la misma manera, por la misma razón establecida en mi respuesta.
BoltClock
44
@David Estoy seguro que :nth-of-typesignifica "elemento / etiqueta" cuando dice "tipo". Por lo tanto, solo considera el elemento, no cosas como las clases.
gcampbell
2
@gcampbell: Sí, eso es exactamente lo que significa, y es por eso que esta respuesta es errónea. La razón por la que se eligió la palabra "tipo" es para no acoplar selectores con HTML / XML, ya que no todos los lenguajes tienen un concepto de "etiquetas" que definen elementos.
BoltClock
74

La respuesta correcta es:

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

Parte I: Si el elemento es primero a su padre y tiene la clase "rojo", obtendrá un borde.
Parte II: Si el elemento ".red" no es el primero de su padre, sino que sigue inmediatamente a un elemento sin la clase ".red", también merece el honor de dicho borde.

Violín o no sucedió.

La respuesta de Philip Daubmeier, aunque aceptada, no es correcta; vea el violín adjunto.
La respuesta de BoltClock funcionaría, pero define y sobrescribe estilos innecesariamente
(particularmente un problema donde de lo contrario heredaría un borde diferente; no desea declarar otro al borde: ninguno)

EDITAR: en el caso de que tenga "rojo" después de no rojo varias veces, cada "primero" rojo obtendrá el borde. Para evitar eso, uno necesitaría usar la respuesta de BoltClock. Ver violín

SamGoody
fuente
2
Esta respuesta no es incorrecta, el selector es correcto para el marcado dado, pero debo reiterar que la situación mencionada en la edición es la razón por la que afirmo que una anulación es necesaria al menos cuando no puede garantizar la estructura de marcado, solo porque .redSigue a un :not(.red)no siempre lo hace el primero .redentre sus hermanos. Y si el borde necesita ser heredado, es simplemente una cuestión de declarar en border: inheritlugar de border: noneen la regla de anulación.
BoltClock
3
Esta solución es la mejor OMI porque puedes hacer lo mismo fácilmente para el último hijo.
A1rPun
@ A1rPun ¿Cómo se cambia esto para el último hijo?
Willem
@Willem Simplemente cambie first-childa last-child, pero veo después de probar que esto no siempre funciona.
A1rPun
3
Lo siento, pero no coincide con el "primer elemento secundario que tiene clase .red", sino que coincide con el "primer elemento si tiene clase .red y el primer elemento .red después de un elemento no .red". Estos dos no son equivalentes. Fiddle: jsfiddle.net/Vq8PB/166
Gunchars
19

podrías usar first-of-typeonth-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
fuente
66
No funcionará si tiene un elemento <p></p>intermedio entre spany su primer pelemento con redclase. Mira este JSFiddle .
Francisco Romero
1
Vi ahora que reproduce en el último ejemplo de su respuesta el mismo comportamiento que señalé aquí. Cuando probé su ejemplo y "jugué" un poco, noté ese comportamiento y quería señalarlo para que los futuros lectores lo sepan.
Francisco Romero
13

Las respuestas anteriores son demasiado complejas.

.class:first-of-type { }

Esto seleccionará el primer tipo de clase. Fuente MDN

Gabriel Fair
fuente
44
No puedo encontrar ninguna referencia al primer tipo, y el primero de tipo solo se aplica al nombre de la etiqueta; ¿Estás seguro de que esta respuesta es correcta?
Matt Broatch
1
first-type se ha cambiado el nombre a first-of-type
Gabriel Feria
77
No funciona con clases. Así es como debería funcionar, porque seleccionar el enésimo de una clase sería más útil que seleccionar el enésimo de una etiqueta. Pero ': first-of-type' solo ha funcionado con tagName. Si sucede que 'primero en su clase' y 'primero en etiqueta' se refieren al mismo elemento, lo que a menudo hacen, es fácil confundirse y pensar que funciona de esa manera; y luego se pregunta qué está roto cuando llegas a un caso en el que no lo hacen.
Evan Thompson
2
No estoy seguro de por qué esta no es la respuesta seleccionada. Esto hace exactamente lo que pedía el OP, y funciona perfectamente. SMH.
Bernesto
1
@ Bernesto, cuando tienes un caso en el que tienes múltiples elementos del mismo "tipo", digamos <span>, y algunos tienen la clase highlight. El .highlight:first-of-typeselector seleccionará la primera instancia del "tipo" con la clase seleccionada, en este ejemplo la primera <span>, y no la primera instancia de la clase highlight. Solo si esa primera instancia de span también tiene el resaltado de clase, se implementará el estilo. ( codepen.io/andrewRmillar/pen/poJKJdJ )
Sl4rtib4rtf4st
9

Para que coincida con su selector, el elemento debe tener un nombre de clase redy debe ser el primer hijo de su padre.

<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
fuente
esto es correcto y este parece ser mi problema, pero ¿hay alguna manera de seleccionar el primer elemento con una clase independientemente de si está precedido por otros elementos?
Rajat
Si hubiera mirado la respuesta de Martyns, no habría tenido que preguntar esto :)
Philip Daubmeier
9

Como las otras respuestas cubren lo que está mal , intentaré la otra mitad, cómo solucionarlo. Desafortunadamente, no sé si tienes una solución de CSS aquí, al menos no que se me ocurra . Sin embargo, hay otras opciones ...

  1. Asigne una firstclase al elemento cuando lo genere, así:

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

    CSS:

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

    Este CSS solo coincide con elementos con ambos first y redclases.

  2. Alternativamente, haga lo mismo en JavaScript, por ejemplo, esto es lo que jQuery usaría para hacer esto, usando el mismo CSS que el anterior:

    $(".red:first").addClass("first");
Nick Craver
fuente
Se me ocurrió una solución solo para CSS hace un tiempo ; Lo he reproducido aquí como una respuesta canónica.
BoltClock
1
Mientras no haya nada parecido :first-of-class, también sugeriría agregar una clase adicional al primer elemento. Parece la solución más fácil por ahora.
Kai Noack
7

Tengo este en mi proyecto.

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

De acuerdo a su problema actualizado

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

qué tal si

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

Esto seleccionará la clase de inicio , luego el elemento span y finalmente todos los elementos .red que se colocan inmediatamente después de los elementos span.

Referencia: http://www.w3schools.com/cssref/css_selectors.asp

StinkyCat
fuente
3

Puede usar nth-of-type (1) pero asegúrese de que el sitio no necesite admitir IE7, etc., si este es el caso, use jQuery para agregar la clase de cuerpo, luego encuentre el elemento a través de la clase de cuerpo de IE7 y luego el nombre del elemento, luego agregue en el enésimo estilo infantil.

Jamie Paterson
fuente
3

Puede cambiar su código a algo como esto para que funcione

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

Esto hace el trabajo por ti

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

Aquí hay una referencia CSS de SnoopCode sobre eso.

Prabhakar Undurthi
fuente
3

Estoy usando CSS a continuación para tener una imagen de fondo para la lista 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
fuente
2

Prueba esto :

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

Por alguna razón, ninguna de las respuestas anteriores parecía abordar el caso del primer y único primer hijo del padre.

#element_id > .class_name:first-child

Todas las respuestas anteriores fallarán si desea aplicar el estilo solo al hijo de primera clase dentro de este código.

<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
fuente
1
Esencialmente, su respuesta ya está contenida en las dos respuestas más altas a esta pregunta y no agrega nada. Además, su uso de la :first-childpseudoclase es engañoso, ya que agregarlo .class_nameantes de que no cambie la forma en que funciona y simplemente lo hace actuar como un filtro. Si tiene un div antes, <div class="class_name">First content that need to be styled</div>entonces su selector no coincidiría, ya que no es el primer hijo real.
j08691
No creo que sea tan sencillo para alguien que necesita la información. Y sí, tienes razón sobre el caso. Esto sirve un caso más específico. Por eso pensé que podría ser útil.
Yavor Ivanov
Esto es exactamente lo que necesitaba, preciso y conciso. Gracias @YavorIvanov
cantidad
1

Prueba esto simple y efectivo

 .home > span + .red{
      border:1px solid red;
    }
Amigo
fuente
-1

Creo que usando el selector relativo + para seleccionar elementos colocados inmediatamente después, funciona mejor aquí (como pocos sugirieron antes).

También es posible para este caso usar este selector

.home p:first-of-type

pero este es el selector de elementos, no el de clase uno.

Aquí tiene una buena lista de selectores CSS: https://kolosek.com/css-selectors/

Nesha Zoric
fuente
-2

Prueba esta solución:

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

Enlace CodePen

Santosh Khalse
fuente
La pregunta es " selector CSS para el primer elemento con clase ", no " selector CSS para el primer elemento con nombre de etiqueta ".
GeroldBroser reintegra a Mónica el