DOM ou le parcours du combattant
Toujours plongé ces dernières semaines dans ce magnifique outil qu’est le DOM, j’ai de nouveau été confronté aux insuffisances de divers navigateurs. Il est vraiment dommage que IE fasse bande à part, hormis pour une grande partie du module Core. Cela complique réellement la tâche du développeur et l’oblige à réaliser toute une série de tests pour obtenir une compatibilité avec tous les navigateurs du marché.
Comme je le disais, IE (surtout la version 6.x) reconnait, à peu de choses près, l’ensemble du module Core. Là où il diverge passablement des normes, c’est avec les modules d’évènements et de styles (je ne me suis pas encore attaqué aux modules de vues et de traversée). Dans ces deux cas, Microsoft a défini ses propres objets, méthodes et propriétés, quand il en a défini, ce qui n’est pas toujours le cas.
Définir ses propres méthodes
Mon objectif est simple : Pas de code propriétaire dans mes scripts. Je me suis donc mis en tête de créer un script ajoutant les méthodes standard manquantes dans IE. Exemple :
if( !window.addEventListener )
{
window.addEventListener = function(eventType, listener, useCapture) {
this.attachEvent("on" + eventType, listener);
}
}
Un petit problème cependant, il me faut faire cela pour chaque élément du document, ce qui m’impose de faire une boucle qui traitera chaqu’un de ces éléments. Un peu lourd… D’autant plus que ce n’est pas la seule boucle, il y a d’autres objets à "normaliser". J’ai donc commencé à chercher un moyen d'ajouter automatiquement une méthode à toutes les instances d’un objet, m’appuyant sur un passage de la documentation sur JavaScript 1.5 dont voici un extrait :
If you add a new property to an object that is being used as the
prototype for a constructor function, you add that property to all
objects that inherit properties from the prototype. For example,
you can add a specialty property to all employees with the
following statement :
Employee.prototype.specialty = "none";
Mais évidemment, car cela eût été trop simple, cela n'a pas fonctionné, ou du moins de la façon dont j’ai testé:
if( !window.addEventListener )
{
document.getElementById('body').prototype.addEventListener = function(eventType, listener, useCapture) {
this.attachEvent("on" + eventType, listener);
}
}
Propriété inexistante… Puis je suis tombé sur cette page:
The prototype object of JavaScript 1.1 (cette page et la suivante).
Cet objet prototype
existe donc depuis belle lurette,
les tests sont d’ailleurs concluants, de plus, l’exemple de la dernière
page me confirme qu’il est possible d’ajouter une méthode à un objet
prédéfini, ce qui n’était pas forcément évident à la lecture de la
documentation de Netscape.
J’ai cherché longtemps. C’est simple, j’ai bien dù y perdre mon week-end. Mais j’ai fini par trouver la solution. Ahh, le pied quand on cherche inlassablement et qu’on finit enfin par trouver :) Donc pour ajouter une méthode à un objet, et qu’elle soit héritée par toutes les instances en cours et à venir de l’objet, il fallait finalement faire :
if( !window.addEventListener )
{
// Objet HTMLElement, l'objet constructeur de tous les éléments du document
HTMLElement.prototype.addEventListener = function(eventType, listener, useCapture) {
this.attachEvent("on" + eventType, listener);
}
}
// ou encore
if( !window.addEventListener )
{
// Objet 'constructor' pour accéder à l'objet constructeur
document.getElementById('body').constructor.prototype.addEventListener = function(eventType, listener, useCapture) {
this.attachEvent("on" + eventType, listener);
}
}
Les deux façons de faire fonctionnement parfaitement dans Firefox, la seconde seulement fonctionne dans Opera 7.22 et sur IE 6.x, … aucune… Autant vous dire que je suis extrèmement frustré, et ce, pour plusieurs raisons.
- Cette possibilité est vraiment interessante, pas très connue ou utilisée de ce que j’ai pu en voir cependant.
- Je tombe à nouveau sur une carence de IE alors que je cherchais à en combler une autre
Du coup, je suis à court d’idées et me suis rabattu sur les boucles. Je ne désespère pas de trouver une solution (il doit forcément y avoir un moyen d’accéder au constructeur d’un objet quel qu’il soit non ?).
Évènement souris et bouton cliqué
Alors là, c’est pire que d’habitude. Il n’y a plus deux modèles (W3C et Microsoft) pour connaitre le bouton de souris sur lequel a cliqué l’utilisateur mais trois ! Et oui, Opera fait lui aussi bande à part sur ce coup là.
Lorsqu'un évènement est lancé, on reçoit en argument de la fonction
appellée un objet contenant diverses informations sur l’évènement
enclenché. Ça, c’est pour le modèle du
W3C.
Chez microsoft, on dispose d’un objet similaire (du moins dans son
origine) mais il se trouve rattaché à l’objet window
.
Bref, cet objet a une propriété button
contenant le numéro
du bouton de la souris qui a été pressé. Voici la valeur de cette
propriété selon les différents modèles existants :
W3C | Microsoft | Opera | |
---|---|---|---|
Bouton gauche | 0 | 1 | 1 |
Bouton milieu | 1 | 4 | 3 |
Bouton droite | 2 | 2 | 2 |
Mhhh…, difficile de s’y retrouver n’est ce pas ? Il y a une autre
propriété interessante dans l'objet event
, c’est la propriété
which
, présente uniquement sur Mozilla, Safari et Opera.
Pour un évènement souris adèquat, elle contient la même valeur que
button
sous Safari et Opera, sous Mozilla, cette valeur est
incrémentée de un. En mixant tout ça, je pense être parvenu à une solution
adéquat pour obtenir la même valeur sous ces trois navigateurs ainsi que
sous IE.
Si ça peut aider :
var button = event.button;
if( event.button != 0 && event.button != 2 && ( typeof(event.which) == 'undefined' || event.which > 0 ) )
{
if( typeof(event.which) != 'undefined' )// Mozilla, Safari et Opera
{
button = event.which;
}
button--;
if( button == 2 || button == 3 )// Correction Opera et MS
{
button = 1;
}
}