Selector CSS para el primer elemento con clase.
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>
¿Qué hay de malo en este selector y cómo lo corrijo para apuntar al primer niño con clase red
?
Este es uno de los ejemplos más conocidos de autores que no entienden cómo :first-child
funciona. Introducida en CSS2 , la :first-child
pseudoclase 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-type
pseudoclase , que representa el primer elemento entre hermanos de su tipo de elemento. Esta respuesta explica, con ilustraciones, la diferencia entre :first-child
y :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-class
una 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>
No se aplican reglas; no se representa ningún borde.
Este elemento no tiene la clasered
, por lo que se omite.Sólo se aplica la primera regla; se representa un borde rojo.
Este elemento tiene la clasered
, pero no está precedido por ningún elemento con la clasered
en su padre. Así no se aplica la segunda regla, sólo la primera, y el elemento mantiene su frontera.Se aplican ambas reglas; no se representa ningún borde.
Este elemento tiene la clasered
. También está precedido por al menos otro elemento con la clasered
. Así, se aplican ambas reglas y la segundaborder
declaració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-type
de :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-type
en 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()
, :first
y :last
má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 p
aquí:
<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 ( p
y div
respectivamente), 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-type
funcionará, pero una vez que agregues otra p
sin 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 .red
elemento ahora es el segundo p
elemento.
El :first-child
selector 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 red
no 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>
first
y third
será rojo entonces.
Para tu caso, puedes utilizar el :nth-of-type
selector:
.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 , 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.
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