Nessun risultato. Prova con un altro termine.
Guide
Notizie
Software
Tutorial

CSS Sprites2: è l'ora di Javascript

Rivisitazione basata su jQuery della celebre tecnica inventata da Dave Shea per l'ottimizzazione di menu CSS
Rivisitazione basata su jQuery della celebre tecnica inventata da Dave Shea per l'ottimizzazione di menu CSS
Link copiato negli appunti

Questa è la traduzione dell'articolo CSS Sprites2 - It's JavaScript Time di Dave Sheapubblicato originariamente su A List Apart il 26 Agosto 2008. La traduzione viene qui presentata con il consenso dell'editore e dell'autore.

Il movimento è spesso ciò che spesso fa la differenza tra i siti basati su Flash e quelli standard. Le interfacce in Flash hanno sempre dato l'impressione di essere più vive, rispondendo alle interazioni dell'utente in un modo dinamico che i siti web basati sugli standard non sono in grado di replicare.

In seguito tutto ciò è però cambiato con l'avvento massiccio di effetti dinamici nelle interfacce resi possibili dall'utilizzo di librerie che rendono semplice la loro implementazione. Parlo di librerie come Prototype, Scriptaculous, MooTools, YUI, MochiKit (e potrei andare avanti). È dunque venuto il momento di rivisitare la tecnica dei CSS Sprites risalente a quattro anni fa ormai e vedere se non possiamo inserire un po' di movimento.

Gli esempi qui sotto mostrano i CSS Sprites2 in azione, è la tecnica che affronteremo in questo articolo (nota del traduttore: in questa traduzione è riportato solo uno screenshot dell'esempio; potete vederlo in azione facendo riferimento all'articolo originale):

Figura 1 - Screenshot del menu realizzato con la tecnica CSS Sprites2
screenshot

Ecco jQuery

Eccoci dunque al primo avviso: ci appoggeremo a jQuery per ottenere il risultato desiderato. jQuery è una libreria matura che fa tutte le cose interessanti che fanno anche le altre librerie ed ha un vantaggio addizionale che si presta molto bene per estendere come vogliamo la tecnica degli Sprites CSS: jQuery ci consente di selezionare gli elementi presenti sulla pagina usando una sintassi simile a quella dei CSS che conosciamo già.

