Niveau : 
Une galerie d'images simple avec jQuery
Tutoriel par Benjamin D.C. (Interaction Designer, Lasne)
Introduction
Réaliser une galerie photo en JavaScript n'est à première vue pas bien
compliqué. Pourtant, si l'on veut bien faire les choses, le code peut
rapidement devenir complexe et verbeux.
Nous allons réaliser un système simple et non intrusif de visualisation
d'images avec chargement et effet de fondu, comme sur
cet exemple.
Grâce à jQuery,
l'écriture de ce script s'en trouve grandement simplifiée et concise.
Voyons pas à pas la création de notre galerie photo.
Attention: l’adjectif «simple» dans le titre du tutoriel désigne la galerie d’images réalisée, pas forcément les étapes pour y parvenir. Ce tutoriel est recommandé pour les personnes ayant déjà des connaissances de base en JavaScript, et connaissant si possible la libraire jQuery.
La base HTML
Nous allons commencer par créer une liste de vignettes qui, une fois
cliquées, afficheront nos images grand format. Nous pourrions tout aussi
bien écrire de simples liens textuels, cela n'a aucune importance sur le
script que nous allons réaliser. Partons donc sur base d'une liste non
ordonnée que nous appellerons thumbs et qui englobera nos
vignettes photos.
Les images sont ici classées dans des dossiers big et small, eux-mêmes contenus dans un
dossier images; libre à vous de nommer différemment vos dossiers,
voire de ne pas créer de dossier du tout.
<ul id="thumbs">
<li>
<a href="images/big/bernache.jpg">
<img alt="Photo grand format d'une famille de Bernaches du Canada"
src="images/small/bernache.png" />
</a>
</li>
<li>
<a href="images/big/bois.jpg">
<img alt="Photo grand format d'un sous bois"
src="images/small/bois.png" />
</a>
</li>
<li>
<a href="images/big/fleur.jpg">
<img alt="Photo grand format d'un cœur de fleur"
src="images/small/fleur.png" />
</a>
</li>
<li>
<a href="images/big/gouttelettes.jpg">
<img alt="Photo grand format de gouttelettes sur une toile d'araignée"
src="images/small/gouttelettes.png" />
</a>
</li>
<li>
<a href="images/big/rat.jpg">
<img alt="Photo grand format d'un rat des prés"
src="images/small/rat.png" />
</a>
</li>
</ul>
Nous avons maintenant notre balisage HTML définitif. Bien que laborieuse à consulter, notre galerie est fonctionnelle en l'état. Nous allons y rajouter une série d'interactions permettant d'améliorer l'expérience utilisateur dans le cas où la configuration de l'agent utilisateur le permet.
La surcouche JavaScript
Pour tirer parti des bénéfices qu'apporte jQuery, nous devons au préalable
charger la bibliothèque depuis notre document HTML. Pour ce faire, nous
allons utiliser le CDN de Google AJAX Libraries API
qui apporte son lot de commodités, mais vous pouvez naturellement héberger
la bibliothèque directement sur votre serveur si vous préférez. Importons donc
la bibliothèque en écrivant dans la section <head> de notre
document HTML:
<script src="http://ajax.googleapis.com/ajax/libs/jquery/1.3.2/jquery.min.js" type="text/javascript"></script>
Il reste à créer et lier le fichier qui contiendra le code de création de notre galerie d'images. J'ai choisi de l'appeler gallery.js mais à nouveau, vous pourriez le nommer tout autrement. Il est à noter qu'il est important de charger ce fichier après l'import de jQuery puisqu'il utilisera ce dernier pour exécuter bon nombre de méthodes.
<script src="http://ajax.googleapis.com/ajax/libs/jquery/1.3.2/jquery.min.js" type="text/javascript"></script>
<script src="gallery.js" type="text/javascript"></script>
Nous pouvons maintenant commencer à écrire le code JavaScript dans notre
fichier gallery.js.
La majorité des scripts non intrusifs doivent logiquement s'assurer que le
DOM soit chargé avant
de pouvoir le manipuler. jQuery fait ça très bien en mettant à notre
disposition l'événement ready(), remplaçant véloce du
traditionnel window.onload. De manière générale, les scripts
jQuery seront donc englobés dans la fonction suivante:
jQuery(document).ready(function(){
// Mon script
});
La fonction jQuery() étant invoquée fréquemment, un alias a été
établi permettant de remplacer son appel par $(), plus pratique
et concis. Nous pourrions donc maintenant écrire:
$(document).ready(function(){
// Mon script
});
La fonction jQuery() (ou $()) possède cette
particularité bien commode d'être d'usage différent selon le contexte. Elle
permet notamment par son simple appel de simuler de façon raccourcie le
$(document).ready() vu ci-dessus.
L'alias $ étant utilisé par de nombreuses bibliothèques, nous
allons le passer en paramètre de la fonction jQuery (nommée de
façon explicite) afin de protéger notre code d'éventuels conflits et de le
rendre par la même occasion plus robuste et portable:
jQuery(function($){
// Mon script
});
La première étape de notre script va être de générer une boîte HTML (un
paragraphe en l'occurrence) qui fera office de récepteur pour les images
grand format. Ce conteneur qu'on nommera viewer va être
généré directement après notre liste #thumbs:
jQuery(function($){
$("#thumbs").after(
$(document.createElement("p"))
.attr("id","viewer")
);
});
Voyons étape par étape la construction de ce code:
-
je récupère l'élément ayant pour identifiant thumbs
grâce au sélecteur
$("#thumbs") -
je lui applique la méthode
after()qui permet de générer un contenu après ce dernier -
ce contenu généré peut être divisé en 3 étapes:
-
je crée l'élément "p" en utilisant les méthodes classiques du
DOM:
document.createElement("p") -
j'englobe ce nouvel élément dans une fonction jQuery
(
$()) afin de le transformer en objet jQuery -
j'applique à ce nouveau paragraphe créé un identifiant viewer grâce à la méthode
attr()
-
je crée l'élément "p" en utilisant les méthodes classiques du
DOM:
Afin de ne pas mélanger le code de construction de la galerie et les paramètres personnels, nous allons définir un objet settings au début de notre code contenant:
-
thumbListIdqui représentera l'identifiant donné à notre liste de vignettes dans le code HTML -
imgViewerIdqui contiendra l'identifiant appliqué au paragraphe nouvellement créé
Le sélecteur d'identifiant $("#thumbs") ainsi que l'application
de l'identifiant viewer sur le paragraphe conteneur
peuvent dès lors faire appel à ces nouvelles variables. Nous allons au
passage créer une variable imgViewer référenciant ce
paragraphe afin d'y faire appel plus aisément par la suite:
jQuery(function($){
var settings = {
thumbListId: "thumbs",
imgViewerId: "viewer"
};
$("#"+settings.thumbListId).after(
$(document.createElement("p"))
.attr("id",settings.imgViewerId)
);
var imgViewer = $("#"+settings.imgViewerId);
});
Notre paragraphe est bien créé mais à ce stade vide de tout contenu. Vous
pouvez vous aider d'outils tels que Firebug pour observer le code source
généré.
Par défaut, nous afficherons dans ce paragraphe la première image de la
liste. Nous allons logiquement devoir commencer par trouver l'URL de cette image. Pour
ce faire, il nous faudra référencer toutes les vignettes cliquables dans un
tableau (nommé thumbLinks) et en extraire le premier élément
(firstThumbLink), situé à l'indice 0.
jQuery(function($){
var settings = {
thumbListId: "thumbs",
imgViewerId: "viewer"
};
var thumbLinks = $("#"+settings.thumbListId).find("a"),
firstThumbLink = thumbLinks.eq(0);
$("#"+settings.thumbListId).after(
$(document.createElement("p"))
.attr("id",settings.imgViewerId)
);
var imgViewer = $("#"+settings.imgViewerId);
});
Nous sommes maintenant en mesure d'afficher la première image grand format
dans notre paragraphe récepteur. Il nous suffit d'extraire le contenu de
l'attribut href de la première vignette photo et de le
renseigner en tant que valeur de l'attribut src d'un nouvel
objet image créé. Nous allons également en profiter pour faire de cette
image grand format une variable bigPic qui nous servira
plus tard.
jQuery(function($){
var settings = {
thumbListId: "thumbs",
imgViewerId: "viewer"
};
var thumbLinks = $("#"+settings.thumbListId).find("a"),
firstThumbLink = thumbLinks.eq(0);
$("#"+settings.thumbListId).after($(document.createElement("p"))
.attr("id",settings.imgViewerId)
.append(
$(document.createElement("img")).attr({
alt: "",
src: firstThumbLink.attr("href")
})
)
);
var imgViewer = $("#"+settings.imgViewerId),
bigPic = imgViewer.children("img");
});
Notre première image apparaît désormais dans #viewer par
défaut. Il nous faut maintenant nous attaquer à la partie centrale du
script: la navigation entre les différentes images.
L'événement click() permet de définir une fonction qui sera
déclenchée au clic sur chaque élément ciblé. Nous allons appliquer cette
méthode pour tous les liens contenus dans #thumbs, maintenant
définis par la propriété thumbLinks:
jQuery(function($){
var settings = {
thumbListId: "thumbs",
imgViewerId: "viewer"
};
var thumbLinks = $("#"+settings.thumbListId).find("a"),
firstThumbLink = thumbLinks.eq(0);
$("#"+settings.thumbListId).after(
$(document.createElement("p"))
.attr("id",settings.imgViewerId)
.append(
$(document.createElement("img")).attr({
alt: "",
src: firstThumbLink.attr("href")
})
)
);
var imgViewer = $("#"+settings.imgViewerId),
bigPic = imgViewer.children("img");
thumbLinks
.click(function(){
// Actions à déclencher
});
});
La première étape consiste à annuler l'événement par défaut lors du clic sur
un lien (représenté par le paramètre e) afin que le navigateur
ne suive pas la cible du lien:
jQuery(function($){
var settings = {
thumbListId: "thumbs",
imgViewerId: "viewer"
};
var thumbLinks = $("#"+settings.thumbListId).find("a"),
firstThumbLink = thumbLinks.eq(0);
$("#"+settings.thumbListId).after(
$(document.createElement("p"))
.attr("id",settings.imgViewerId)
.append(
$(document.createElement("img")).attr({
alt: "",
src: firstThumbLink.attr("href")
})
)
);
var imgViewer = $("#"+settings.imgViewerId),
bigPic = imgViewer.children("img");
thumbLinks
.click(function(e){
e.preventDefault();
});
});
L'action par défaut étant à présent annulée, nous pouvons réécrire le
comportement à adopter lors du clic sur les vignettes. Quel est donc ce
comportement? Il s'agit simplement de changer dynamiquement l'attribut
src de bigPic en fonction de la vignette
cliquée (grâce à l'attribut href). La vignette cliquée sera
référencée par l'objet $(this) lui-même stocké dans une
variable $this pour s'abstenir de parcourir plusieurs fois le
DOM inutilement. Nous testerons également si l'image cliquée n'est pas
d’ores et déjà l'image affichée afin d'éviter un rechargement inutile le cas
échéant.
jQuery(function($){
var settings = {
thumbListId: "thumbs",
imgViewerId: "viewer"
};
var thumbLinks = $("#"+settings.thumbListId).find("a"),
firstThumbLink = thumbLinks.eq(0);
$("#"+settings.thumbListId).after(
$(document.createElement("p"))
.attr("id",settings.imgViewerId)
.append(
$(document.createElement("img")).attr({
alt: "",
src: firstThumbLink.attr("href")
})
)
);
var imgViewer = $("#"+settings.imgViewerId),
bigPic = imgViewer.children("img");
thumbLinks
.click(function(e){
e.preventDefault();
var $this = $(this),
target = $this.attr("href");
if (bigPic.attr("src") == target) return;
bigPic
.attr("src",target);
});
});
Notre galerie, bien qu'en apparence fonctionnelle, pêche par l'absence de
marqueur graphique et sémantique indiquant à l'utilisateur la vignette en
cours de visualisation.
Nous allons appliquer au lien actif une classe active en
vue d'un stylage CSS
particulier. L'information ne pouvant être transmise par le seul biais de la
couleur (point
de contrôle 1.4.1, WCAG 2.0), nous ajouterons également un attribut
title explicite sur la vignette sélectionnée. Commençons par
rajouter deux propriétés à l'objet settings contenant ces
informations:
jQuery(function($){
var settings = {
thumbListId: "thumbs",
imgViewerId: "viewer",
activeClass: "active",
activeTitle: "Photo en cours de visualisation"
};
var thumbLinks = $("#"+settings.thumbListId).find("a"),
firstThumbLink = thumbLinks.eq(0);
$("#"+settings.thumbListId).after(
$(document.createElement("p"))
.attr("id",settings.imgViewerId)
.append(
$(document.createElement("img")).attr({
alt: "",
src: firstThumbLink.attr("href")
})
)
);
var imgViewer = $("#"+settings.imgViewerId),
bigPic = imgViewer.children("img");
thumbLinks
.click(function(e){
e.preventDefault();
var $this = $(this),
target = $this.attr("href");
if (bigPic.attr("src") == target) return;
bigPic
.attr("src",target);
});
});
Ces nouvelles variables définies, il faut maintenant nous en servir pour
mettre en évidence les vignettes cliquées, mais également la première
vignette photo par défaut.
Afin de ne pas se répéter inutilement, nous allons définir la fonction highlight qui appliquera à l'élément qui l'appelle tout ce
dont il a besoin pour se faire distinguer. Notons qu'il ne faudra pas
oublier d'ôter à chaque clic la classe active et le
title sur l'élément sélectionné auparavant.
jQuery(function($){
var settings = {
thumbListId: "thumbs",
imgViewerId: "viewer",
activeClass: "active",
activeTitle: "Photo en cours de visualisation"
};
var thumbLinks = $("#"+settings.thumbListId).find("a"),
firstThumbLink = thumbLinks.eq(0),
highlight = function(elt){
thumbLinks.removeClass(settings.activeClass).removeAttr("title");
elt.addClass(settings.activeClass).attr("title",settings.activeTitle);
};
highlight(firstThumbLink);
$("#"+settings.thumbListId).after(
$(document.createElement("p"))
.attr("id",settings.imgViewerId)
.append(
$(document.createElement("img")).attr({
alt: "",
src: firstThumbLink.attr("href")
})
)
);
var imgViewer = $("#"+settings.imgViewerId),
bigPic = imgViewer.children("img");
thumbLinks
.click(function(e){
e.preventDefault();
var $this = $(this),
target = $this.attr("href");
if (bigPic.attr("src") == target) return;
highlight($this);
bigPic
.attr("src",target);
});
});
Le style graphique du lien possédant la classe active n'ayant pas encore été défini, aucune différence visuelle n'est constatable. Remédions à ce problème et profitons-en pour habiller quelque peu notre galerie photo:
body {background:#222;}
img {vertical-align:middle; border:none;}
#thumbs {overflow:auto; list-style:none; margin:30px; padding:0;}
#thumbs li {float:left;}
#thumbs a {display:block; padding:10px; outline:none;}
#thumbs a:hover, #thumbs a:focus {background:#fff;}
#thumbs a.active {background:#000;}
#viewer {width:700px; height:465px; margin-left:30px;}
Le lien actif est désormais mis en évidence par un cadre noir et l'ensemble
a globalement meilleure allure.
Notre galerie est relativement convaincante et pourrait être utilisée telle
quelle. Cela dit, le manque d'indication lors du chargement d'une nouvelle
image amoindri considérablement l'expérience utilisateur. Ne nous arrêtons
pas en si bon chemin et pallions sur-le-champ cette carence ergonomique.
Nous allons utiliser un gif animé tiré du générateur ad hoc
Ajaxload pour
représenter l'icône de chargement. Le loader choisi se
présente comme tel:
Cette image n'étant appelée que lorsque JavaScript est activé, c'est via le DOM que nous allons la générer. Il nous faudra dès lors ajouter dans l'objet settings les valeurs des attributs nécessaires à la bonne formation de notre image:
jQuery(function($){
var settings = {
thumbListId: "thumbs",
imgViewerId: "viewer",
activeClass: "active",
activeTitle: "Photo en cours de visualisation",
loaderTitle: "Chargement en cours",
loaderImage: "images/loader.gif"
};
var thumbLinks = $("#"+settings.thumbListId).find("a"),
firstThumbLink = thumbLinks.eq(0),
highlight = function(elt){
thumbLinks.removeClass(settings.activeClass).removeAttr("title");
elt.addClass(settings.activeClass).attr("title",settings.activeTitle);
},
loader = $(document.createElement("img")).attr({
alt: settings.loaderTitle,
title: settings.loaderTitle,
src: settings.loaderImage
});
highlight(firstThumbLink);
$("#"+settings.thumbListId).after(
$(document.createElement("p"))
.attr("id",settings.imgViewerId)
.append(
$(document.createElement("img")).attr({
alt: "",
src: firstThumbLink.attr("href")
})
)
);
var imgViewer = $("#"+settings.imgViewerId),
bigPic = imgViewer.children("img");
thumbLinks
.click(function(e){
e.preventDefault();
var $this = $(this),
target = $this.attr("href");
if (bigPic.attr("src") == target) return;
highlight($this);
bigPic
.attr("src",target);
});
});
À ce stade, le loader a bien été créé dans le DOM mais
n'est pas encore utilisé.
jQuery met à notre disposition le gestionnaire d'événement
load() observant l'état de chargement d'un élément nommé.
Lorsque celui-ci arrive à son terme, une méthode peut être déclenchée.
À chaque clic sur une nouvelle vignette, la marche à suivre consistera en 3
étapes successives:
-
je remplace le contenu d'
imgViewerpar mon loader -
j'attache le gestionnaire d'événement à mon objet
bigPicqui insérera une fois chargée la nouvelle photographie dansimgViewer -
j'actualise la valeur de l'attribut
srcdebigPic(grâce à la variabletargetprécédemment définie) en fonction de la vignette cliquée
Ce dernier point ayant déjà été couvert, il ne nous reste plus qu'à écrire
les étapes 1 et 2. Nous utiliserons la méthode html() pour
introduire les contenus successifs dans imgViewer. Tant que
nous y sommes, et plutôt que d'altérer les images de façon brutale, nous
profiterons de l'événement fadeIn() pour faire apparaître les
photographies de manière progressive. Le temps d'exécution du fondu est
exprimé en millisecondes.
jQuery(function($){
var settings = {
thumbListId: "thumbs",
imgViewerId: "viewer",
activeClass: "active",
activeTitle: "Photo en cours de visualisation",
loaderTitle: "Chargement en cours",
loaderImage: "images/loader.gif"
};
var thumbLinks = $("#"+settings.thumbListId).find("a"),
firstThumbLink = thumbLinks.eq(0),
highlight = function(elt){
thumbLinks.removeClass(settings.activeClass).removeAttr("title");
elt.addClass(settings.activeClass).attr("title",settings.activeTitle);
},
loader = $(document.createElement("img")).attr({
alt: settings.loaderTitle,
title: settings.loaderTitle,
src: settings.loaderImage
});
highlight(firstThumbLink);
$("#"+settings.thumbListId).after(
$(document.createElement("p"))
.attr("id",settings.imgViewerId)
.append(
$(document.createElement("img")).attr({
alt: "",
src: firstThumbLink.attr("href")
})
)
);
var imgViewer = $("#"+settings.imgViewerId),
bigPic = imgViewer.children("img");
thumbLinks
.click(function(e){
e.preventDefault();
var $this = $(this),
target = $this.attr("href");
if (bigPic.attr("src") == target) return;
highlight($this);
imgViewer.html(loader);
bigPic
.load(function(){
imgViewer.html($(this).fadeIn(250));
})
.attr("src",target);
});
});
L'icône de chargement apparaît bien tant que l'UA n'a pas fini de récupérer l'image mais elle mériterait
d'être positionnée plus élégamment. Nous allons centrer le loader verticalement et horizontalement grâce à un
positionnement absolu par rapport à imgViewer qui servira de
référent relatif. Ce loader peut être facilement
ciblé grâce au sélecteur de sous-chaîne dans la valeur de l'attribut
src. Notons que ce sélecteur
d'attribut issu de CSS3 est implémenté par tous les navigateurs
modernes, Internet Explorer 7 inclus.
body {background:#222;}
img {vertical-align:middle; border:none;}
#thumbs {overflow:auto; list-style:none; margin:30px; padding:0;}
#thumbs li {float:left;}
#thumbs a {display:block; padding:10px; outline:none;}
#thumbs a:hover, #thumbs a:focus {background:#fff;}
#thumbs a.active {background:#000;}
#viewer {position:relative; width:700px; height:465px; margin-left:30px;}
#viewer img[src*="loader"] {position:absolute; left:50%; top:50%; margin:-15px 0 0 -15px;}
Cette fois, nous y sommes! Notre galerie est aboutie et prête à l'emploi.
Vous pouvez tester
un exemple en ligne du résultat final.
Pour aller plus loin, nous pourrions faire de ce script un plugin jQuery, proposer des options de configuration,
implémenter un système de navigation suivant/précédent et bien d'autres
choses encore. N'hésitez pas à améliorer ce script et, pourquoi pas, poursuivre votre
découverte de jQuery!
- Auteur: Benjamin De Cock
- Photos: Dominique Cocagne
Un problème, une question?
Vous avez des soucis avec ce tutoriel? Venez en discuter dans le salon Spécifique aux tutoriels et articles du Forum Alsacréations.








