In questo tutorial realizzeremo un menù verticale, che potrà quindi essere inserito in una barra di navigazione laterale (a destra o a sinistra del contenuto principale della pagina). Passando con il mouse su ciascuna voce del menù, si deve aprire un sottomenù (anch'esso verticale) relativo alla voce stessa, come mostrato nella figura seguente:
Nota: questa sezione della pagina riproduce esattamente la sezione con lo stesso nome che si trova nel tutorial "Realizzazione di menu a tendina con i CSS". Se hai già svolto quel tutorial, quindi, ti consiglio di passare alla sezione "Creazione del CSS".
Dal menu che vedi in figura, avrai capito che come esempio utilizzeremo delle voci adatte ad un sito di acquisti online. Le voci sono classificate per categorie di prodotti (Electronics, Fashion, Home & Garden, Deals & Gifts, Collectibles & Art) in un primo livello di menu, quindi in sottocategorie più specifiche in un secondo livello di menu.
Costruiamo innanzitutto la struttura semantica del menu nel nostro file HTML. L'intero menu è compreso in un nodo di navigazione delimitato dal tag <nav>
. All'interno di questo menu si trova una lista <ul>
, composta dalle voci di menu di primo livello, ciascuna delle quali contiene a sua volta il relativo menu di secondo livello.
Iniziamo con il costruire il menu principale (all'interno del tag <body>
, ovviamente). Ecco la struttura HTML:
I codici &
sono l'"entità" HTML utilizzata per rappresentare il simbolo & ("e commerciale", in inglese "ampersand").
Ogni elemento del menu principale, però, deve contenere, oltre ad una delle categorie principali (Electronics, Fashion, Home & Garden, Deals & Gifts, Collectibles & Art), anche il sottomenu corrispondente a ciascuna di esse. Ogni elemento <li>
della struttura precedente, quindi, deve contenere a sua volta una lista <ul>
. Ecco come ciò viene realizzato tramite il codice HTML:
Nella figura precedente, si noti che abbiamo inserito anche un elemento <div>
, che ospiterà il contenuto principale della pagina.
Ricapitolando, la nostra struttura di menù con relativi sottomenù può essere schematizzata così:
Si noti, in particolare, che la voce "Deals & Gifts" è priva di sottomenù.
Ora che abbiamo la struttura HTML del nostro menù, dobbiamo impostarne l'aspetto e il comportamento attraverso il CSS.
Innanzitutto impostiamo uno sfondo grigio scuro per la pagina e scegliamo un carattere sans-serif verde e uno sfondo grigio chiaro per la barra di navigazione:
html {
background-color: rgb(60,40,60);
}
nav {
background-color: #DDD;
color: green;
font-family: sans-serif;
}
Quindi impostiamo la dimensione della barra di navigazione. Assegnamole una larghezza relativa espressa in em (cioè nell'unità di misura relativa alla dimensione del testo), in modo tale che, anche se l'utente del nostro sito ridimensiona i caratteri, le voci di menù saranno sempre visibili per intero:
html {
background-color: rgb(60,40,60);
}
nav {
background-color: #DDD;
color: green;
font-family: sans-serif;
width: 18em;
}
Diamo un'occhiata a cosa abbiamo ottenuto finora:
La barra laterale che contiene il menù di navigazione dovrebbe estendersi fino al limite inferiore della pagina, indipendentemente dalla lunghezza del menù. Dobbiamo allora assegnarle un'altezza pari al 100% del suo contenitore:
html {
background-color: rgb(60,40,60);
}
nav {
background-color: #DDD;
color: green;
font-family: sans-serif;
width: 18em;
height: 100%;
}
Vediamo se abbiamo ottenuto il nostro scopo:
Macché! La situazione non è cambiata di una virgola, se non per la comparsa del bordo giallo, che abbiamo aggiunto all'elemento <html>
per capire il motivo per cui la barra di navigazione non ha assunto le dimensioni desiderate.
Il bordo giallo ci permette di comprendere che l'elemento <html> non ha "di default" l'altezza del viewport. A meno che non la impostiamo noi come tale:
html {
background-color: rgb(60,40,60);
height: 100%;
}
nav {
background-color: #DDD;
color: green;
font-family: sans-serif;
width: 18em;
height: 100%;
}
La figura seguente (in particolare il bordo giallo) mostra che, pur avendo ottenuto l'altezza desiderata per l'elemento <html>
, non abbiamo ottenuto l'"estensione" dell'altezza della barra di navigazione.
La ragione è semplice: il genitore del nostro elemento <nav>
non è l'elemento <html>
, bensì l'elemento <body>
. Quindi la barra assume il 100% dell'altezza del <body>
, che però al momento, non avendo un valore assegnato per la proprietà height
, regola la propria altezza in base al contenuto della pagina.
Provvediamo allora ad assegnare esplicitamente al <body>
un'altezza pari al 100% dell'elemento <html>
:
html {
background-color: rgb(60,40,60);
height: 100%;
}
body {
height: 100%;
}
nav {
background-color: #DDD;
color: green;
font-family: sans-serif;
width: 18em;
height: 100%;
}
Ed ecco finalmente il risultato desiderato:
(Dal momento che non ci serve più, ho anche eliminato il bordo giallo.)
Ci sono dei margini indesiderati attorno alla barra. Facciamo così: eliminiamo tutti i margini e i padding che il browser assegna di default, in modo che poi potremo impostare solo quelli che desideriamo, avendo il completo controllo del nostro layout. L'applicazione di margin:0
e padding:0
a tutti gli elementi della pagina (indicati dal selettore *
) si chiama "normalizzazione".
Contemporaneamente, eliminiamo i punti elenco, che non si addicono a delle voci di menù, attraverso l'impostazione list-style:none
per entrambi gli elementi <ul>
, sia quello che contiene il menù principale che quello relativo ai sottomenù.
* {
margin: 0;
padding: 0;
}
html {
background-color: rgb(60,40,60);
height: 100%;
}
body {
height: 100%;
}
nav {
background-color: #DDD;
color: green;
font-family: sans-serif;
width: 18em;
height: 100%;
}
nav>ul {
list-style: none;
}
nav>ul>li>ul {
list-style: none;
}
Ora che abbiamo tolto tutti i margini e le decorazioni, ci troviamo con un menù tutto da formattare:
Iniziamo aggiungendo un po' di padding ai sottomenù:
nav>ul {
list-style: none;
}
nav>ul>li>ul {
list-style: none;
padding-left: 1em;
}
Ecco un particolare di quanto ottenuto:
Proseguiamo assegnando alle voci dei sottomenù un carattere leggermente più piccolo rispetto alle voci del menù principale, e inserendo un po' di spazio tra le voci dei sottomenù. Inserisci questo codice in coda al CSS scritto finora:
nav>ul>li>ul>li {
padding-top: 0.5em;
font-size: 0.9em;
}
Anche tra un sottomenù e il successivo c'è bisogno di un po' più di separazione. Assegniamo un padding-top
a ciascuna voce di sottomenù:
nav>ul {
list-style: none;
}
nav>ul>li {
padding-top: 0.7em;
}
nav>ul>li>ul {
list-style: none;
padding-left: 1em;
}
La figura seguente mostra l'effetto delle ultime proprietà impostate:
Il menù è troppo "attaccato" al margine sinistro del viewport: diamogli un po' di padding. Poi cambiamo l'aspetto del puntatore del mouse al passaggio sopra il menù.
nav>ul {
list-style: none;
padding-left: 0.5em;
cursor: pointer;
}
Possiamo anche regolare la dimensione della barra laterale, che, dopo la riduzione dei padding e della dimensione del font dei sottomenù rispetto a quelli di default, ora ha bisogno di una larghezza inferiore:
nav {
background-color: #DDD;
color: green;
font-family: sans-serif;
width: 14em;
height: 100%;
}
Ecco l'effetto dell'applicazione di queste ultime proprietà:
Ora nascondiamo i sottomenù: ognuno di essi dovrà apparire solo al passaggio del mouse sulla corrispondente voce del menù principale. Per farlo, dobbiamo:
<li>
del menù principale, in modo che contenga solo la voce del menù principale, lasciando fuori il sottomenù;Si tratterà quindi di impostare le proprietà height
e overflow
degli elementi nav>ul>li
:
nav>ul>li {
padding-top: 0.7em;
height: 1em;
overflow: hidden;
}
I sottomenù sono "scomparsi", come desiderato:
Al passaggio del mouse sulla voce di menù, questa deve cambiare di altezza lasciando apparire il relativo sottomenù. Quale altezza impostare, se i sottomenù hanno tutti altezze diverse, dipendenti dal numero di voci da cui sono composti? Ovvio: height:auto
, in modo che il browser regoli l'altezza scegliendo il valore più adeguato:
nav>ul>li {
padding-top: 0.7em;
height: 1em;
overflow: hidden;
}
nav>ul>li:hover {
height: auto;
}
La figura seguente dimostra che tutto funziona:
C'è solo un ultimo dettaglio da sistemare: l'apertura dei sottomenù è troppo brusca. Per fare in modo che essa avvenga più dolcemente si può utilizzare una transizione.
Una transizione non è altro che il passaggio graduale di una proprietà da un valore di partenza ad uno di arrivo. La proprietà "soggetta" alla transizione è indicata assegnando un valore alla proprietà transition-property
. La transizione è regolabile attraverso alcuni parametri come transition-duration
e transition-timing-function
: la prima determina la durata della transizione, la seconda la funzione che regola la "distribuzione" della transizione sulla sua durata (velocità costante dall'inizio alla fine; più veloce all'inizio, meno alla fine; più lenta all'inizio, più veloce alla fine; ecc.). Dopo qualche prova, ho individuato i valori più soddisfacenti per queste proprietà:
nav>ul>li {
padding-top: 0.7em;
height: 1em;
overflow: hidden;
transition-property: height;
transition-duration: 1s;
transition-timing-function: linear;
}
Le tre proprietà che abbiamo impostato per definire la transizione, possono essere impostate in un'unica riga di codice attraverso la sola proprietà transition
:
nav>ul>li {
padding-top: 0.7em;
height: 1em;
overflow: hidden;
transition: height 1s linear;
}
Sfortunatamente, il codice precedente non funziona: la transizione non avviene. Perché?
Perché non è possibile effettuare una transizione che coinvolga, come valore di partenza o di arrivo, il valore auto
. Ma non possiamo sostituire auto
con un valore ben determinato. Come ne usciamo?
Semplice: invece di utilizzare la proprietà height
, utilizziamo max-height
e applichiamo la transizione ad essa, assegnandole un valore finale maggiore del sottomenù più "lungo" tra quelli che abbiamo (o che ipotizziamo di avere in futuro):
nav>ul>li {
padding-top: 0.7em;
max-height: 1em;
overflow: hidden;
transition: max-height 1s linear;
}
nav>ul>li:hover {
max-height: 15em;
}
Ora il menù funziona come desideravamo.
A questo punto non ci resta altro da fare che associare dei collegamenti ipertestuali a ciascuna voce dei sottomenù o alle voci del menù principale che non abbiano sottomenù (come ad esempio, nel nostro caso, la voce Deals & Gifts) e che quindi devono aprire direttamente il link.
A mo' di esempio, associeremo ad ogni voce un link alla pagina di ricerca di Google relativa alla voce stessa. Riportiamo il codice HTML della terza e della quarta voce di menù: la terza è la più breve, e quindi ci fa risparmiare spazio; la quarta è l'unica che non ha sottomenù, quindi ha una struttura leggermente diversa. La struttura delle altre voci è facilmente deducibile da queste due.
<li>Home & Garden
<ul>
<li><a href="https://www.google.it/search?q=home+crafts">Crafts</a></li>
<li><a href="https://www.google.it/search?q=home+décor">Home Décor</a></li>
<li><a href="https://www.google.it/search?q=pet+supplies">Pet Supplies</a></li>
</ul>
</li>
<li><a href="https://www.google.it/search?q=deals+gifts">Deals & Gifts</a>
</li>
Come forse avevi già immaginato, gli elementi <a>
sono contenuti all'interno degli elementi <li>
.
Se provi a visualizzare il menù dopo queste modifiche, noterai che le voci a cui hai associato il link hanno i colori e la sottolineatura di default dei collegamenti ipertestuali.
Per modificare queste impostazioni puoi aggiungere questo codice in coda al file CSS:
nav a {
color: green;
text-decoration: none;
}
nav a:visited {
color: purple;
}
Vale forse la pena ricordare il significato dei selettori utilizzati. Il selettore nav a
identifica tutti gli elementi <a>
contenuti all'interno di un elemento <nav>
, non importa quanti livelli al di sotto di esso; a differenza, ad esempio, del selettore nav>a
(non presente nel nostro codice), che avrebbe indicato solo gli elementi <a>
figli (ossia discendenti diretti) di un elemento <nav>
.
Il selettore nav a:visited
, poi, identifica tutti gli elementi <a>
contenuti all'interno di un elemento <nav>
il cui collegamento sia stato già visitato.
Ecco finalmente il nostro menù completo e finito: