Realizzazione con i CSS di una barra di navigazione laterale con sottomenù che si aprono al passaggio del mouse

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:

Menù verticale con apertura del sottomenu al passaggio del mouse

Creazione della struttura HTML

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:

Struttura HTML del menù principale

I codici &amp; 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:

Struttura HTML del menù principale con i sottomenù

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ù.

Creazione del CSS

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:

Menù laterale formato da voci principali e sottomenù con <ul> e <li> visualizzati con i punti elenco di default

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:

La nostra pagina dopo aver impostato height: 100% per l'elemento <nav>: ancora non abbiamo ottenuto il comportamento desiderato

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 nostra pagina dopo aver impostato height: 100% per l'elemento <nav>: ancora non abbiamo ottenuto il comportamento desiderato

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:

La nostra pagina dopo aver impostato height: 100% per l'elemento <body>: finalmente abbiamo ottenuto il comportamento 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:

Menù senza decorazioni: nessun punto elenco, nessun padding, nessun margine

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:

Rispetto all'immagine precedente, ora ai sottomenù è stato assegnato un rientro attraverso la proprietà padding

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 nostro menù dopo la regolazione di alcuni padding e il rimpicciolimento delle voci del menù secondario

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à:

Il nostro menù dopo la regolazione del padding e della larghezza dell'intero menù

Ora nascondiamo i sottomenù: ognuno di essi dovrà apparire solo al passaggio del mouse sulla corrispondente voce del menù principale. Per farlo, dobbiamo:

  1. ridurre l'altezza di ciascun elemento <li> del menù principale, in modo che contenga solo la voce del menù principale, lasciando fuori il sottomenù;
  2. nascondere il sottomenù che è "fuoriuscito" in seguito alla riduzione dell'altezza.

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:

Il nostro menù con i sottomenù nascosti (grazie all'impostazione delle proprietà height e overflow

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:

I sottomenù si aprono al passaggio del puntatore del mouse

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:

Il nostro menù completo e finito