Realizzazione di menu a tendina con i CSS

In questo tutorial realizzeremo un menu orizzontale, che posizioneremo nella parte superiore della pagina. Passando con il mouse su ciascuna voce del menu, si deve aprire un sottomenu verticale relativo alla voce stessa, come mostrato nella figura seguente:

Menù orizzontale con apertura del sottomenu al passaggio del mouse

Creazione della struttura HTML

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 chiaro per la pagina e scegliamo un carattere sans-serif bianco e uno sfondo grigio scuro per la barra di navigazione:

html { background-color: #CCC; } nav { background-color: rgb(60,40,60); color: white; font-family: sans-serif; }

Quindi impostiamo la posizione e la dimensione della barra di navigazione. Riguardo alla larghezza, le diamo il 100%, in modo tale che si estenda sull'intera larghezza del viewport. Poiché inoltre vogliamo che essa occupi una striscia orizzontale in alto, indipendentemente dalla posizione che l'elemento <nav> occupa nel DOM, le assegniamo una posizione assoluta:

html { background-color: #CCC; } nav { background-color: rgb(60,40,60); color: white; font-family: sans-serif; width: 100%; position: absolute; top: 0; left: 0; }

Gli elementi <ul> e <li> sono entrambi di tipo block, quindi per ora la pagina risulterà costituita da una "colonna" di voci elencate verticalmente:

Colonna di voci di menù elencate verticalmente (perché gli elementi <ul> e <li> sono di tipo block)

Nulla a che vedere, per ora, con il menù orizzontale che vorremmo ottenere...

Trasformiamo allora gli elementi <li> che costituiscono le categorie principali del menù in elementi di tipo inline-block: html { background-color: #CCC; } nav { background-color: rgb(60,40,60); color: white; font-family: sans-serif; width: 100%; position: absolute; top: 0; left: 0; } nav>ul>li { display: inline-block; }

Ed ecco che infatti le 5 categorie si dispongono orizzontalmente lungo la barra di navigazione:

Disposizione orizzontale delle categorie grazie a display: inline-block;

Vale la pena osservare che l'impostazione display:inline-block ha fatto perdere alle voci del menù principale il segnaposto che precede ciascuna voce, come se avessimo impostato list-style:none. A scanso di equivoci, comunque, conviene esplicitare quest'impostazione, sia nel menù principale che in quello secondario:

nav>ul { list-style: none; } nav>ul>li { display: inline-block; } nav>ul>li>ul { list-style: none; }

Ora allineiamo i menù in alto, impostando la proprietà vertical-align degli elementi <li> del menù principale:

nav>ul { list-style: none; } nav>ul>li { display: inline-block; vertical-align: top; } nav>ul>li>ul { list-style: none; }

Ecco il risultato:

Allineamento verticale in alto delle categorie

Veniamo finalmente alla creazione dell'"effetto tendina", grazie al quale passando con il mouse su una voce del menù principale, vedremo comparire solo il sottomenù corrispondente.

Al passaggio del mouse, insomma, dovremo rendere "visibile" qualcosa che prima era "nascosto". Per ottenere questo effetto è naturale quindi sfruttare la proprietà overflow, modificandola da overflow:hidden a overflow:visible.

Perché questo "trucco" funzioni, è necessario però che i sottomenù, che devono comparire e scomparire, siano in overflow, ossia fuori dal rettangolo occupato dal loro genitore.

Un modo per "tirarli fuori" è impostarne la proprietà position al valore absolute. Ti ricordo, infatti, che un elemento con position:absolute si trova fuori dal flusso degli elementi della pagina (tanto è vero che, ad esempio, abbiamo impostato position:absolute anche per il nostro elemento <nav>: in questo modo potremo posizionarlo dove vogliamo - nel nostro caso nella parte alta della pagina - indipendentemente dalla posizione che occupa all'interno del codice HTML).

Passiamo all'azione: l'elemento che deve apparire e scomparire è il sottomenù <ul> contenuto in ogni elemento <li>. Assegniamogli una posizione assoluta:

nav>ul { list-style: none; } nav>ul>li { display: inline-block; vertical-align: top; border: solid 1px yellow; /* solo per studio */ } nav>ul>li>ul { list-style: none; position: absolute; }

Avrai notato che ho inserito anche un bordo attorno alle categorie di primo livello; ci servirà per capire meglio cosa comporta l'impostazione di una posizione assoluta. Ecco l'esito di questa impostazione:

Impostazione della posizione assoluta per i sottomenù

Ignoriamo per un attimo il fatto che i sottomenù siano sovrapposti e quindi illegibili: i dettagli relativi agli allineamenti, ai margini e ai padding di ciascun elemento potranno essere sistemati in seguito. In ogni caso, la sovrapposizione che vedi in figura non costituirà un problema, dal momento che, quando avremo ottenuto il funzionamento che desideriamo, i sottomenù non compariranno mai contemporaneamente: ne sarà visibile invece solo uno, quello corrispondente alla voce su cui l'utente passerà con il mouse.

La figura precedente, invece, ci fornisce due indizi che ci suggeriscono che i sottomenù sono "usciti" in qualche modo dalle voci del menù principale a cui appartengono, almeno dal punto di vista del layout:

Visto che ora i sottomenù "fuoriescono", proviamo ad impostare overflow:hidden per gli <li> (ossia, in termini di CSS, quelli individuati dal selettore nav>ul>li):

nav>ul>li { display: inline-block; vertical-align: top; overflow: hidden; border: solid 1px yellow; /* solo per studio */ }

Ecco cosa otteniamo:

Impostazione di overflow:hidden per i sottomenù

Ops! Non è cambiato nulla! Perché?

Probabilmente il motivo è che, impostandone la posizione ad absolute, abbiamo, sì, "tirato fuori" ciascun sottomenù dall'area coperta dal suo nodo genitore (che quindi, pur restando il suo genitore, non è più il suo "contenitore"). Ma forse abbiamo fatto ancora di più (troppo!): dal punto di vista del layout, essi "appartengono" ora al primo "antenato" con proprietà position diversa da quella di default (vale a dire static). Poiché non è stata impostata la proprietà position di nessun elemento del nostro DOM, l'elemento contenitore dei sottomenù è l'elemento radice del DOM, ossia l'elemento <html>.

Per questo motivo, l'impostazione overflow:hidden per l'elemento <li> non ha alcun effetto. Lo avrebbe se facessimo in modo che l'elemento <li> tornasse ad essere il contenitore dei sottomenù.

Si può fare! Impostando la proprietà position degli <li> al valore relative:

nav>ul>li { display: inline-block; vertical-align: top; position: relative; overflow: hidden; }

Si noti che abbiamo tolto anche il bordo giallo, che ora non ci serve più.

Il risultato che otteniamo conferma i nostri ragionamenti:

Menù con i sottomenù nascosti: si vedono solo le voci principali

Le voci sono troppo vicine le une alle altre: aumentiamo allora il degli elementi <li>:

nav>ul>li { display: inline-block; vertical-align: top; position: relative; overflow: hidden; margin: 1em; }

Il codice inserito in realtà modifica, in un'unica istruzione, oltre ai margini orizzontali (ossia margin-left e margin-right), anche quelli verticali (cioè margin-top e margin-bottom). Al momento sembra non essere necessario, visto che le voci hanno già un certo margine verticale; tuttavia averli definiti esplicitamente si rivelerà utile quando, più avanti, normalizzeremo il foglio inserendo all'inizio un'istruzione CSS per azzerare tutti i margini e tutti i padding.

Ecco il nostro menù:

Menù con i sottomenù nascosti e un margine adeguato tra una voce e l'altra

Se passi con il puntatore del mouse, com'è ovvio, non succede nulla. Per far apparire i sottomenù dobbiamo impostarne la proprietà overflow al valore visible quando su di loro passa il puntatore del mouse (ossia, la proprietà va associata allo pseudo-elemento nav>ul>li:hover):

nav>ul { list-style: none; } nav>ul>li { display: inline-block; vertical-align: top; position: relative; overflow: hidden; margin: 0 1em; } nav>ul>li:hover { overflow: visible; } nav>ul>li>ul { list-style: none; position: absolute; }

L'apertura dei sottomenù al passaggio del mouse funziona. Resta aperto qualche problema:

  1. i sottomenù sono spostati a destra rispetto alle rispettive voci principali: una certa indentazione (rientro verso destra) può anche essere desiderata, ma quella che c'è ora è eccessiva;
  2. essendo in overflow rispetto al loro contenitore, i sottomenù non hanno colore di sfondo;
  3. le voci dei sottomenù sono troppo vicine tra loro: servirebbe un po' di separazione verticale;
  4. i sottomenù hanno una larghezza troppo esigua, che tra l'altro obbliga ad andare a capo le voci più lunghe;
  5. il cursore del mouse, al passaggio sui menù, dovrebbe cambiare aspetto: un cursore adeguato potrebbe essere il pointer, ossia quello associato di default ai collegamenti ipertestuali.
  6. Visto che è emersa la necessità di regolare alcune dimensioni, oltre ai margini e ai padding di alcuni elementi, prima di iniziare conviene inserire in testa al file CSS un'istruzione che "normalizza" la pagina, azzerando tutti i margini e i padding (in questo modo potremo inserire i valori che desideriamo, senza essere "intralciati" dai valori di default): * { margin: 0; padding: 0; } html { background-color: #CCC; }

    (Ripeto: l'istruzione di normalizzazione dei margini e dei padding va inserita all'inizio del foglio di stile.)

    Torniamo ai problemi elencati poco fa.

    1. Il primo punto era dovuto al padding di default degli elementi <li>, quindi è stato già risolto dalla "normalizzazione" appena effettuata.
    2. Il colore di sfondo deve essere assegnato al blocco <ul> di secondo livello, ossia a quello individuato dal selettore nav>ul>li>ul.
    3. Per separare le varie voci dei sottomenù, ossia gli elementi individuati dal selettore nav>ul>li>ul>li, si può assegnare ad esse un margin.
    4. Per aumentare la larghezza dei sottomenù, si può impedire di andare a capo alle voci da cui sono composti, impostando white-space:nowrap.
    5. Per fare in modo che, al passaggio sui menù, il mouse assuma l'aspetto di default dei collegamenti ipertestuali, si può assegnare cursor:pointer all'elemento <ul> di primo livello. Ciò in realtà non è indispensabile, visto che tra poco associeremo dei collegamenti ipertestuali alle voci dei menù, ottenendo automaticamente l'effetto desiderato.

    Ecco il codice con tutte le modifiche proposte (compresa la normalizzazione, che ripetiamo qui, sebbene sia stata già mostrata nella sezione di codice precedente):

    * { margin: 0; padding: 0; } html { background-color: #CCC; } nav { background-color: rgb(60,40,60); color: white; font-family: sans-serif; width: 100%; position: absolute; top: 0; left: 0; } nav>ul { list-style: none; cursor: pointer; } nav>ul>li { display: inline-block; vertical-align: top; position: relative; overflow: hidden; margin: 1em; } nav>ul>li:hover { overflow: visible; } nav>ul>li>ul { list-style: none; position: absolute; background-color: rgb(60,40,60); } nav>ul>li>ul>li { margin: 1em; white-space: nowrap; }

    Abbiamo così ottenuto finalmente il menù che desideravamo, come si può vedere dalle due figure seguenti:

    Apertura del primo sottomenù al passaggio del mouse Apertura del terzo sottomenù al passaggio del mouse

    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: white; text-decoration: none; } nav a:visited { color: pink; }

    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 il risultato:

    Il nostro menù completo e finito