Extension des éléments du DOM

26 décembre 2017 / Marouen Mhiri / Javascript

Au cours du développement d'un site ou d'une application web, il s'avère parfois très pratique de vouloir utiliser ses propres méthodes avec les éléments du DOM. Un exemple très élégant est jQuery. Dans cet article je vais vous montrer les méthodes possibles pour réussir cette extension puis je vais essayer de les évaluer.

Méthode n°1 - Étendre "Element" : pour commencer on va expliquer ce qui ce passe lorsqu'on essaye de créer un élément DOM en Javascript en utilisant par exemple:

                
var box = document.createElement('DIV');
                
            

Ce processus de création se fait comme suit :

                
document.createElement('DIV');
|
|_ HTMLDIVElement.prototype
  	^
		|_ HTMLElement.prototype
  		^
			|_ Element.prototype
  			^
				|_ Node.prototype
  				^
					|_ Object.prototype
  					^
						|_ null
                
            

On voit ainsi que si on veut étendre les éléments 'DIV' du DOM on a le choix d'utiliser le prototype que ce soit de HTMLDIVElement, HTMLElement, Element ou Node.
Si on veut par exemple étendre l'élément DIV dans ce cas on pourrait utiliser le prototype HTMLDIVElement.prototype comme suit:

				
HTMLDIVElement.prototype.toggle = function () {
	this.style.display = this.style.display === 'none' ? 'block' : 'none';
}
document.querySelector('div.classname').toggle(); // ceci va effectuer le toggle sur le div séléctionné
document.querySelector('p').toggle(); // ceci ne va pas fonctionner
				
			

Dans cet exemple la ligne de code "document.querySelector('p').toggle();" ne donne pas de résultat car la fonction toggle est seulement définit pour les éléments de type 'DIV'.
Donc si on veut que tous les éléments aient cette fonction, on pourrait utiliser le prototype "Element.prototype ". C'est plus sûre.

				
Element.prototype.toggle = function () {
	this.style.display = this.style.display === 'none' ? 'inline-block' : 'none';
}								
				
			

Ainsi on a pu ajouter une méthode à "tous" les éléments du DOM.
ATTENTION: là on vient d'étendre le prototype de "Element". Cela ne comprend pas les élément de type texte et commentaire

				
document.createTextNode('foo'); // < Text.prototype < CharacterData.prototype < Node.prototype
				
			

Donc un élément de type texte a pour parent CharacterData, du moment si on veut ajouter notre finction toggle aussi à tous les éléments il faut l'appliquer au prototype de "Node".

ATTENTION: Internet Explorer 7 n'expose pas ses variables globales Node, Element, HTMLElement et HTMLParagraphElement 🤨.

On voit ainsi que, jouer directement sur les prototypes des éléments du DOM n'est pas vraiment la bonne solution pour ajouter de nouvelles propriétés et méthodes. Pour plus d'informations sur les inconvénients de cette méthode, je vous conseille de consulter cette page.

Recours à l'Objet wrapper

