Scovato via Twitter su jsFiddle, presentiamo in questo articolo un menu a livelli multipli con liste annidate creato da Harry Roberts di CSSWizardry.
Chiariamolo subito: non ha alla base tecniche rivoluzionarie o mai viste. Il valore aggiunto consiste nel fatto che l'autore ha concepito e strutturato il suo lavoro come una specie di mini-framework, se così si può dire, una solida base di partenza da riusare con facilità ogni qual volta si intenda adoperare questo tipo di soluzione. Verificherete che con piccolissimi interventi di personalizzazione e strutturando il markup nel modo corretto potrete ottenere in pochissimo un menu perfettamente funzionante su tutti i principali browser. L'unica accortezza, come in tutti i framework, consiste nell'uso di specifiche classi nel markup.
Per iniziare, ecco subito una demo con il menu decontestualizzato da un layout. Il documento, che potete scaricare in allegato insieme agli altri esempi, contiene tutto il codice HTML e CSS che ci serve, opportunamente commentato nei punti salienti.
Il CSS
Partiamo nella nostra analisi proprio dal CSS. Abbiamo suddiviso il codice per comodità in due sezioni.
La struttura del menu
La prima sezione è relativa alla struttura del menu. Contiene le regole che gestiscono il layout, il posizionamento degli elementi nei vari stati, etc. Importante: questa sezione va lasciata intatta, non c'è alcun motivo per modificarla. Eccola nella sua interezza:
/* Struttura menu */
.nav
{
list-style:none;
margin:0;
padding: 0;
}
.nav > li,
.nav > li > a
{
display:inline-block;
*display:inline; /* Per IE7 */
zoom:1; /* Per IE 7 */
}
.stacked > li
{
display:list-item;
}
.stacked > li > a
{
display:block;
}
.flyout,
.flyout-alt
{
position:relative;
}
.flyout-content
{
position:absolute;
top:100%;
/* Posizioniamo i sottomenu fuori dallo
schermo; alternativa a display:none
*/
left:-99999px;
height:0;
overflow:hidden;
}
.flyout:hover > .flyout-content
{
left:0;
}
.flyout-alt:hover > .flyout-content
{
top:0;
left:100%;
}
.flyout:hover > .flyout-content,
.flyout-alt:hover > .flyout-content
{
height:auto;
overflow:visible;
}
/* Fine struttura menu */
Evidenziamo solo alcuni punti.
- La lista di base (con classe
.nav
) va resettata impostandolist-style:none
e azzerando margini e padding. - Il menu di base viene strutturato in orizzontale non con i float, ma usando
display: inline-block
(si veda l'articolo Una griglia di immagini senza float e tabelle); la regola comprende anche gli hack per estendere la compatibilità a IE7. - Gli elementi con classe
.flyout-content
, ovvero i sottomenu, vengono posizionati assolutamente e inizialmente nascosti posizionandoli fuori dallo schermo, la nota tecnica alternativa adisplay: none
. - Questi stessi sottomenu vengono riposizionati nel punto giusto solo all'hover sugli elementi che li attivano.
L'aspetto del menu
La seconda sezione riguarda l'aspetto del menu, i suoi connotati visuali: colore del testo, colore di sfondo, bordi e qualunque altro effetto visivo si voglia applicare (nella demo ci siamo mantenuti sul semplice).
Questa parte contiene regole che ovviamente potranno essere modificate per adeguare il menu al look and feel del proprio sito. Ecco come si presenta nella demo:
/* Aspetto menu */
.site-nav a
{
line-height:1;
padding:1em;
background-color:#f58529; /* Colore di sfondo iniziale */
color:#fff; /* Colore del testo dei link */
font-weight: bold;
white-space:nowrap;
}
.site-nav .flyout:hover > a,
.site-nav .flyout-alt:hover > a,
.site-nav a:hover
{
background-color: #ba0202; /* Colore di sfondo sull'hover */
}
.site-nav .flyout-alt > a:after
{
content:" »";
} /* Inseriamo l'indicatore di presenza del sottomenu */
.site-nav a,
.site-nav .flyout-content
{
/* Colore e stile dei bordi;
azzeriamo lo spessore con 0px
*/
border: 0px solid #000;
}
.site-nav > li > a
{
/* Bordo sinistro sulle voci del menu principale */
border-left-width:1px;
}
.site-nav > li:first-child > a
{
/* Eliminiamo il bordo dalla prima voce del menu */
border:none;
}
.site-nav .flyout-content
{
/* Bordo superiore per il sottomenu */
border-width:1px 0 0 0;
}
.site-nav .flyout-content a
{
/* Bordo inferiore per le singole voci del sottomenu */
border-bottom-width:1px;
}
.site-nav .flyout-alt:hover > .flyout-content
{
/* Un piccolo aggiustamento per la posizione del
sottomenu sull'hover
*/
top:-1px;
}
/* Fine aspetto menu */
I commenti al codice parlano da sé, per cui non ci soffermiamo ulteriormente. Un'unica osservazione va fatta a proposito di questa riga:
.site-nav .flyout-alt > a:after {content:" »";}
Le voci di menu che sull'hover attivano un sottomenu annidato, sono contrassegnate dal carattere », una chiara indicazione all'utente. » viene inserito come contenuto generato e nulla vieta di poterlo sostituire con altro usando la stessa tecnica, si tratti di altri simboli o caratteri speciali o di piccole icone/immagini. Ricordiamo che il contenuto generato non è però supportato su IE7.
Il markup HTML</h2
Veniamo ora alla parte HTML. Come si accennava, essendo il tutto concepito come una sorta di framework, bisognerà prestare attenzione alla strutturazione dei tag e al rispetto di alcune regole relativamente ai nomi delle classi, che ovviamente dovranno essere in linea con quanto fissato nel CSS.
Iniziamo il nostro percorso passo per passo con il primo esempio, in cui il menu è stato inserito in un layout.
Nel foglio di stile style.css
ho tutte le regole che mi servono, devo solo mettere mano all'HTML. La base di partenza sarà un menu con cinque voci disposte orizzontalmente. Ecco il codice:
<ul class="nav site-nav">
<li><a href=#>Lorem</a><!-- * -->
<li><a href=#>Ipsum</a><!-- * -->
<li><a href=#>Sit</a><!-- * -->
<li><a href=#>Dolor</a><!-- * -->
<li><a href=#>Amet</a><!-- * -->
</ul>
La lista (ul
) principale, quella che racchiude tutto il resto, va dunque definita con due classi, .nav
e .site-nav
.
Ma probabilmente non è questo che ha attirato la vostra attenzione nel codice qui sopra riportato. Ricordate? Sul menu primario usiamo display: inline-block. E nell'articolo Inline block, risolvere il problema dello spazio bianco abbiamo imparato che se non si desiderano margini tra gli elementi è necessario rimuovere lo spazio bianco che si crea adottando tale tecnica. In questo articolo abbiamo usato la tecnica dell'eliminazione del tag di chiusura. I tag </li>
soppressi sono stati sostituiti per pure finalità espositive da un asterisco tra commenti. Potrete ovviamente eliminarli tranquillamente questi commenti oppure usare altre tecniche. Mai dimenticare però di eliminare lo spazio bianco sui li
del menu primario!
Aggiungiamo un sottomenu
Ora abbiamo dunque un semplice menu orizzontale. Aggiungiamo un sottomenu a discesa come abbiamo fatto nel secondo esempio.
Il sottomenu è associato alla seconda voce del menu, 'Ipsum'. Questo il markup HTML:
<ul class="nav site-nav">
<li><a href="#">Lorem</a><!-- * -->
<li class="flyout">
<a href="#">Ipsum</a>
<!-- Flyout -->
<ul class="flyout-content nav stacked">
<li><a href="#">Item 1</a></li>
<li><a href="#">Item 2</a></li>
<li><a href="#">Item 3</a></li>
</ul>
<!-- * -->
<li><a href="#">Sit</a><!-- * -->
<li><a href="#">Dolor</a><!-- * -->
<li><a href="#">Amet</a><!-- * -->
</ul>
E dunque:
- al
li
che attiva il sottomenu va associata la classe.flyout
; - dopo il link va inserita una lista annidata (
ul
) con tre classi,.flyout-content, .nav, .stacked
; - all'interno della lista inseriamo tutte le voci che ci interessano, ovviamente sotto forma di una serie
li
con link.
Ancora un sottomenu
Su questa base possiamo inserire tutti i sottomenu annidati che desideriamo. Nell'esempio 3, infatti, oltre al primo sottomenu, abbiamo pure un ulteriore sottomenu che compare di lato in corrispondenza dell'ultima voce, 'Item 3'. Ancora una volta, ecco il codice HTML:
<ul class="nav site-nav">
<li><a href="#">Lorem</a><!-- * -->
<li class="flyout">
<a href="#">Ipsum</a>
<!-- Flyout -->
<ul class="flyout-content nav stacked">
<li><a href="#">Item 1</a></li>
<li><a href="#">Item 2</a></li>
<li class="flyout-alt">
<a href="#">Item 3</a>
<!-- Flyout -->
<ul class="flyout-content nav stacked">
<li><a href="#">Item 1</a></li>
<li><a href="#">Item 2</a></li>
</ul>
</li>
</ul>
<!-- * -->
<li><a href="#">Sit</a><!-- * -->
<li><a href="#">Dolor</a><!-- * -->
<li><a href="#">Amet</a><!-- * -->
</ul>
Cosa abbiamo aggiunto rispetto a prima? Vediamolo:
- il
li
'Item 3' deve attivare un sottomenu che compare di lato, dunque prende la classe.flyout-alt
(e non semplicemente.flyout
!); - all'interno di questo
li
con classe.flyout-alt
, aggiungiamo la lista con le classi.flyout-content, .nav, .stacked
e suoi item.
Tutto qui. Questo il meccanismo di base ripetibile all'infinito, con tutti gli annidamenti che si desiderano. La demo originale su jsFiddle è più che sufficiente per chiarire cosa intendiamo per 'ripetibile all'infinito'.
Riassumendo:
- la lista/menu principale ha le classi
.nav
e.site-nav
; - un
li
che attiva sull'hover un semplice sottomenu verticale a discesa ha come classe.flyout
; - un
li
che attiva un sottomenu che compare di lato ha come classe.flyout-alt
; - le liste
ul
annidate che costituiscono i sottomenu hanno le classi.flyout-content, .nav, .stacked
.
Nell'esempio 4, per concludere, abbiamo arricchito il menu con altri sottomenu.
Tutte le demo sono disponibili in allegato.