Le DOM est une méthode pour
représenter un document sous forme d'objets arborescents. Cette méthode est par
exemple utilisée pour manipuler des documents XML ou des pages web en HTML.
Plusieurs spécifications ont défini le modèle, des méthodes pour y accéder, le
modifier, etc. L'une d'elles
permet de gérer des évènements survenant sur les objets. Malheureusement, j'ai
toujours trouvé cette spécification étonnamment incomplète. Elle présente à mon
sens 2 lacunes:
- elle ne permet pas de connaître les guetteurs associés à un événement
- elle ne définit pas, ou mal, ce qui doit arriver à ces guetteurs lors de
manipulations du document.
La méthode de gestion retenue est en effet de permettre d'attacher à chaque
objet des guetteurs qui seront appelés quand surviendra un événement.
Malheureusement, la spécification ne dit pas ce qui doit arriver à ces
gestionnaires quand l'objet est déplacé, par exemple dans un autre document, ou
dupliqué. Chaque implémentation de la spécification a donc dû décider de
l'attitude à adopter dans ces cas. Dans le cas de Firefox, les guetteurs sont
perdus en cas de déplacement du nœud dans un autre document (par exemple
lorsqu'on essaie d'insérer dans une fenêtre popup des éléments générés dans la
fenêtre mère), ou lorsqu'on le copie (la spécification indique effectivement
quand un objet Node est recopié en utilisant la méthode cloneNode, les
guetteurs EventListener attachés à l'objet Node source ne le sont pas sur
l'objet Node copié
, cf la
définition des guetteurs.
Petit exemple: imaginons que l'on ait besoin de créer dynamiquement un
formulaire permettant à l'utilisateur de saisir un nombre quelconque de
réponses, chaque réponse étant l'objet d'une validation. Cela pourrait se coder
ainsi:
<!-- Création du formulaire -->
<form id="my_form">
<input type="button" id="button_add" value="ajouter" />
</form>
<script type="text/javascript">
function init(){
// on crée une ligne permettant la saisie
var _div = document.createElement("div");
var _input = document.createElement("input");
_input.setAttribute("type", "text");
// on attache un guetteur qui affiche la saisie quand le curseur quitte la zone
_input.addEventListener('blur', function(){alert("la valeur saisie est : " + this.value);}, false);
_div.appendChild(_input);
// on insère la première ligne
document.getElementById("my_form").appendChild(_div);
// chaque pression sur le bouton insérera une nouvelle ligne
document.getElementById("button_add").addEventListener('click', function(){document.getElementById("my_form").appendChild(_div)}, false);
}
</script>
Ce code ne fonctionnera pas. En cliquant sur le bouton "Ajouter", on insère
bien une copie de la première ligne, mais le guetteur censé valider la saisie
n'a pas été recopié par le cloneNode.
Cet exemple est bien sûr trivial, il suffirait, après le clonage, d'ajouter
un nouveau guetteur sur le champs de saisie. Mais cela suppose de connaître
tous les guetteurs qui ont été associés, potentiellement par d'autres portions
de code, au fragment cloné. En l'absence d'interface pour connaître ces
guetteurs. le problème est difficile à résoudre. C'est pour le contourner que
je me suis enfin intéressé à XBL, dont je vous parlerai dans les prochains
jours (ce billet ne servait qu'à liquider ce problème auquel je me suis heurté
vainement pendant pas mal de temps en codant Couac )
(toute ceci ne concerne que Firefox. Il parait que les récentes versions
du malware au 'e' bleuté dupliquent les guetteurs lors d'un cloneNode. Je n'ai
pas été vérifier)