Comment faire des rollovers (des images qui changent au survol de la souris) ?

Au départ, il s'agit simplement de d'obtenir qu'une image portant un lien change au passage de la souris. On va ensuite compliquer et généraliser le concept :

Rollover simple sur une image cliquable. Préchargement

L'image change sous la souris, puis revient à son état initial quand la souris s'en va.
On profite du fait que les images d'une page HTML sont des objets tout à fait identifiables au sens du javascript. Il s'agit d'objets de type Image (évidemment !), rangés dans un tableau document.images[ ] numéroté à partir de 0 dans l'ordre d'apparition des images dans la page. On pourrait donc repérer les images par leur numéro, mais il est beaucoup plus commode de leur attribuer un nom au moyen d'un attribut NAME dans la balise qui provoque l'affichage de l'image :
<IMG SRC="image.jpg" NAME="bouton" WIDTH=... >
Ce nom est tout à fait arbitraire (enfin, presque... n'exagérez pas trop). Son intérêt est qu'il peut servir d'index symbolique dans le tableau des images, c'est-à-dire que document.images['bouton'] sera précisément l'objet Image associé à cette image. Notez les quotes de part et d'autre du nom, destinées à rappeler à Javascript que bouton n'est qu'une chaine de caractères et non pas une variable.
Le contenu d'un objet Image s'obtient en accolant .src à son nom (on dit qu'on accède à sa propriété «src»). L'instruction
document.image['bouton'].src='image_2.jpg' 
va tout simplement provoquer le chargement d'un autre fichier image dans ce contenu et donc substituer une nouvelle image à l'ancienne.
Pour avoir le fonctionnement en rollover, on déclenchera cette substitution lors de l'événement « entrée de la souris sur le lien », puis on déclenchera la restauration de l'image lors de l'événement « départ de la souris », ces deux événements étant gérés respectivement par les gestionnaires d'événements onMouseOver et onMouseOut. Le code pourrait avoir la structure suivante:
 <A HREF="fichier à charger" 
   onMouseOver="change_0('bouton','nouvelle image.jpg');" 
   onMouseOut="change_0('bouton','ancienne image.jpg');"><IMG 
   NAME="bouton" SRC="ancienne image.jpg" etc...></A>
où la fonction Javascript change_0() serait ainsi définie :
<script language="Javascript">  
    function change_0(nom,fichier) { 
       if (document.images) document.images[nom].src=fichier
    } 
</script>
Plusieurs remarques pour les débutants en javascript :
(i) noter que les arguments de cette fonction sont des chaînes de caractères;
(ii) le test if(document.images) vérifie simplement que le tableau des images existe. Il n'est destiné qu'à éviter les messages d'erreur pour les vieux navigateurs qui ne comprendraient pas ce code (IE3, NN2 et antérieurs);
(iii) cette fonction exécute simplement l'intruction javascript précédente, c'est à dire, par exemple, que l'exécution du onMouseOver déclenche
document.image['bouton'].src='nouvelle image.jpg'
Cependant, au premier survol par la souris, l'application directe du code précédent provoquerait une demande de chargement de la nouvelle image depuis le serveur. Ce n'est pas instantané. On attendra que la demande soit honorée, puis le disque dur va se mettre à tourner, et rien ne se passera sur l'écran pendant tout ce temps. Ce n'est qu'au 2ème survol que le chargement se fera à partir du cache, donc beaucoup plus vite, et que le rollover va réellement fonctionner. On a l'impression désagréable que ça marche quand ça veut...
Pour améliorer ce fonctionnement, on va précharger les deux images du rollover, via un script
<script language="JavaScript">     
    var bouton_0 = new Image(); bouton_0.src = "pic_repos.jpg"
    var bouton_1 = new Image(); bouton_1.src = "pic_actif.jpg"
</script> 
à placer dans le bloc <HEAD> de la page. Ce script crée 2 nouveaux objets Images avec les noms bouton_0 et bouton_1 (on aurait pu choisir d'autres noms) et il charge les images dans leurs « contenus » respectifs. Les images correspondantes ne sont pas visibles parce que ces contenus ne sont pas dans la mémoire vidéo. Pour changer l'image affichée, il suffira d'échanger le contenu de son objet Image avec celui d'un des deux objets qui viennet d'être définis. Par exemple, l'intruction
document.images('bouton').src=bouton_1.src
ferait apparaître l'image "pic_actif.jpg". Le rollover sera donc créé par les lignes suivantes
  <A HREF="fichier à charger"  
  onMouseOver="change('bouton', bouton_1)" 
  onMouseOut = "change('bouton', bouton_0)"><IMG 
  NAME="bouton" SRC="pic_repos.jpg" etc...></A> 
qui font intervenir une nouvelle fonction javascript change() légèrement différente de la fonction précédente :
  <script language="Javascript">  
  function change(nom,obj_im) {   
    if (document.images) document.images[nom].src=obj_im.src 
  } 
  </script>
Pour les débutants en javascript : dans les arguments de la fonction, nom est une chaîne de caractères et il faut donc mettre des quotes au moment de l'appel de la fonction (d'où le « 'bouton' » dans cet appel), tandis que obj_im est une variable de Javascript de type Image, dont le script va invoquer la propriété «src» (donc, pas de quotes lors de l'appel).
démo Et ca marche ! Passez la souris sur la figure à gauche pour voir... Si vous cliquez, le popup vous donnera le code d'une page complète pour ce rollover — en fait rien d'autre qu'une mise au clair de ce qui précède.

Rollovers sur une image à liens multiples

Sur une image à liens multiples («image-map»), on placera tout simplement les gestionnaires d'évènements onMouseOver et onMouseOut dans les liens de l'image, c'est-à-dire dans les lignes AREA SHAPE du code :
    <AREA SHAPE="rect" COORDS="..." ALT="..."
    onMouseOver="....."  onMouseOut="...."> 

Rollovers déportés

Dans les codes précédents, on fait des substitutions dans le contenu d'un seul objet-image. Mais rien n'interdit d'aller faire joujou avec une 2ème image quand on en survole une autre. On arrive au fonctionnement ci-dessous (survolez le bouton actif, à droite ; si vous cliquez le popup vous donnera l'ensemble du code).
image déportée démo On peut appliquer ce mécanisme pour donner des explications sur le rôle d'un bouton, comme une sorte d'alternative aux bulles d'aide.

Cet effet s'obtient :
(1) en préchargeant les deux images déportées (l'image de base et l'image activée), dans des objets Image dep_0 et dep_1 (les noms sont bien sûr arbitraires)
(2) en donnant un nom à l'image déportée lors de son invocation dans la page :
   <IMG SRC="image_deport.gif" NAME="expli"  WIDTH=...> 
(3) et en demandant tout simplement un 2ème passage de la fonction change() sur l'objet-image 'expli' :
	 onMouseOver = 
	    "change('bouton', bouton_1); change('expli', dep_1)"
	 onMouseOut = 
	    "change('bouton', bouton_0); change('expli', dep_0)"
	 
On peut supprimer le premier appel à change() si on ne veut pas d'effet rollover sur le bouton lui-même. Rappelons que vous aurez le code complet en allant cliquer sur le bouton ci-dessus.
Quand la page est découpée en plusieurs cadres, on peut aussi agir depuis un cadre vers un autre, au prix d'une petite complication dans la désignation des images, expliquée ci-après.

Rollovers sans lien cliquable

démo Si vous avez un Explorer 4 (ou plus) ou NN6, le bouton de gauche, qui ne porte aucun lien, devrait se comporter comme un rollover simple. En effet, si les événements de survol par la souris étaient détectés au passage sur un objet-lien dans tout ce qui précède, Explorer peut aussi les détecter au passage sur un objet-image. Il suffit tout simplement de faire rentrer les instructions précédentes onMouseOver=... et onMouseOut=... à l'intérieur de la balise <IMG> :
 
   <IMG NAME="bouton" SRC="pic_repos.jpg" WIDTH=... 
    onMouseOver="change('bout3', bouton_1)"
    onMouseOut = "change('bout3' ,bouton_0)" > 
   
Cela ne fonctionne pas sur les versions antérieures de NN ou IE.

Effet de rollover sur du texte

Toujours avec IE4+ ou NN6, on peut faire qu'un simple lien texte se comporte comme un rollover à 2 états, c.à.d. change de couleur ou passe en gras, etc..., en HTML 4, via une feuille de style. Par exemple, le code ci-dessous fait passer le lien en rouge, en gras, en gros et supprime le soulignement (hein, tant qu'à faire :-)) :
<STYLE> 
 <!--  
  a:hover{color:red; font-weight:bold; 
     font-size:20px; text-decoration:none;}
  --> 
  </STYLE>
	
