« Prototyper » les Éléments DOM avec Internet Explorer

Vous le savez certainement, le JavaScript est un langage dit « à prototype ». Il est ainsi possible d'ajouter facilement des propriétés/méthodes à toutes les instances d'une Classe.

Les Classes pouvant être des chaînes de caractères (String), des nombres (Number), ... et aussi les éléments DOM (Element). Seulement voilà, comme à son habitude, Internet Explorer nous mets des bâtons dans les roues en empêchant le "prototypage" des éléments DOM.

Heureusement, des solutions existent, nous allons en voir une ici.

Concrètement

Nous voulons par exemple ajouter des méthodes show() et hide() qui nous permettrons d'afficher et masquer les éléments. Avec le prototypage, nous procéderons de la façon suivante :

// Ajout des méthodes aux éléments DOM

Element.prototype.show= function(){
    if (this.style.display=='none'){
        this.style.display = '';
    }
    return this;
};

Element.prototype.hide= function(){
    this.style.display = 'none';
    return this;
};

Avec la plupart des navigateurs, cela fonctionnera sans problème.
Internet Explorer par contre nous générera l'erreur suivante : 'Element' est indéfini.

Comment faire alors ?

Le problème a déja été abordé dans cet article (entre autres) et certaines suggestions très intéressantes ont étés proposées par les lecteurs.
Une autre solution consiste à implémenter les méthodes/propriétés lorsque la page est chargée (événement "load").

Nous remarquons que différentes solutions existent, chacune avec leurs avantages et inconvénients.
Partant de ce constat, j'ai tenté diverses expérimentations jusqu'à trouver une méthode qui me satisfasse.

Mon choix s'est finalement porté sur l'utilisation d'un fichier .htc qui ajoutera dynamiquement la série de méthodes à implémenter. Pour plus de sécurité, les fonctions document.getElementById(), document.getElementsByTagName() et document.createElement() seront modifiées afin de s'assurer que les éléments qu'ils retourneront implémentent bien les nouvelles méthodes.

Le code

La page de test :

<html>
<head>
    <title>Event.prototype avec IE</title>

    <script type="text/javascript">
    Element = window.Element || function(){};

    Element.prototype.show= function(){
        if (this.style.display=='none'){
            this.style.display = '';
        }
        return this;
    };

    Element.prototype.hide= function(){
        this.style.display = 'none';
        return this;
    };
    </script>

    <!--[if IE]>
    <style type="text/css">
    * { behavior:url(prototype.htc) }
    </style>
    <![endif]-->
</head>
<body>

<p>
    <input type="button" value="Hide" onclick="document.getElementById('test').hide();" />
    <input type="button" value="Show" onclick="document.getElementById('test').show();" />
</p>

<p id="test">Cliquez sur les boutons ci-dessus pour me masquer / m'afficher.</p>

</body>
</html>

Le fichier prototype.htc :

<PUBLIC:COMPONENT>
<script type="text/javascript">

/*
 * Prototypage
 */
window.__prototypize = function(element){
    if (element && !element.__prototyped && window.Element && window.Element.prototype){
        element.__prototyped = true;
        for (var key in window.Element.prototype){
            if (typeof(element[key])=='undefined'){
                try {
                    element[key] = window.Element.prototype[key];
                } catch(e) {}
            }
        }
    }
    return element;
}
__prototypize(this);

/*
 * Pour éviter des problèmes éventuels, on vérifie avec les fonctions
 * les plus courantes que les éléments ont bien été "prototypés"
 */
if (!window.__getElementById){
    window.__getElementById = window.document.getElementById;
    window.document.getElementById = function(element){
        element = window.__getElementById.call(window.document, element);
        element = window.__prototypize(element);
        return element;
    }

    window.__getElementsByTagName = window.document.getElementsByTagName;
    window.document.getElementsByTagName = function(tagName){
        var elements = window.__getElementsByTagName.call(window.document, tagName);
        for(var i=0, element; element=elements[i]; i++){
            element = window.__prototypize(element);
        }
        return elements;
    }

    window.__createElement = window.document.createElement;
    window.document.createElement = function(tagName){
        var element = window.__createElement.call(window.document, tagName);
        element = window.__prototypize(element);
        return element;
    }
}

</script>
</PUBLIC:COMPONENT>

Demo (testez-la avec Internet Explorer)

Et les librairies JavaScript ?

Vous vous demandez peut-être pourquoi vouloir je cherche encore à faire cela alors que des librairies JavaScript (JQuery, Mootools pour ne citer qu'eux) le prennent en charge.
Je vous répondrai simplement que je trouve dommage de sortir la "grosse artillerie" pour des projets qui n'utilisent que quelques unes de ces fonctionnalités. J'ai déja vu des sites charger près de 100ko de code javascript juste pour un bouton "scroll to top"

Si vous avez des questions, n'hésitez pas à m'en faire part. Je me ferai un plaisir d'y répondre ;)

comments powered by Disqus