Questa è la traduzione dell'articolo Cleaner Code with CSS3 Selectors di Rachel Andrew pubblicato originariamente su 24 Ways il 20 Dicembre 2009.
Le parti dei CSS3 che sembrano attirare l'attenzione maggiore su blog e articoli sono quelle dedicate agli aspetti prettamente visuali. Angoli arrotondati, ombreggiature sul testo e nuovi modi per ottenere layout con i CSS sono tutte cose molto interessanti e portano con sé nuovi scenari per chi si occupa di web design. Tuttavia, la cosa che mi attira di più, come sviluppatrice, è un'altra, molto meno appariscente.
In questo articolo esamineremo alcuni dei modi in cui il nostro codice a livello di front-end e di back-end può essere semplificato grazie all'uso dei CSS3, osservando da vicino come certi effetti visuali sono ottenuti oggi e come saranno realizzati in un futuro glorioso in cui il supporto ai CSS3 sarà diffuso pienamente. Mostreremo pure come possiamo usare già ora i selettori CSS3 con un piccolo aiuto di Javascript, cosa che può essere molto utile se vi trovate in situazioni in cui non potete cambiare il markup che viene prodotto da codice lato server.
Le meraviglie di nth-child
Perché mi entusiasma così tanto il selettore di tipo nth-child
? Ecco uno scenario comune. Il designer vorrebbe creare una tabella che abbia questo aspetto:
Impostare colori diversi e alternati per le righe di una tabella è un metodo comune per migliorare la leggibilità di righe lunghe. I metodi sperimentati per implementare questo effetto consistono nell'aggiunta di una classe alle righe (pari o dispari) della tabella. Se state scrivendo il codice a mano si tratta di un'operazione più che fastidiosa, e se poi sbagliate su una riga verso il centro, vi toccherà modificare tutto il resto. Se il markup è prodotto da un CMS, avrete bisogno di uno script lato server per aggiungere la classe (sempre che possiate mettere mano al codice).
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<title>Striping every other row - using classes</title>
<style type="text/css">
body { padding: 40px; margin: 0;
font: 0.9em Arial, Helvetica, sans-serif; }
table { border-collapse: collapse;
border: 1px solid #124412;
width: 600px; }
th { border: 1px solid #124412;
background-color: #334f33;
color: #fff;
padding: 0.4em;
text-align: left; }
td { padding: 0.4em; }
tr.odd td { background-color: #86B486; }
</style>
</head>
<body>
<table>
<tr><th>Name</th><th>Cards sent</th><th>Cards received</th><th>Cards written but not sent</th></tr>
<tr><td>Ann</td><td>40</td><td>28</td><td>4</td></tr>
<tr class="odd"><td>Joe</td><td>2</td><td>27</td><td>29</td></tr>
<tr><td>Paul</td><td>5</td><td>35</td><td>2</td></tr>
<tr class="odd"><td>Louise</td><td>65</td><td>65</td><td>0</td></tr>
</table>
</body>
</html>
La situazione la incontro in quasi tutti i progetti su cui lavoro. Non è certo ideale avere del codice lato server che vada a inserire nomi di classe nel markup per fini puramente presentazionali. Ecco allora che ci viene in aiuto il selettore di pseudo-classe nth-child
dei CSS3. Il codice lato server crea una tabella in HTML valido per i dati, con i CSS selezioniamo le righe dispari con il seguente selettore:
tr:nth-child(odd) td {
background-color: #86B486;
}
Le parole chiave odd
(dispari) e even
(pari) sono molto utili in un simile contesto, ma è possibile usare anche un moltiplicatore qui. 2n
sarebbe l'equivalente della parola chiave 'odd', 3n
selezionerebbe ogni terza riga in un gruppo di tre, e così via.
Supporto nei browser
Purtroppo, nth-child
ha un supporto non molto esteso nei principali browser. Non è supportato da Internet Explorer 8 e il supporto presenta qualche bug anche in altri browser. Firefox 3.5 lo supporta pienamente. In alcune circostanze, tuttavia, si potrebbe considerare l'uso di Javascript per aggiungere il supporto ai browser che non lo offrono. La cosa potrebbe essere molto utile se avete a che fare con un CMS su cui non potete modificare il codice lato server per aggiungere i nomi di classe nel markup.
Userò la libreria jQuery in questi esempi, dal momento che è molto semplice usare lo stesso selettore usato nel CSS per individuare gli elementi a cui applicare la classe. Potrete ovviamente usare qualunque libreria per ottenere lo stesso effetto. Nel CSS ho aggiunto il selettore di classe originale al selettore nth-child
:
tr:nth-child(odd) td, tr.odd td {
background-color: #86B486;
}
Quindi aggiungo un po' di Javascript basato su jQuery per assegnare una classe al markup una volta che il documento sia stato caricato. Si usa lo stesso selettore nth-child
che funziona nei browser che lo supportano:
<script src="http://code.jquery.com/jquery-latest.js"></script>
<script>
$(document).ready(function(){
$("tr:nth-child(odd)").addClass("odd");
});
</script>
Potremmo semplicemente aggiungere un colore di sfondo usando jQuery, tuttavia io preferisco non mischiare questa informazione nel codice Javascript, dal momento che se cambiamo il colore sulle righe della nostra tabella dovremmo ricordarci di cambiarlo sia nel CSS sia nello script.
Fare qualcosa di diverso con l'ultimo elemento
Ed ecco un'altra situazione con cui ci confrontiamo spesso. Abbiamo una lista di oggetti tutti floattati a sinistra con un margine destro su ciascun elemento vincolato in un layout a larghezza fissa. Se ciascun elemento ha un margine applicato a destra il margine dell'ultimo elemento farà sì che il box risulti troppo largo, facendo scivolare in basso l'ultimo elemento su un'altra riga.
Al momento abbiamo due modi per risolvere questo problema. Possiamo impostare un margine destro negativo sulla lista, pari alla larghezza dello spazio tra gli elementi. Ciò significa che il margine extra sull'elemento finale riempie quello spazio e l'elemento non scivola in basso.
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<title>The last item is different</title>
<style type="text/css">
body { padding: 40px; margin: 0;
font: 0.9em Arial, Helvetica, sans-serif; }
div#wrapper { width: 740px; float: left;
border: 5px solid #ccc; }
ul.gallery { margin: 0 -10px 0 0; padding: 0;
list-style: none; }
ul.gallery li { float: left; width: 240px;
margin: 0 10px 10px 0;}
</style>
</head>
<body>
<div id="wrapper">
<ul class="gallery">
<li><img src="xmas1.jpg" alt="baubles" /></li>
<li><img src="xmas2.jpg" alt="star" /></li>
<li><img src="xmas3.jpg" alt="wreath" /></li>
</ul>
</div>
</body>
</html>
L'altra soluzione consiste nell'assegnare una classe all'elemento finale e nel CSS rimuovere il margine per questa classe.
ul.gallery li.last {
margin-right: 0;
}
La seconda soluzione potrebbe non essere semplice se il contenuto è generato da un CMS di cui non si può modificare il codice.
Potrebbe in realtà essere tutto diverso. Nei CSS3 abbiamo meravigliosi selettori come last-child
. Significa che possiamo semplicemente aggiungere delle regole per l'ultimo item della lista.
ul.gallery li:last-child {
margin-right: 0;
}
Non abbiamo fatto altro che rimuovere il margine sull'elemento li
che è il last-child
(ultimo elemento figlio) della lista ul
con classe gallery
. Non c'è bisogno di fare confusione con classi applicate all'ultimo item o con la modifica della larghezza dell'elemento attraverso margini negativi.
Se questa lista di item si ripetesse all'infinito allora potreste usare per questo scopo il selettore nth-child
creando una regola che toglie il margine ai terzi elementi di un gruppo.
ul.gallery li:nth-child(3n) {
margin-right: 0;
}
Un esempio simile è quello in cui un designer abbia aggiunto un bordo sul fondo di ciascun elemento, ma non vuole che il bordo appaia sull'ultimo elemento oppure vuole dargli un aspetto diverso. Di nuovo, solo una classe applicata all'ultimo elemento vi salverà in una circostanza simile se non potete fare affidamento sul selettore last-child
.
Supporto di last-child nei vari browser
La situazione per last-child
è simile a quella di nth-child
: non è supportato da IE8. Tuttavia, ancora una volta, è molto semplice replicare la funzionalità usando jQuery. Aggiungendo, ad esempio, la classe .last
all'ultimo item della lista:
$("ul.gallery li:last-child").addClass("last");
Divertirsi con i form
Applicare stili ai form può essere complicato, soprattutto per il fatto che ogni CSS applicato all'elemento <input>
avrà effetti sui campi di testo, sui pulsanti, sui checkbox e sui radiobuttons. A noi sviluppatori non rimane che aggiungere delle classi ai vari campi per differenziarli. I miei campi di testo, per esempio, hanno quasi sempre una classe text
, sebbene mai mi sognerei di aggiungere una classe para ai singoli paragrafi definiti nel documento.
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<title>Syling form fields</title>
<style type="text/css">
body { padding: 40px; margin: 0;
font: 0.9em Arial, Helvetica, sans-serif;}
form div { clear: left; padding: 0 0 0.8em 0; }
form label { float: left; width: 120px; }
form .text, form textarea { border:1px solid #333;
padding: 0.2em;
width: 400px; }
form .button { border: 1px solid #333;
background-color: #eee;
color: #000;
padding: 0.1em; }
</style>
</head>
<body>
<h1>Send your Christmas list to Santa</h1>
<form method="post" action="" id="christmas-list">
<div><label for="fName">Name</label>
<input type="text" name="fName" id="fName" class="text" /></div>
<div><label for="fEmail">Email address</label>
<input type="text" name="fEmail" id="fEmail" class="text" /></div>
<div><label for="fList">Your list</label>
<textarea name="fList" id="fList" rows="10" cols="30"></textarea></div>
<div><input type="submit" name="btnSubmit" id="btnSubmit" value="Submit" class="button" ></div>
</form>
</body>
</html>
I selettori di attributo forniscono un modo per individuare elementi in base ai loro attributi. Diversamente dagli esempi precedenti che sono tutti basati su selettori CSS3, i selettori di attributo sono definiti già nella specifica CSS2.1. Solo che non vengono molto usati a causa del mancato supporto su IE6.
Usando i selettori di attributo possiamo scrivere delle regole per i campi di testo e per i pulsanti del modulo senza la necessità di usare classi aggiuntive nel markup. Per esempio, dopo aver rimosso le classi text
e button
dagli elementi input, posso usare le seguenti regole per individuarli:
form input[type="text"] {
border: 1px solid #333;
padding: 0.2em;
width: 400px;
}
form input[type="submit"]{
border: 1px solid #333;
background-color: #eee;
color: #000;
padding: 0.1em;
}
Un altro problema che incontro con i form è quando uso i CSS per posizionare le label e gli elementi dei form rendendo float
le label. Funziona bene fin quando voglio che tutte le mie label siano floattate; tuttavia, a volte abbiamo un set di checkbox e radiobuttons e non vogliamo che le label siano floattate. Come potete vedere nell'esempio qui sotto, la label per i checkbox è spinta in basso nello spazio usato per altre label, anche se ha un senso che il checkbox appaia dopo il testo.
Potrei usare una classe su quell'elemento label; tuttavia i CSS3 mi consentono di individuare l'attributo della label direttamente guardando al valore dell'attributo for
.
label[for="fOptIn"] {
float: none;
width: auto;
}
Essere in grado di individuare elementi con questa precisione può essere incredibilmente utile. Una volta che IE6 non sarà più un problema, tutto ciò ci consentirà di rendere molto più pulito il nostro markup.
Resa cross browser degli elementi dei form
Le notizie per il selettore di attributo sono piuttosto buone per IE7+, Firefox 2+ e per tutti i browser moderni. Come ho accennato, si tratta di un selettore dei CSS2.1 e dovremmo pur essere in grado di usarlo nel 2010! IE7 ha purtroppo qualche bug e non esegue al meglio l'esempio visto in precedenza. Ho però scoperto un metodo per aggiurare il problema nei commenti alla reference CSS di Sitepoint. Aggiungendo il selettore label[htmlFor="fOptIn"]
al giusto selettore farà funzionare le cose anche sul browser di Microsoft.
IE6, invece, non supporta questo selettore. Possiamo però ancora una volta affidarci a jQuery. Il codice che segue aggiungerà le classi text
e button
ai nostri campi, e anche una classe checks
alla label per i checkbox (potremo usarla per rimuovere il float e la larghezza dalla label):
$('form input[type="submit"]').addClass("button");
$('form input[type="text"]').addClass("text");
$('label[for="fOptIn"]').addClass("checks");