fév.
2009
Désactiver les liens hypertextes avec JavaScript
Lorsque, dans une page Web, l'utilisateur déclenche une action qui modifie toute la page, il est de bon ton de s'assurer qu'il ne déclenche pas une autre action avant que la page suivante ne soit chargée.
Tous les éléments qui permettent à l'utilisateur d'agir sur la page doivent donc être désactivés. Parmi ceux-ci, il y a les champs de formulaires (champs texte, boutons-poussoir, boutons radio, cases à cocher, ...), et les liens hypertextes.
Si désactiver les champs de formulaires est assez facile en JavaScript (tous possèdent un attribut disabled
, qu'il suffit de mettre à true
), désactiver les liens est une autre paire de manches ! Il y a bien un attribut disabled
sur les ancres, mais c'est une spécificité, non standard, d'Internet Explorer. Et en plus cet attribut ne modifie que l'affichage : avec IE, un hyperlien dans l'état "disabled" est toujours actif !
Ce constat mène assez rapidement à la question suivante : comment faire ? Et d'ailleurs, est-ce faisable ?
La réponse est simple : c'est faisable, mais il va falloir écrire une fonction JavaScript ad hoc.
Cette fonction va, non seulement griser le texte du lien, en gérant les différences entre navigateurs (et profiter, sur IE, du bel effet d'ombrage que l'on obtient par un disabled=true
), mais aussi se charger de désactiver le lien lui-même, pour que le clic ou le double-clic n'ait plus aucun effet sur celui-ci.
Pour ce faire, il va falloir éliminer l'attribut href
de l'ancre, car c'est la présence de cet attribut qui fait de l'ancre un lien hypertexte. Mais il va falloir aussi penser à éliminer le code de l'événement onclick
, si celui-ci est défini !
Enfin, il va falloir sauvegarder toutes les valeurs que l'on modifie, pour pouvoir les restaurer au cas où l'on souhaiterait, plus tard, réactiver l'ancre.
La fonction ci-dessous fait tout cela :
function disableHyperLink(obj, disable) { var href, onclick, color; if (disable) { // Protect against multiple deactivations var href_bak = element.getAttribute("href_bak"); if (href_bak && href_bak != null) { return; } // Deactivate the hyperlink and onclick event and // save them for future reactivation onclick = obj.getAttribute("onclick"); href = (obj.nodeName.toLowerCase() == 'a') ? obj.getAttribute("href") : null; if (onclick && onclick != null) { obj.setAttribute("onclick_bak", onclick); obj.setAttribute("onclick", function() { return false; }); } if (href && href != null) { obj.setAttribute("href_bak", href); obj.removeAttribute("href"); //(1) // Gray the link text if (navigator.appName == "Microsoft Internet Explorer") { obj.disabled = true; // Non standard } else { color = obj.style.color; if (color == "") { color = "(null)"; } obj.setAttribute("color_bak", color); obj.style.color = "gray"; } } } else { // Reactivate the hyperlink and onclick event using // saved link address and onclick event onclick = obj.getAttribute("onclick_bak"); href = obj.getAttribute("href_bak"); if (onclick && onclick != null) { obj.setAttribute("onclick", onclick); } obj.removeAttribute("onclick_bak"); if (href && href != null) { obj.setAttribute("href", href); // Ungray the link text if (navigator.appName == "Microsoft Internet Explorer") { obj.disabled = false; // Non standard } else { color = obj.getAttribute("color_bak"); if (color && color != null) { obj.style.color = ((color == "(null)") ? "" : color); } obj.removeAttribute("color_bak"); } } obj.removeAttribute("href_bak"); } }
Comme on peut voir, cette fonction n'est pas triviale !
Son code mérite quelques explications :
- Cette fonction attend un élément de l'arbre DOM de la page (le lien hypertexte à modifier) et un booléen qui est à
true
pour désactiver le lien ou àfalse
pour le réactiver. Elle fonctionne avec des éléments autres que des hyperliens (comme les images par exemple) sur lesquels on aurait défini un attributonclick
; mais dans ce cas, il ne faut plus s'occuper de l'attributhref
(attention, avec Internet Explorer, les images ont un attribut implicitehref
qui a la même valeur que l'attributsrc
!). - Cette fonction travaille en 2 phases : désactiver/réactiver le caractère hypertexte du lien, en supprimant les attributs
href
etonclick
; puis griser/dégriser le texte du lien, en modifiant la couleur dans le style CSS de l'ancre. - Cette fonction sauvegarde les 3 valeurs modifiées ou supprimées dans des attributs
href_bak
etonclick_bak
etcolor_bak
(que l'on peut voir avec Firebug pour Firefox, par exemple). Elle utilise ces 3 attributs de sauvegarde pour pouvoir restaurer les attributshref
etonclick
etstyle.color
au moment de réactiver le lien.
Puisque c'est la présence d'un attribut href
qui fait qu'une ancre (balise <a />
) devient un lien hypertexte, notre fonction supprime l'attribut href
lorsqu'elle désactive le lien. C'est très efficace, mais certains trouveront peut-être dommage que l'ex-lien ne puisse plus être différencié du tout du texte brut.
Dans ce cas, on peut remplacer la ligne obj.removeAttribute("href");
par celle-ci : obj.setAttribute("href", "#");
(dans le code ci-dessus, cette ligne est marquée par un //(1)
pour pouvoir la retrouver plus facilement). Le lien reste alors hypertexte une fois qu'il a été désactivé, et le curseur de la souris change toujours en passant au dessus de lui, même si cliquer sur le lien reste toujours sans effet.
Une dernière petite question : comment appliquer facilement ce code à toutes les ancres d'une page ?
Facile, me direz-vous ! L'API DOM de JavaScript offre tout ce qu'il faut :
var disable = true; var allAnchors = document.getElementsByTagName("a"); for (var i = 0; i < allAnchors.length; i++) { disableHyperLink(allAnchors[i], disable); }
Commentaires
Durant mon expérience professionnelle, j'ai rencontré cette problématique également.
Dans deux des applications web, nous avions une barre de boutons (images avec un lien hypertext), nous changions donc le contenu de cette barre bar pour enlever les boutons. Néanmoins ça ne désactivait pas les actions javascripts des autres champs, mais à l'époque Ajax n'était pas trop présent.
Dans un autre projet, lorsqu'une action modifiait la page, nous affichions un calque semi-transparent au dessus de tous les éléments (division avec un très grand z-index et un alpha) ce qui empêchait l'accès aux éléments de la page. Mais cela ne fonctionnait pas avec les listes déroulantes sous IE qui ont sont toujours affichées devant les calques.