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