Conception de contrôles Web JavaScript avancés : Créer une table HTML éditable - HtmlEditTable v2.0


précédentsommairesuivant

II. Création d'un HtmlEditTable à partir d'une table HTML existante

Vous avez peut-être tardivement entendu parler de la révolutionnaire (mytho?) HtmlEditTable et vous aimeriez l'utiliser sans pour autant devoir réécrire vos pages. Eh oui! Vous avez déjà construit vos tables en HTML ou via un script et il serait intéressant de leur ajouter les capacités de la HtmlEditTable sans toucher à l'existant. Et bien allons-y!

II-A. Page de test

Notre page de test est simple : une table HTML suivie d'un script de création d'un HtmlEditEditTable à partir de la table HTML. Le script consiste en l'appel du constructeur de la classe HtmlEditTable auquel on passe l'objet paramètre avec la propriété "table" qui n'est autre que l'objet table.

Page de test - HtmlEditTable.html
Sélectionnez

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html>
  <head>
    <script type="text/javascript" src="HtmlEditTable.js"></script>
    <style type="text/css">
      @import url(HtmlEditTable.css);
    </style>
  </head>
  <body>
    <table id="tableHTML">
      <thead>
        <tr>
          <td>head</td>
          <td>head</td>
          <td>head</td>
          <td>head</td>
          <td>head</td>
        </tr>
      </thead>
      <tbody>
        <tr>
          <td>data</td>
          <td>data</td>
          <td>data</td>
          <td>data</td>
          <td>data</td>
        </tr>
        <tr>
          <td>data</td>
          <td>data</td>
          <td>data</td>
          <td>data</td>
          <td>data</td>
        </tr>
        <tr>
          <td>data</td>
          <td>data</td>
          <td>data</td>
          <td>data</td>
          <td>data</td>
        </tr>
      </tbody>
    </table>
    <br/>
    <script>
      var tableHTML = document.getElementById("tableHTML");
      var tableEdit = new HtmlEditTable({"table": tableHTML});
    </script>
  </body>
</html>

II-B. JS : HtmlEditTable(param)

Si la table HTML existe, alors passons-la au constructeur de la classe HtmlEditTable. Bref rappel, le constructeur accepte un unique paramètre qui est un objet disposant des propriétés (facultatives) suivantes :

Propriétés du paramètre du constructeur
  • le nombre de colonnes (X) ou la liste des noms de colonnes (Xn)
  • le nombre de lignes (Y) ou la liste des noms de lignes (Yn)
  • les données (data)
  • le titre de la table (caption)
  • la liste des en-têtes de colonnes (head)

A cette liste de propriétés possibles, on ajoute donc la suivante :

Nouvelle propriété du paramètre du constructeur
  • la table HTML (table) à étendre en HtmlEditTable

Concrètement l'impact de cette fonctionnalité se situe au niveau du constructeur :

Constructeur HtmlEditTable - v1.1
Sélectionnez

HtmlEditTable = function(){
  if (arguments.length > 0 && arguments[0].table){
    this.Transform(arguments);      
  }
  else{
    this.control = document.createElement("table");
    if (arguments.length > 0){
      this.Build(arguments[0]);
    }
  }
  this.control.cellSpacing = 0;
  this.control.cellPadding = 0;
  this.control.className = "HtmlEditTable";
};

Que s'est-il passé? La nouveauté vient de l'instruction conditionnelle. Si la propriété table du paramètre existe, alors il s'agit d'étendre les fonctionnalités d'une table existante, sinon il s'agit de créer un HtmlEditTable de A à Z.

Dans le cas de l'extension d'une table, la fonction Transform() intervient. Son rôle est identique à celui de la fonction Build() à ceci près qu'au lieu d'initialiser le HtmlEditTable à partir des propriétés de la v1.0 du paramètre du constructeur, l'initialisation se base sur la table, propriété de la v2.0 :).

HtmlEditTable.prototype.Transform v1.1
Sélectionnez

Transform: function(args){
  this.columns = [];
  this.lines = [];
  this.headers = [];
  this.control = args[0].table;
  this.caption = this.control.caption;
  for (var i=0, imax=this.control.rows[0].cells.length; i<imax; i++){
      this.columns.push("" + i);
  }      
  for (var i=0, imax=this.control.rows.length; i<imax; i++){
    this.lines.push("" + i);
  }      
  if (this.control && this.control.tBodies){
    for (var i=0, imax=this.control.tBodies.length; i<imax; i++){
      var tBody = this.control.tBodies[i];
      for (var j=0, jmax=tBody.rows.length; j<jmax; j++){
        for (var k=0, kmax=tBody.rows[j].cells.length; k<kmax; k++){
          var cell = tBody.rows[j].cells[k];
          cell.tabIndex = 0;
          HtmlEditTableHelper.CellInitialize(this, cell);
          if (cell.childNodes.length == 0){
            cell.appendChild(document.createTextNode(""));
		  }
        }
      }
    }
  }
}

Dans un premier temps, la fonction Transform() :

  • crée les arrays columns, lines et headers,
  • assigne la table à la propriété control,
  • assigne le titre (caption) de la table au HtmlEditTable.