Dobbiamo ovviamente accennare alla questione non triviale dei kilobyte extra che la libreria aggiungerà al peso della pagina al momento del suo caricamento. Un file Javascript esterno è naturalmente conservato nella cache del browser, così si tratta di scaricarlo una sola volta nel momento in cui un utente giunge per la prima volta sul vostro sito. La versione più compatta di jQuery pesa 15kb. È una cosa di cui tenere inevitabilmente conto. Se state già usando jQuery sul vostro sito per altre ragioni, allora la preoccupazione non sussiste. Se siete interessati ad aggiungerla solo per sfruttare questa tecnica, allora considerate il peso del file e decidete se ne vale la pena (dato che Google sta ora ospitando e servendo jQuery dai suoi server, potreste linkare la loro versione della libreria, come è stato fatto negli esempi che accompagnano questi articoli, e sperare che molti dei vostri utenti hanno già quell'URL nella cache dei loro browser).

E per le altre librerie Javascript? Non c'è assolutamente ragione per cui non possiate o dobbiate usarle; considerate questo articolo come una sorta di invito a portare i risultati di questa tecnica nella vostra libreria preferita.

Setup HTML e CSS di base

La prima cosa che vogliamo fare è creare uno di stato dei default, senza script per gli utenti che navigano senza Javascript (conosciamo l'articolo di Jeremy Keith da un po' di anni e siamo ora grandi fan del Javascript non intrusivo, ovviamente).

Abbiamo già un metodo basato sui soli CSS per il rollover, così iniziamo a costruire la nostra navigazione perché funzioni con un setup di base di Sprites CSS. E dato che siamo pigri, non rifaremo i rollover una seconda volta, riuseremo questo schema di base più tardi e aggiungeremo ad esso l'interazione con jQuery. Ci arriveremo presto.

Lascerò da parte i come e i perché dei CSS Sprites rimandandovi per essi all'articolo originale, ma ci sono alcune cose da chiarire. Iniziamo con il codice HTML. Fate attenzione alla struttura, ci torneremo spesso:

<ul class="nav current-about">
<li class="home"><a href="#">Home</a></li>
<li class="about"><a href="#">About</a></li>
<li class="services"><a href="#">Services</a></li>
<li class="contact"><a href="#">Contact</a></li>
</ul>

Ogni classe serve per uno scopo ben preciso: l'elemento ul contenitore ha una classe .nav che ci consente di usarla come target nel nostro CSS (e più tardi nel nostro Javascript) e una seconda classe .current-about che useremo per indicare, all'interno della navigazione, quale pagina o sezione del sito stiamo vedendo in quel momento. Ciascun li ha la sua specifica classe che useremo anche come target.

Bene. Il markup della nostra navigazione è una semplice e accessibile lista HTML, e abbiamo abbastanza classi per iniziare a lavorare sui nostri Sprites:

.nav {
width: 401px;
height: 48px;
background: url(../i/blue-nav.gif) no-repeat;
position: absolute;
top: 100px;
left: 100px;
}

Abbiamo impostato il valore per position su absolute per cambiare lo spostamento del posizionamento sul li invece che sull'elemento body. Avremmo potuto usare il valore relative per ottenere lo stesso risultato lasciando l'elemento con classe .nav all'interno del flusso del documento. Ci sono buone ragioni per usare entrambe le soluzioni, ma per il momento useremo absolute. Sulla questione absolute/relative si veda questo articolo di Douglas Bowman sull'argomento.

Il cuore della nostra tecnica per gli Sprites consiste nell'applicare un'immagine di sfondo a ciascuno degli item della navigazione e nel posizionarli assolutamente rispetto all'elemento ul che li contiene:

.nav li a:link, .nav li a:visited {
position: absolute;
top: 0;
height: 48px;
text-indent: -9000px;
overflow: hidden;
}
.nav .home a:link, .nav .home a:visited {
left: 23px;
width: 76px;
}
.nav .home a:hover, .nav .home a:focus {
background: url(../i/blue-nav.gif) no-repeat -23px -49px;
}
.nav .home a:active {
background: url(../i/blue-nav.gif) no-repeat -23px -98px;
}

Andremo un po' avanti rispetto all'articolo originale definendo anche gli stati per :focus e :active. La prima è un'aggiunta minore per attivare l'immagine di hover quando un ancora è soggetta sia allo stato :hover sia allo stato :focus. La seconda aggiunge un nuovo stato quando l'utente clicca su un item della navigazione. Nessuna delle due aggiunte è essenziale, sebbene sia una buona idea definirle entrambe. La regola overflow: hidden; è anch'essa nuova, è usata per impedire che alcuni browser estendano una cornice a puntini dalla posizione dell'elemento fino alla parte sinistra dello schermo dove si trova il testo indentato con un valore negativo.

Ecco il primo esempio, con il setup CSS di base per gli Sprites.

Siamo quindi al punto iniziale, un menu di navigazione basato sugli Sprites e funzionante, completo di segnalazione dell'item corrente. Ora estendiamolo.

Inizializzare jQuery

Notate che tutto ciò che segue qui sotto sarà inserito all'interno di una funzione jQuery che assicura che il codice venga eseguito solo quando l'intero documento è stato caricato:

$(document).ready(function(){
// everything goes here
});

Dal momento che il menu con i soli Sprites è la soluzione da offrire agli utenti quando Javascript è disabilitato, facciamo meglio a liberarci di quelle immagini di sfondo applicate nel CSS sull'hover dal momento che andremo a creare le nostre direttamente nello script qui sotto:

$(".nav").children("li").each(function() {
$(this).children("a").css({backgroundImage:"none"});
});

Con la prima riga di codice andiamo a rintracciare tutti gli elementi che hanno una classe .nav e aggiungiamo una nuova funzione a tutti gli elementi li figli che contengono. Quella funzione è definita nella seconda riga: rintraccia l'oggetto this per tutti gli elementi a figli. Se li trova, imposta la proprietà CSS background-image sul valore none. Dato il contesto, this indica gli elementi li su cui viene eseguita la funzione.

Ecco l'esempio 2, in cui disabilitiamo l'hover tramite CSS con jQuery.

Funziona, ma nel processo perdiamo anche il nostro item attualmente selezionato. Abbiamo così bisogno di effettuare un controllo per verificare quale item abbiamo identificato con la classe current-qualunquecosa che abbiamo applicato all'elemento ul parente e toglierlo dal processo di eliminazione dell'immagine di sfondo. Ecco allora che il precedente snippet di codice ha bisogno di qualche piccola aggiunta:

$(".nav").children("li").each(function() {
var current = "nav current-" + ($(this).attr("class"));
var parentClass = $(".nav").attr("class");
if (parentClass != current) {
$(this).children("a").css({backgroundImage:"none"});
}
});

Nella seconda riga si crea ora una variabile current che usa ciascuna classe dei li in sequenza per creare una stringa che dovrebbe corrispondere alle classi dell'elemento ul se quel particolare li rappresenta l'item attualmente selezionato. La terza riga crea una seconda variabile che legge il valore attuale direttamente dall'elemento ul. Infine, la quarta riga confronta le due variabili. Se non corrispondono, solo allora impostiamo la proprietà background-image dell'elemento a. Ciò evita la perdita dell'evidenziazione dell'item attualmente selezionato.

Aggiungere gli eventi

Ora abbiamo bisogno di aggiungere una funzione a ciascun elemento li per ogni evento di interazione che vogliamo modificare a livello di stile. Creiamo allora una funzione chiamata attachNavEvents:

function attachNavEvents(parent, myClass) {
$(parent + " ." + myClass).mouseover(function() {
// do things here
}).mouseout(function() {
// do things here
}).mousedown(function() {
// do things here
}).mouseup(function() {
// do things here
});
}

Questa funzione prende due argomenti. Il primo è una stringa che contiene la classe dell'elemento parent, completa del punto iniziale. Il secondo è una stringa che contiene la classe del li particolare a cui attaccare gli eventi. Combineremo entrambi nella prima riga della funzione per creare un selettore che ha come target lo stesso elemento del selettore discendente CSS di, per esempio, .nav, .home, etc.

Dal momento che jQuery ci consente di 'concatenare' più funzioni in un singolo oggetto, siamo in grado di creare tutte le funzioni attivate dagli eventi nello stesso tempo. Quello di 'concatenazione' è un concetto esclusivo di jQuery. È un po' particolare adattarvisi mentalmente e non è essenziale capire perché funziona, così se siete confusi vi basti sapere che funziona.

Ora attaccheremo queste funzioni a ciascun item della nostra navigazione. Lo snippet di codice che segue è un po' verboso, lo ottimizzeremo dopo, ma per ora eseguiamo la funzione su ogni li. Come argomenti passeremo l'elemento parent di ognuno, così come la classe propria di ciascun li:

attachNavEvents(".nav", "home");
attachNavEvents(".nav", "about");
attachNavEvents(".nav", "services");
attachNavEvents(".nav", "contact");

Tutto ciò non fa ancora molto, ma lo aggiusteremo.

Eccoci all'esempio 3: script con il setup di base per gli eventi.

La teoria

Spiegherò ora cosa accade. Seguitemi, è importante capire cosa accade in questo contesto perché avremo poi bisogno di modificare gli stili degli elementi che stiamo manipolando.

Per ciascuno dei link creeremo un nuovo elemento div all'interno del li che stiamo usando come target, div che useremo per i nostri effetti con jQuery. Applicheremo l'immagine nav a questo nuovo div usando la stessa regola per background-image vista in precedenza per l'elemento a all'interno dell'elemento li condiviso come parent. Posizioneremo assolutamente il div all'interno dell'elemento parent. È più o meno un duplicato dell'elemento a esistente visto nel setup dei nostri Sprites CSS. Attraverso una serie di prove, ho scoperto che la creazione di questo nuovo div risulta meno problematica dell'applicare direttamente l'effetto jQuery agli elementi esistenti, dunque è un passo necessario.

Lo stile per questo div deve essere definito nel CSS. Creeremo una nuova classe per questo li (.nav-home) basata sulla classe dell'elemento li usato come target (così non avremo conflitti con quanto già creato), e aggiungeremo lo stile:

.nav-home {
position: absolute;
top: 0;
left: 23px;
width: 76px;
height: 48px;
background: url(../i/blue-nav.gif) no-repeat -23px -49px;
}

La pratica

Ora è tempo di aggiungere gli effetti. Quando scatta l'evento mouseover, creeremo l'elemento div e gli assegneremo la classe menzionata in precedenza. Dobbiamo far sì che esso sia inizialmente invisibile prima dell'effetto fade con cui apparirà, così useremo la funzione css di jQuery per impostare un valore uguale a none per la proprietà display. Infine useremo la funzione jQuery fadeIn per far apparire il div; passeremo un argomento pari a 200 per quella funzione per specificare la durata di questa animazione in millisecondi:

function attachNavEvents(parent, myClass) {
$(parent + " ." + myClass).mouseover(function() {
$(this).before('<div class="nav-' + myClass + »
'"></div>');
$("div.nav-" + myClass).css({display:"none"}) »
.fadeIn(200);
});
}

Quindi, andremo a fare la stessa cosa al contrario quando scatta l'evento mouseout, facendo scomparire il div. Appena sarà scomparso, puliremo tutto rimuovendolo dal DOM. Ecco come dovrebbe essere la nostra funzione attachNavEvents:

function attachNavEvents(parent, myClass) {
$(parent + " ." + myClass).mouseover(function() {
$(this).before('<div class="nav-' + myClass + »
'"></div>');
$("div.nav-" + myClass).css({display:"none"}) »
.fadeIn(200);
}).mouseout(function() {
// fade out & destroy pseudo-link
$("div.nav-" + myClass).fadeOut(200, function() {
$(this).remove();
});
});
}

Esempio 4: script per gli eventi hover.

Avremmo fatto meglio a fare anche qualcosa per gli eventi mousedown e mouseup se in precedenza avessimo definito un cambiamento per lo stato :active nel CSS. Avremo bisogno di una classe diversa rispetto agli hover in modo che possa essere usata come unico target nel CSS. Cambiamo dunque la classe all'evento mousedown. Vogliamo anche far tornare tutto com'era al mouseup riportando alla luce lo stato :hover dal momento che l'utente potrebbe anche non aver mosso il mouse dall'item di navigazione. Ecco come appare la funzione attachNavEvents così modificata:

function attachNavEvents(parent, myClass) {
$(parent + " ." + myClass).mouseover(function() {
$(this).before('<div class="nav-' + myClass + »
'"></div>');
$("div.nav-" + myClass).css({display:"none"})»
.fadeIn(200);
}).mouseout(function() {
$("div.nav-" + myClass).fadeOut(200, function() {
$(this).remove();
});
}).mousedown(function() {
$("div.nav-" + myClass).attr("class", "nav-" »
+ myClass + "-click");
}).mouseup(function() {
$("div.nav-" + myClass + "-click").attr("class", »
"nav-" + myClass);
});
}

Possiamo riusare lo stile del div usato come hover modificando leggeremente la posizione dello sfondo per aggiustare la parte del nostro Sprite principale che viene mostrata al click:

.nav-home, .nav-home-click {
position: absolute;
top: 0;
left: 23px;
width: 76px;
height: 48px;
background: url(../i/blue-nav.gif) no-repeat -23px -49px;
}
.nav-home-click {
background: url(../i/blue-nav.gif) no-repeat -23px -98px;
}

Ora abbiamo gli hover, l'item attualmente selezionato e gli eventi attivati al click.

Esempio 5: mettere tutto insieme.

Altre considerazioni

Non siamo limitati al solo effetto fade. jQuery comprende anche una funzione slideUp/slideDown che possiamo usare (è mostrata nel secondo esempio allegato a questo articolo). Oppure possiamo sbizzarrirci e creare animazioni personalizate basate sui CSS con la funzione animate (come mostrato nel terzo esempio). Attenzione comunque ad animate: i risultati non sono sempre perfetti come avrete potuto notare nell'esempio.

Per quel che concerne la funzionalità cross-browser, jQuery funziona sulla maggior parte dei browser moderni. Tutto quelle che vedete funziona in IE6+, Firefox, Safari, Opera, etc. Abbiamo anche tenuto conto di diversi scenari in termini di graceful degradation. Se l'utente ha Javascript disabilitato ottiene la versione di base con gli Sprites CSS. Se ha disabilitato sia Javascript sia i CSS ottiene una lista di base HTML. E otteniamo anche gli altri benefici di base degli Sprites CSS, dal momento che stiamo ancora usando una singola immagine per tutti i vari stati della navigazione e per gli effetti.

Sebbene non sia richiesto, vi suggerisco di usare alcuni accorgimenti. Una velocità delle animazioni di più di qualche centinaio di millisecondi può essere divertente come inizio, ma presto la cosa potrebbe stare sui nervi a qualcuno. Piuttosto, quindi, rallentate.

Un altro potenziale inconveniente in cui potreste incorrere è quando altro testo presente sulla pagina sembri 'lampeggiare' durante l'animazione. È una questione complicata che ha che fare con il rendering sub-pixel comune nei moderni sistemi operativi. La correzione migliore sembra consistere nell'applicare un valore della proprietà opacity per forzare un particolare rendering del testo. Se aggiungete tutto ciò al vostro CSS il lampeggiamento dovrebbe scomparire:

p {
opacity 0.9999;
}

Questo codice è applicato nelle demo ai paragrafi, ma si può applicare a qualunque elemento.

Pacchetto pronto per l'uso

Non avete bisogno di ricordare alcuno script presente in questo articolo dal momento che è disponibile la funzione completa e pronta per l'uso che trovate nell'esempio finale. Usando il Javascript nel file HTML come riferimento avrete solo bisogno di modificare una piccola riga di Javascript per applicare la tecnica Sprites2 al vostro sito:

$(document).ready(function(){
generateSprites(".nav", "current-", true, 150, "slide");
});

La funzione generateSprites prende cinque argomenti:

  1. La classe primaria dell'elemento ul parent, incluso il punto iniziale.
  2. Il prefisso che state usando per gli item selezionati: per esempio, per una classe selected-about, usate selected- come valore.
  3. Un comando per indicare se state usando gli stili sullo stato :active. Impostatelo a true se avete definito lo stato :active nei vostri CSS, altrimenti impostatelo a false.
  4. La velocità dell'animazione.
  5. Lo stile dell'animazione che preferite sotto forma di stringa. Impostatelo su "slide" o "fade" (che è il valore di default).

Ecco l'esempio 6, con una semplice riga di Javascript da modificare grazie alla funzione generateSprites.

Avrete ancora bisogno di posizionare e modificare con gli stili i vari elementi nel CSS: usate pure come base di partenza i file CSS presenti in questo articolo.

Conclusione

Durante la scrittura di questo articolo è stata presentata una tecnica simile, sebbene non abbia come la nostra la versione che usa come fallback gli Sprites CSS. Abbiamo anche scoperto un menu animato basato su jQuery molto diverso ma che potrete trovare comunque utile.

Ti consigliamo anche