JQuery utilise une méthode simple mais aussi très efficace et sure.
Il s'agit d'inventer un objet à qui on peut associer des propriétés et des méthodes sans avoir peur des collisions ou qu'un Browser ne le supporte pas.
Voyons comment ça se passe:

				
;(function(){
  
  "use strict";

  /* DOM */
  // On définit notre wrapper, qu'on va appeler DOM. C'est notre Constructor.
  // Il reçoit "el" (un ou plusieurs éléments DOM) comme paramètre
  // on définit la propriété elements qui génère un tableau contenant les éléments
  // du DOM
  function DOM(el) {
    this.elements = el.length ? Array.prototype.slice.call(el) : [el];
  }

  // On étend notre Wrapper pour contenir les méthodes suivantes:
  DOM.prototype = {
    // La méthode scale fait un zoom d'un certain élément en ayant un 
    //facteur de zoom comme paramètre
    scale: function (factor) {
      this.elements.map(function(element, index) {
        element.style.height = factor * parseInt(window.getComputedStyle(element).height, 10) + 'px';
        element.style.width = factor * parseInt(window.getComputedStyle(element).width, 10) + 'px';
      });
      return this; // Ceci est important pour l'enchainement des méthodes 
                  // (on veut par exemple El.method1(..).methode2(..) etc...)
    },
    
    // La méthode css définit le style d'un élément tout comme jQuery. 
    // Dans ce cas le paramètre style est un objet de propriétés CSS
    css: function (styles) {
      this.elements.map(function(element, index) {
        for (var prop in styles) {
          if (styles.hasOwnProperty(prop)) {
            element.style[prop] = styles[prop];
          }
        }
      });
      return this;
    },
    
    // La méthode changeBGColor change la couleur de fond d'un élément du DOM.
    // paramètre : la nouvelle couleur
    changeBGColor: function (color) {
      this.elements.map(function(element, index) {
        element.style.backgroundColor = color;
      });
      return this;
    },

    // Cette Méthode est une simplification de addEventListener, 
    // qui a pour but d'ajouter des évènements aux éléments du DOM
    on: function (eventName, callback) {
      this.elements.map(function(element, index) {
        element.addEventListener(eventName, function (evt) {
          if (typeof callback === 'function') { callback(evt)}
        }, false);
      });
    }
  }
  /* EO:DOM */
  // On définit une instance de notre Wrapper DOM. Comme ça on pourrait 
  // utiliser notre petite Biliothèque d'une façon trés simple :
  // directement : dom(ELEMENT(S) DU DOM)
  window.dom = function (element) {
    return new DOM(element);
  }

  // on retourne notre wrapper au cas où l'utilisateur veut 
  // utiliser cette bibliothèque comme suit: var $D = new DOM(ELEMENT(S) DU DOM);
  return DOM;

}());
				
			

Ainsi on a pu créer un Objet qui nous permettra de travailler tout en sécurité avec les éléments du DOM de pouvoir les "manipuler". Voyons alors un petit exemple d'utilisation de notre bibliothèque.
Ayant pour exemple ce code HTML :

        
<!DOCTYPE html>
<html>
    <head>
        <meta charset="utf-8">
        <meta name="viewport" content="width=device-width">
        <title>DOM</title>
        <script src="dom.js"></script> <!-- dom.js contient notre code en haut (notre bibliothèque DOM) -->
        <style>
          html, body {
            height: 100%;
            min-height: 1005;
          }
          *, *:before, *:after {
            box-sizing: border-box;
          }
          .me {
            width: 200px;
            height: 20px;
            background: red;
            margin: 10px;
          }
        </style>
    </head>
    <body>
        <h1>Exemple : Extension des éléments du DOM</h1>
        <div class="me">1</div>
        <div class="me">2</div>
        <div class="autre">3</div>
        <div class="me">4</div>
        <div class="me">5</div>
        <span class="autre">6</span>
        <div class="me">7</div>
    </body>
</html>
        
      

Si on veut par exemple:
1) changer les styles de tout les élément de classe ".me"
2) changer la couleur de fond du SPAN ".autre" et
3) changer le style du DIV ayant le nom de classe ".autre" et le zoomer après un clic
Notre code Javascript serait le suivant :

        
var cssElements = dom(document.querySelectorAll('.me'));
var BGElement = dom(document.querySelector('span.autre'));
var zoomElement = dom(document.querySelector('div.autre'));

// tous les éléments de classe '.me' auront la couleur de texte blanche, 
// centrés et une fonte de 20px
cssElements.css({
  color: '#FFF',
  textAlign: 'center',
  fontSize: '20px'
});

// On change la couleur de fond du span 'autre' en bleu.
BGElement.changeBGColor('blue'); 

// on change le style du DIV '.autre' et on enchaine avec la méthode on pour définir
// une fonction qui sera appelée après un clic. Cette méthode serait 'scale' avec
// un facteur egal à 2 (donc on double les dimensions du DIV par chaque clic)
zoomElement.css({
  width: '100px',
  height: '10px',
  backgroundColor: 'orange',
  margin: '10px',
  color: '#FFF',
  textAlign: 'center',
  transition: 'all 0.25s ease-in-out 0s'
}).on('click', function(e){
  e.preventDefault();
  zoomElement.scale(2);
});
        
      

Maintenant si on met tout dans un petit projet 😉:

See the Pen extend DOM Elements by Marouen Mhiri (@Marouen) on CodePen.

J'espère ainsi avoir pu éclaircir l'idée sur l'extension des éléments du DOM. N'hésitez pas à poser vos questions ou ajouter vos propositions.
Il est important de signaler que la petite bibliothèque qu'on a construite manque encore de pas mal de "Checks" en cas d'erreurs inattendues.

Tags :

javascript