Puis chaque colonne et chaque ligne sont parcourues afin d'initialiser les arrays columns et lines. Pour rappel, il s'agit des listes de noms de colonnes et de lignes. Ceux-ci sont initalisés comme étant l'indice de la colonnes ou de la ligne dans la table.

Pour finir, toutes les cellules des tBody sont parcourues et leurs fonctionnalités sont étendues grâce à la fonction HtmlEditTableHelper.CellInitialize(). Les cellules n'ayant pas de contenu se voient ajouter un nœud texte vide.

Pour finir ? Oui mais non... Si vous disposez d'un outil pour visualiser le DOM de votre document sous Internet Explorer, vous constaterez que la structure de la table est "propre". Par contre avec d'autres navigateurs (Firefox, Chrome, pour ne citer qu'eux) vous remarquerez qu'il y a pas mal de nœuds texte qui polluent la table. Ces navigateurs interprètent les retours chariot comme des nœuds texte et donc ces nœuds s'intercalent avec les nœuds "légitimes" de la table.

Comme par la suite nous voudrons naviguer au clavier entre les cellules de la table, il est préférable de nettoyer la structure de cette dernière dès maintenant. Cela facilite les développements futurs et rendra cohérente la structure du contrôle qu'il ait été généré à partir d'une table HTML préexistante ou non (car lorsqu'on construit le contrôle à partir de rien, comme dans la v1.0, il n'y a pas de nœuds parasites).

Ajoutons à la classe utilitaire Tools une fonction de suppression des nœuds texte :

Tools.RemoveTextNode
Sélectionnez

RemoveTextNode: function(o){
  if (o == null){
    return;
  }

  for (var i=o.childNodes.length-1; i>-1; i--){
    var child = o.childNodes[i];
    if (child.nodeName == "#text"){
      o.removeChild(child);
    }
  }
}

Et utilisons-la dans la fonction HtmlEditTable.prototype.Transform pour nettoyer la table des nœuds texte parasites :

HtmlEditTable.prototype.Transform v1.2
Sélectionnez

Transform: function(args){
  this.columns = [];
  this.lines = [];
  this.headers = [];
  this.control = args[0].table;
  this.caption = this.control.caption;
  for (var i=0, imax=this.control.rows[0].cells.length; i<imax; i++){
      this.columns.push("" + i);
  }      
  for (var i=0, imax=this.control.rows.length; i<imax; i++){
    this.lines.push("" + i);
  }

  if (this.control && this.control.tBodies){
    for (var i=0, imax=this.control.tBodies.length; i<imax; i++){
      var tBody = this.control.tBodies[i];
      for (var j=0, jmax=tBody.rows.length; j<jmax; j++){
        for (var k=0, kmax=tBody.rows[j].cells.length; k<kmax; k++){
          var cell = tBody.rows[j].cells[k];
          cell.tabIndex = 0;
          HtmlEditTableHelper.CellInitialize(this, cell);
        }
      }
    }
  }

  if (this.control){
    Tools.RemoveTextNode(this.control);
    if (this.control.tHead){
      var tHead = this.control.tHead;
      Tools.RemoveTextNode(tHead);
      for (var i=0, imax=tHead.rows.length; i<imax; i++){
        Tools.RemoveTextNode(tHead.rows[i]);
      }
    }
    if (this.control.tBodies){
      for (var i=0, imax=this.control.tBodies.length; i<imax; i++){
        var tBody = this.control.tBodies[i];
        Tools.RemoveTextNode(tBody);
        for (var j=0, jmax=tBody.rows.length; j<jmax; j++){
          Tools.RemoveTextNode(tBody.rows[j]);
        }
      }
    }   
    if (this.control.tFoot){
      var tFoot = this.control.tFoot;		
      Tools.RemoveTextNode(tFoot);
      for (var i=0, imax=tFoot.rows.length; i<imax; i++){
        Tools.RemoveTextNode(tFoot.rows[i]);
      }
    }
  }
}

Vous allez rire, mais... c'est fini :) voir la démo - télécharger les sources

Cette fonction peut être optimisé au niveau des boucles. Cependant j'ai préféré séparé le "nettoyage" de la table de l'initialisation des cellules, surtout pour le côté pédagogique de l'article. Libre à vous d'optimiser la fonction si vous pensez avoir des problèmes de performances à ce niveau.


précédentsommairesuivant

Vous avez aimé ce tutoriel ? Alors partagez-le en cliquant sur les boutons suivants : Viadeo Twitter Facebook Share on Google+   

  

Les sources présentées sur cette page sont libres de droits et vous pouvez les utiliser à votre convenance. Par contre, la page de présentation constitue une œuvre intellectuelle protégée par les droits d'auteur. Copyright © 2009 Nourdine FALOLA. Aucune reproduction, même partielle, ne peut être faite de ce site ni de l'ensemble de son contenu : textes, documents, images, etc. sans l'autorisation expresse de l'auteur. Sinon vous encourez selon la loi jusqu'à trois ans de prison et jusqu'à 300 000 € de dommages et intérêts.