Le gallerie di immagini realizzate attraverso l'utilizzo del solo Javascript costituiscono una possibilità alternativa e di crescente diffusione rispetto all'uso di Flash per la presentazione di foto ed immagini.
Nell'esempio che presenteremo verrà utilizzato jQuery, nella versione attualmente disponibile (1.3.2), per realizzare una semplice image gallery a scorrimento orizzontale. Ecco subito il link alla demo.
Per avere un approccio corretto ad un sviluppo progressivo dei contenuti, è imprescindibile partire da una versione di gallery che sia completamente fruibile senza Javascript e che XHTML e CSS siano in grado di offrire una buona flessibilità allo scopo. Pertanto sarà necessario prestare la stessa attenzione sia allo sviluppo della struttura della gallery quanto alla realizzazione dello script.
Il markup
Scegliamo quindi di rappresentare la nostra gallery come una consueta lista non ordinata <ul>
in cui le immagini si troveranno dentro i list-items <li>
da disporre orizzontalmente. Il codice minimale è così strutturato:
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"> <html xmlns="http://www.w3.org/1999/xhtml" lang="it" xml:lang="it"> <head> <meta http-equiv="Content-Type" content="text/html; charset=utf-8" /> <title>jQuery simple image gallery</title> </head> <body> <div class="imagegallery"> <ul> <li><img src="img/laghi-dei-piani-small.jpg" alt="" /></li> <li><img src="img/laghi-dei-piani-medium.jpg" alt="" /></li> <li><img src="img/laghi-dei-piani.jpg" alt="" alt="" /></li> <li><img src="img/laghi-dei-piani-medium.jpg" alt="" /></li> <li><img src="img/laghi-dei-piani-small.jpg" alt="" /></li> </ul> </div> </body> </html>
Il CSS
Affiancheremo quindi i list-items ma, invece di fare ricorso al consueto posizionamento tramite la proprietà float, utilizzeremo la proprietà display: inline-block
.
I vantaggi di questa soluzione sono molteplici perché:
- non usando blocchi in float non dovremo preoccuparci di effettuare un clearing sulla lista;
- a differenza dei float possiamo evitare di specificare la larghezza dei list-items e/o la larghezza complessiva della lista
<ul>
. Ciò ovviamente ci consentirà di inserire un numero indefinito di immagini di larghezza qualsiasi, senza dover dipendere da specifiche misure o impostazioni; - se abbiamo immagini di altezze differenti, attraverso la proprietà vertical-align sui list-items potremo
decidere come allinearle verticalmente (ad esempio possiamo centrarle con 'middle' o metterle in bottom), anche in questo caso senza conoscere a priori le dimensioni (altezza) delle immagini stesse.
Ecco il codice:
<style type="text/css" media="screen"> /* regole di base */ .imagegallery { position : relative; width : 860px; /* spazio orizzontale visibile della gallery */ height : 320px; overflow : hidden; border : 1px red solid; } .imagegallery ul { position : absolute; top : 0; left : 0; height : 300px; width : 100%; overflow-x : auto; overflow-y : hidden; list-style-type : none; white-space : nowrap; font-size : 0; } /* regola per il progressive enhancement */ ul.jquery { overflow : hidden; width : auto; } .imagegallery ul li { display : -moz-inline-box; /* Firefox 2 */ display : inline-block; vertical-align : middle; padding : 1px; } /** * haslayout trigger su IE6 e 7. */ .imagegallery ul li { *display : inline; } </style>
Si noti la presenza di queste due regole particolari:
white-space : nowrap; font-size : 0;
Applicate alla lista, la prima ci consente di evitare lo spezzarsi dei list-items su più righe, mentre la seconda determina l'annullamento dello spazio tra la chiusura di un elemento <li>
e quello successivo.
Se però si prevede di posizionare del testo all'interno dei list-items sarà necessario ripristinare il font-size (necessariamente con una misura assoluta) su un elemento interno al <li>
oppure eliminare quest'ultima regola e quindi scrivere il codice XHTML in modo da non presentare spazi bianchi (o ritorni a capo) tra ciascun tag </li>
e il tag <li>
successivo.
Procediamo alla parte di scripting. Queste in sintesi sono le funzionalità che dovremo prevedere:
- l'eliminazione della scrollbar orizzontale di sistema;
- la creazione del codice HTML delle azioni per scorrere la gallery;
- l'implementazione ed assegnazione dei relativi eventi.
Lo script
Per prima cosa eseguiamo alcune verifiche (esistenza della gallery, numero complessivo di immagini) e salviamo alcune
informazioni (valorizziamo un array con la larghezza di ciascun list-item, aggiungiamo la classe 'jquery' alla lista per eliminare
la scrollbar e memorizziamo i riferimenti ai nodi del DOM su cui dovremo fare in seguito delle operazioni).
/* funzione init */
(function(self)
{
if (self.g['cnt'] = $(idGallery))
{
self.g['list'] = $('ul', self.g['cnt']);
self.g['list'].addClass('jquery');
self.g['width'] = self.g['cnt'].width();
$('ul li', self.g['cnt']).each(function(i)
{
self.imagesWidth[i] = $(this).outerWidth();
self.listWidth += self.imagesWidth[i];
});
if (self.listWidth < self.g['width']) return false;
_addGalleryActions(self);
};
})(this);
Se la larghezza complessiva dei list-items supera in orizzontale la larghezza visibile della gallery aggiungeremo i controlli necessari per scorrerla attraverso il metodo privato _addGalleryActions()
.
var _addGalleryActions = function(self) { $('<p class="gallerycontrols"></p>').appendTo(self.g['cnt']); self.actionsCnt = $('p.gallerycontrols', self.g['cnt']); var addAction = function(label, classname, f) { $('<a></a>') .html(label) .attr('href', 'javascript:void(0)') .attr('class', classname) .bind('click', f) .appendTo(self.actionsCnt); }; /* azioni 'prev' e 'next' */ addAction(_actions['prev'], 'prev', function() { return _imageClick('prev', self); }); addAction(_actions['next'], 'next', function() { return _imageClick('next', self); }); /* il link 'previous' è disabilitato allo stato iniziale */ $('a.prev', self.actionsCnt).addClass('disabled'); };
Il metodo in questione aggiunge un paragrafo all'interno del div contenitore e la funzione addAction
si occupa di creare dinamicamente un link a cui viene assegnata una caption ("html(...)"
) e una funzione all'evento click ("bind(...)"
).
La funzione addAction()
viene eseguita due volte per creare il link 'precedente' e 'successivo' facendo un binding al metodo privato _imageClick()
. Disabilitiamo quindi il link 'previous' allo stato iniziale.
var _imageClick = function(direction, self) { switch (direction) { case 'prev' : if (self.g['offset'] < 0) { self.g['offset'] += self.imagesWidth[self.currentphoto-2]; self.g['list'].animate({ 'left' : self.g['offset'] }, _scrollTime); self.currentphoto--; /* abilito next e disabilito prev se sono all'inizio della gallery */ $('a.next', self.actionsCnt).removeClass('disabled'); $('a.prev', self.actionsCnt).toggleClass('disabled', self.g['offset'] >= 0); }; break; case 'next' : if (self.g['offset'] > -(self.listWidth - self.g['width'])) { self.g['offset'] -= self.imagesWidth[self.currentphoto-1]; /* lo spostamento della gallery deve considerare la parte restante che può essere minore rispetto alla larghezza del <li> successivo. Spostiamo la gallery del valore minimo tra i due. */ self.g['list'].animate({ 'left' : -(Math.min(-self.g['offset'], (self.listWidth - self.g['width']))) }, _scrollTime); self.currentphoto++; /* abilito prev e disabilito next se sono alla fine della gallery */ $('a.prev', self.actionsCnt).removeClass('disabled'); $('a.next', self.actionsCnt).toggleClass('disabled', self.g['offset'] <= -(self.listWidth - self.g['width'])); }; break; }; return false; };
La funzione _imageClick()
si occupa di animare la gallery (ovvero l'elemento lista). Quando viene richiesta la foto precedente, la gallery scrolla verso destra di un numero di pixel pari alla larghezza del list-item precedente.
Se invece viene richiesta la foto successiva va verificato se la distanza rimasta alla fine della gallery è minore rispetto alla larghezza della foto successiva. Ciò determinerà uno scroll differente a seconda dei casi.
Al termine dell'animazione verifichiamo le condizioni secondo cui dobbiamo disabilitare e/o abilitare le azioni 'precedente' e 'successivo'. La proprietà privata _scrollTime
(impostata all'inizio dello script a 500ms) determina il tempo di transizione tra una immagine e l'altra.
$('document').ready(...) vs. window.onload
È importante ricordare che l'evento $('document').ready(...)
di jQuery potrebbe facilmente scattare prima del completo caricamento delle immagini e ciò comporterebbe un evidente errore nella lettura della dimensione totale della gallery; pertanto è opportuno attivare lo script all'evento window.onload
.
Se si vuole comunque eseguire lo script all'evento $('document').ready(...)
è sufficiente impostare una width
a ciascun <li>
attraverso i CSS.
Il funzionamento (con Javascript abilitato e non) è stato verificato con successo in un buon numero di browser (IE6, IE7, IE8 (compatibility view compresa), Safari 3 e 4, Firefox 2 e 3, Opera 9.63). Lo script ha un peso di 3.7kb (1.9kb minified e 1.3kb se compresso lato server).
I file della demo sono disponibili per il download.