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:
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 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:
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:
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:
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:
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:
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:
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ù:
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:
pointer
, ossia quello associato di default ai collegamenti ipertestuali.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.
<li>
, quindi è stato già risolto dalla "normalizzazione" appena effettuata.<ul>
di secondo livello, ossia a quello individuato dal selettore nav>ul>li>ul
.nav>ul>li>ul>li
, si può assegnare ad esse un margin
.white-space:nowrap
.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:
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: