Selector CSS para el primer elemento con clase.

Resuelto Rajat asked hace 14 años • 25 respuestas

Tengo un montón de elementos con un nombre de clase red, pero parece que no puedo seleccionar el primer elemento usando class="red"la siguiente regla 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>
Expandir fragmento

¿Qué hay de malo en este selector y cómo lo corrijo para apuntar al primer niño con clase red?

Rajat avatar Apr 27 '10 05:04 Rajat
Aceptado

Este es uno de los ejemplos más conocidos de autores que no entienden cómo :first-childfunciona. Introducida en CSS2 , la :first-childpseudoclase representa el primer hijo de su padre . Eso es todo. Existe una idea errónea muy común de que selecciona el elemento secundario 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 (consulte aquí para obtener 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 analiza 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 existe :first-of-classuna pseudoclase similar para hacer coincidir el primer elemento secundario de una clase determinada. En el momento en que se publicó esta respuesta por primera vez, el FPWD recientemente publicado de Selectores nivel 4 introdujo una :nth-match()pseudoclase , diseñada en torno a la mecánica de selector existente, como mencioné en el primer párrafo, agregando un argumento de lista de selectores, a través del cual puede proporcionar el resto. del selector compuesto para obtener el comportamiento de filtrado deseado. En los últimos años, esta funcionalidad se incluyó en :nth-child()sí misma , con la lista de selección apareciendo como un segundo argumento opcional, para simplificar las cosas y evitar la falsa impresión de que :nth-match()coincidía en todo el documento (consulte la nota final a continuación).

Mientras esperamos la compatibilidad con varios navegadores (en serio, han pasado casi 10 años y solo ha habido una implementación durante los últimos 5 de esos años), una solución alternativa que Lea Verou y yo desarrollamos de forma independiente (¡ella lo hizo primero!) es para aplicar primero los estilos deseados a todos los 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 de los elementos con la clase que viene después del primero , usando el combinador de hermanos general~ 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 sólo el primer elemento con class="red"tendrá un borde.

A continuación se muestra una ilustración de cómo se aplican las reglas:

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

  1. No se aplican reglas; no se representa ningún borde.
    Este elemento no tiene la clase red, por lo que se omite.

  2. Sólo 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 padre. Así no se aplica la segunda regla, sólo la primera, y el elemento mantiene su frontera.

  3. Se aplican ambas reglas; 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. Así, se aplican ambas reglas y la segunda borderdeclaración anula la primera, "deshaciéndola", por así decirlo.

Como beneficio adicional, aunque se introdujo en Selectors 3, el combinador de hermanos general en realidad es bastante compatible con IE7 y versiones posteriores, a diferencia :first-of-typede :nth-of-type()los que solo son compatibles con IE9 y posteriores. Si necesitas un buen soporte para tu navegador, estás de suerte.

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

  • Puede usar esto para solucionarlo :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 en la pregunta de 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 */
     }
    
  • Puede 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 técnicamente los pseudoelementos 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 se puede lograr lo mismo con un solo selector para usar con Selectors API o los localizadores CSS de Selenium .

En una nota final, tenga en cuenta que esta respuesta supone que la pregunta busca cualquier número de primeros elementos secundarios que tengan una clase determinada. No existe una pseudoclase ni siquiera una solución CSS genérica para la enésima coincidencia de un selector complejo en todo el documento ; la existencia de una solución depende en gran medida de la estructura del documento. jQuery proporciona :eq(), :firsty :lastmás para este propósito, pero tenga en cuenta nuevamente que funcionan de manera muy diferente a :nth-child()et al . Usando la API de Selectores, puedes usarla document.querySelector()para obtener la primera coincidencia:

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

O utilícelo document.querySelectorAll()con un indexador para seleccionar cualquier 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 escrita originalmente por Martyn pero eliminada desde entonces), no se comporta de la manera esperada.

Por ejemplo, si solo desea seleccionar paquí:

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

... entonces no puedes 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 el selector hará coincidir ambos .

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

<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 agregues 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 avatar Dec 16 '2011 19:12 BoltClock

El :first-childselector está destinado, como su nombre lo indica, a seleccionar el primer hijo de una etiqueta principal. Entonces este ejemplo funcionará (lo probé aquí ):

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

Sin embargo, esto no funcionará si ha anidado sus etiquetas bajo diferentes etiquetas principales, o si sus etiquetas de clase redno son las primeras etiquetas bajo la principal.

Tenga en cuenta también que esto no solo se aplica a la primera etiqueta de este tipo en todo el documento, sino cada vez que se coloca una nueva etiqueta principal alrededor de ella, 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.

Para tu caso, puedes utilizar 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>
Expandir fragmento

Créditos a Martyn , quien eliminó su respuesta que contenía este enfoque.

Más información sobre :nth-child()y :nth-of-type()está disponible en http://www.quirksmode.org/css/nthchild.html .

Tenga en cuenta que este es un selector CSS3, por lo tanto, algunas versiones de navegador ahora obsoletas pueden no comportarse como se esperaba (por ejemplo, IE8 o anteriores). Visite https://caniuse.com/?search=nth-of-type para obtener más detalles.

Philip Daubmeier avatar Apr 26 '2010 22:04 Philip Daubmeier

La respuesta correcta es:

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

Parte I: si el elemento es el primero en su padre y tiene la clase "rojo", obtendrá borde.
Parte II: Si el elemento ".red" no es el primero de su padre, sino que está inmediatamente después de un elemento sin clase ".red", también merecerá el honor de dicho borde.

Violín o no sucedió.

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

EDITAR: En el caso de que tenga "rojo" después de no rojo varias veces, cada "primer" rojo obtendrá el borde. Para evitarlo, sería necesario utilizar la respuesta de BoltClock. Ver violín

SamGoody avatar Nov 08 '2012 11:11 SamGoody