Selettori CSS avanzati: nth-child, nth-of-type e :has()
:has() come selettore padre, :nth-child con selettore, :not() complesso, casi d'uso pratici
Nella pillola sui selettori di base abbiamo visto tipo, classe, id e i combinatori. Qui saliamo di livello: quattro strumenti che permettono di scegliere elementi per posizione, per contenuto e per esclusione: cose impossibili (o scomodissime) con i selettori semplici.
I filtri avanzati li possiamo associare a quando, in un negozio di abbigliamento, chiediamo al comesso: «vorrei provare la terza maglietta da sinistra», «vorrei vedere le t-shirt in offerta», «voglio provare tutti i pantaloni con taglia 48».
1. :nth-child(): scegliere per posizione
:nth-child() seleziona un elemento in base alla sua posizione tra tutti i fratelli, a prescindere dal tipo di tag. Accetta parole chiave (odd, even) o la formula An+B, dove A è il passo e B lo scarto.
tr:nth-child(odd) {
background: #f4f2f8;
} /* righe dispari, effetto zebra */
li:nth-child(3n) {
color: purple;
} /* ogni terzo elemento: 3, 6, 9... */
li:nth-child(3n+1) {
font-weight: bold;
} /* inizio di ogni gruppo: 1, 4, 7... */
Attenzione: conta tutti i fratelli, non solo quelli dello stesso tag. p:nth-child(2) seleziona un <p> solo se è proprio lui il secondo figlio: se il secondo figlio è un <h2>, non scatta nulla.
Fonte: MDN Web Docs: «:nth-child()»: conta gli indici tra tutti i figli del genitore.
2. :nth-of-type(): scegliere per posizione E tipo
:nth-of-type() funziona come :nth-child(), ma conta solo tra i fratelli dello stesso tag. È la scelta giusta quando ti interessa «il secondo paragrafo», non «il secondo figlio, qualunque cosa sia».
<style>
p:nth-child(2) { } /* NON scatta: il 2 figlio e' il primo <p>, non il secondo */
p:nth-of-type(2) { } /* scatta: e' il 2 <p> tra i <p> */
</style>
<h2>Titolo</h2>
<p>Primo paragrafo</p>
<p>Secondo paragrafo</p>
nth-child è come chiamare «il terzo in fila», chiunque sia. nth-of-type è chiamare «il terzo dei camerieri in fila», ignorando chi non è cameriere. Stesso posto, criterio diverso.
Fonte: MDN Web Docs: «:nth-child()» / «:nth-of-type()»: la differenza è cosa viene contato.
3. :nth-child() con selettore: il meglio dei due mondi
Dal 2023 (CSS Selectors Level 4), :nth-child() accetta anche un secondo argomento con la sintassi of <selettore>: prima filtra gli elementi che corrispondono al selettore, poi applica la posizione.
/* i primi 3 elementi della lista che hanno anche classe .important */
li:nth-child(-n + 3 of .important) {
border: 2px solid orange;
}
/* righe pari, ma solo tra quelle NON nascoste */
tr:nth-child(even of :not([hidden])) {
background: lightyellow;
}
Utilissimo con righe di tabella nascoste dinamicamente (filtri, ricerche): senza of, :nth-child(even) continuerebbe a contare anche le righe con hidden, sfalsando lo zebra striping visivo.
4. :has(): il selettore «padre» che mancava
Per vent'anni CSS non ha mai potuto selezionare un elemento in base a cosa contiene: solo il contrario (figlio in base al genitore). :has() chiude quel buco: rappresenta un elemento se al suo interno (o accanto) trova una corrispondenza.
/* la card SOLO se contiene un'immagine */
article:has(img) {
border: 1px solid #ccc;
}
/* l'h1 SOLO se e' subito seguito da un h2 (selettore fratello precedente) */
h1:has(+ h2) {
margin-bottom: 0;
}
/* il form-group SOLO se l'input al suo interno e' invalido */
.form-group:has(input:invalid) {
outline: 2px solid crimson;
}
:has() è più di un «selettore padre»: MDN lo definisce relazionale: con il combinatore + diventa perfino un selettore per il fratello precedente, altra cosa impossibile prima. Nota: prende la specificità del suo argomento più specifico, e non si può annidare dentro un altro :has().
5. :not() complesso: escludere con più criteri
:not() storicamente accettava un solo selettore semplice. Oggi, con Selectors Level 4, accetta una lista di selettori, anche complessi, separati da virgola.
/* esclude sia .draft sia .archived in un colpo solo */
article:not(.draft, .archived) {
display: block;
}
/* combinato con :has(): card senza badge e senza immagine */
.card:not(:has(.badge)):not(:has(img)) {
opacity: 0.6;
}
Fonte: MDN Web Docs: «:not()»: accetta una lista di selettori complessi (complex-selector-list).
In sintesi
| Selettore | Cosa fa | Esempio |
|---|---|---|
:nth-child() |
Posizione tra tutti i fratelli | li:nth-child(3n) |
:nth-of-type() |
Posizione tra fratelli dello stesso tag | p:nth-of-type(2) |
:nth-child(of S) |
Posizione dopo aver filtrato per selettore | li:nth-child(-n+3 of .important) |
:has() |
Elemento che contiene (o precede) un match | article:has(img) |
:not() con lista |
Esclude più selettori insieme | article:not(.draft, .archived) |
Questi selettori spostano in CSS logica che prima richiedeva JavaScript: filtrare per posizione, reagire al contenuto, escludere più casi insieme.
Commenti (0)
Registrati per commentare, fare domande e proporre l'argomento della prossima pillola (oppure accedi se hai già un account).
Nessun commento. Sii il primo a scrivere qualcosa.