Si vous voulez des liens avec des comportements différents au sein d'une même page, il suffira de définir plusieurs classes de liens.
Hélas ! Ça ne marche pas avec NN3 ou NN4x :-((.

Rollovers à 3 ou 4 états

démo On peut encore compliquer notre lien-rollover simple en lui ajoutant une nouvelle forme quand on a cliqué sur l'image. Il suffit de détecter l'événement onClick et de changer alors l'image :
 
   <A HREF="fichier à charger" 
     onMouseOver="change('bouton' ,bouton_1)"
     onMouseOut = "change('bouton', bouton_0)" 
     onClick="change('bouton', bouton_2)"><IMG 
     NAME="bouton" SRC="pic_repos.jpg" etc...></A>
   
l'objet-image bouton_2 et son contenu ayant été préalablement définis lors du préchargement.
On voit tout de suite une application possible : dans un menu où chaque entrée serait commandée par un rollover, on pourrait visualiser l'entrée qui vient d'être activée... à condition que la 3ème image reste en place quand la souris quitte le bouton, ce qui n'est jamais le cas dans la démonstration ci-dessus. Ce qui se passe est que l'événement onMouseOut est détecté dès que la souris quitte le bouton et que le code précédent restaure alors l'image de base du bouton. On peut éviter cet effet en substituant l'image active à l'image de base lors du click, via un
 
 	 onClick = 
	 "change('bouton', bouton_2); bouton_0.src=bouton_2.src"
	 
mais on ne fait que déplacer le problème  le bouton resterait ainsi figé dans l'état activé, alors qu'il faudrait le remettre dans son état de base quand on aurait cliqué sur un autre bouton — ou bien dans un 4ème état, de lien « visité ». Tout cela peut s'arranger avec des scripts à peine plus complexes.
Cliquez ici pour voir une démonstration d'un menu à 3 boutons en rollovers à 3 états. Nous suggérons d'examiner le code et ses commentaires pour étudier le script.

Rollovers trans-cadre (ou «cross-frame»)

On voudrait parfois faire un rollover déporté d'un cadre sur un autre, c'est-à-dire avoir dans un cadre un bouton actif qui agisse sur une image dans un autre cadre.
En fait, tous les objets Javascripts sont enchaînés les uns aux autres selon une hiérarchie qui remonte à l'objet Window correspondant à la totalité de la fenêtre du navigateur, l'objet top. On trouve ensuite les objets Window correspondant aux différents cadres, qui forment le tableau top.frames[ ], puis enfin les objets Document correspondant aux pages affichées dans les différents cadres. La dénomination complète du contenu de l'objet associé à l'image nommée «cible» dans le cadre «cadre_2» serait
	 top.frames['cadre_2'].document.images['cible'].src
	 
Tant qu'on ne travaille qu'à l'intérieur de la fenêtre courante, on peut omettre la partie gauche de cette dénomaination, qui rappelle simplement dans quelle fenêtre on se trouve. Mais il sera évidemment nécessaire d'être plus précis quand on voudra agir depuis un cadre sur un autre.
La dénomination complète des variables javascript créées dans les pages HTML est un peu différente. Par exemple si on a défini un objet Image au moyen d'une variable obj_im dans le cadre «cadre_2», on accèdera à son contenu par l'expression
	 top.frames['cadre_2'].document.obj_im.src
Notez la différence avec la structure de l'expression précédente : obj_im apparaît comme une propriété (au sens du javascript) directe de l'objet document, au même titre que le tableau images[] des diférentes images affichées.
On obtient ainsi la recette pour agir depuis un cadre vers un autre. Par exemple, imaginons qu'on veuille agir dans un cadre «droite» et y remplacer le contenu d'une image nommée «cible» (c.à.d., via un NAME="cible"). Si on veut directement charger un fichier, le gestionnaire onMouseOver devra exécuter
top.frames['droite'].document.images['cible'].src='image_no2.jpg'
ou bien, si on a déjà préchargé ce fichier dans une variable img2 de type Image dans la page affichée dans le cadre «droite» :
top.frames['droite'].document.images['cible'].src =    
              top.frames['droite'].img2.src
    
Cliquez ici pour voir une démonstration.

Quelques remarques pour finir

1 — Par précaution, veillez à ce que toutes les images aient les mêmes dimensions. En principe, les images de substitution devraient être mises aux dimensions de l'image initialement chargée dans le tag <IMG...> , mais... certaines versions des navigateurs ont mangé la consigne et on peut avoir des résultats imprévus.
2 — Attention à la multiplication du nombre des fichiers ! Si votre menu compte 14 entrées, ça fait au moins 28 fichiers et même 42 si vous optez pour des rollovers à 3 états. Même si les fichiers sont tout petits, ça engendre beaucoup d'appels entre le visiteur et le serveur, et ça prend du temps...
3 — Lors du codage, attention à ne pas donner deux fois le même NAME. Ça ne fonctionnerait pas du tout comme vous l'espérez.
4 — En HTML 4, on devrait pouvoir remplacer NAME par ID. Ça fonctionne d'ores et déjà avec IE4+, mais toujours pas avec NN. On en reparlera donc plus tard. Evidemment, ce recours aux ID interdirait le fonctionnement actuel avec NN3.
5 — Dans les manuels de Javascript, on précise que les gestionnaires d'événements (ici, OnMouseOver, OnMouseOut ou OnClick) sont toujours supposés renvoyer une valeur true ou false. Cela implique d'ajouter un return true ou return false à la fin de leurs instructions Javascript.
Par exemple, Nigel McFarlane, dans Le guide du programmeur Javascript, Eyrolles 1999, écrit que « Il reste que ce n'est pas une obligation absolue et que ce n'est pas toujours nécessaire. C'est malgré tout une bonne habitude à prendre ». Mais ledit MacFarlane est loin d'obéir à son précepte dans les différents exemples de son bouquin. Alors ?
Alors, faites l'expérience, mettez l'un ou mettez l'autre, et voyez avec NN et avec IE. Selon notre expérience, cela n'a aucun effet dans les scripts précédents, sauf avec le onMouseOver pour lequel
 
	onMouseOver=
	"if(document.images) bouton.src=actif.src ; return true"
	
fait disparaître le lien dans la barre d'état. Apparemment, ces petits scripts sont dans le cas où ces valeurs de retour ne sont pas nécessaires.
Il y a cependant un autre cas où un return false est indispensable, quand on se sert d'un onClick sur un lien juste pour obtenir un effet de rollover sur un lien (ou tout autre chose) sans qu'on veuille exécuter le lien :
  <a href="#"   
	onClick="change... ; return false">regardez...</a>
	
Sans ce return false, le lien href="#" serait exécuté et on se retrouverait tout en haut de la page; sa présence fait que seul le onClick est exécuté.
6 — Signalons que le DHTML (c'est à dire une combinaison de HTML4, des feuilles de style et du javascript) permet d'obtenir des effets fort semblables aux effets de rollover (qui sont du pur javascript) ; nous y reviendrons ailleurs dans nos articles.
Terminons par une curiosité. Si on a nommé une image avec un NAME="bouton", pour la plupart des navigateurs actuels, tout se passe comme si on pouvait remplacer une instruction comme
	     document.images['bouton'].src = obj_im.src
	 
tout simplement par bouton.src=obj_im.src. Cela permet d'alléger sensiblement les scripts, mais cela ne figure pas dans les spécifications officielles du Javascript. Tous les grands navigateurs du moment comprennent cette syntaxe abrégée, mais il n'est pas sûr que cela se maintienne dans l'avenir. En fait, nous connaissons d'ores et déjà un navigateur alternatif (iCab, pour MacOS) pour lequel ça ne marche pas.

Charles — Patrice — Gérald — JMPlus — (TOM)