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


précédentsommairesuivant

IV. Implémentation : 2ème étape - l'édition

IV-A. Par où commencer ?

Au stade où nous en sommes, ajouter l'édition à notre table va être très simple. Rappelez-vous de la section 2.4 dans laquelle nous posons l'interface minimale d'un contrôle HtmlEdit qui sera la brique élémentaire de notre fonctionnalité.

Comment procéder ? L'idée est de pouvoir remplacer à la volée une cellule par un input d'édition. Mais, à bien y réfléchir, cela n'est pas possible car une cellule d'une table ne peut être remplacée. Par contre le contenu de la cellule, lui, peut-être remplacé ! Donc notre but à présent est de parvenir à substituer un input d'édition au contenu d'une cellule lorsque l'on veut éditer cette dernière, puis, à la fin de l'édition, de substituer la nouvelle valeur à l'input d'édition (abracadabra, en quelque sorte).

IV-B. Comment vider le CELLier

IV-B-1. JS : HtmlEditTableHelper.CellInitialize()

Posons que notre contrôle HtmlEdit existe déjà et intégrons-le au HtmlEditTable. Tout d'abord il faut que l'événement double-clic sur une cellule provoque l'édition de cette dernière. Pour cela, modifions la fonction HtmlEditTableHelper.CellInitialize() pour appeler un callback HtmlEditTableHelper.DblClickHandler() qui réalisera notre souhait. A la création d'une cellule :

HtmlEditTableHelper.CellInitialize - v1.0
Sélectionnez
CellInitialize: function(cell, value){
  cell.ondblclick = HtmlEditTableHelper.DblClickHandler;
  if (typeof value != "undefined"){
    cell.appendChild(document.createTextNode(value));
  }
}

Le callback HtmlEditTableHelper.DblClickHandler() est spécifique à notre implémentation, c'est pourquoi nous l'intégrons à la classe HtmlEditTableHelper.

IV-B-2. JS : HtmlEditTableHelper.DblClickHandler()

Le callback HtmlEditTableHelper.DblClickHandler() se charge de placer un contrôle HtmlEdit dans la cellule :

HtmlEditTableHelper.DblClickHandler
Sélectionnez
DblClickHandler: function(e){
  var src = Tools.Node(Tools.Target(e), "TD");
  if (!src){
    Tools.Event(e).returnValue = false;
    return false;
  }
  var htmlEdit = new HtmlEdit(src.firstChild.data);
  htmlEdit.AppendTo(src);
  src.ondblclick = null;
}

D'abord on isole la cellule. Quand vous double-cliquez sur la cellule, c'est potentiellement son contenu qui déclenchera l'événement, donc la source de l'événement n'est pas forcément la cellule. La fonction utilitaire Tools.Target() nous renvoie la source de l'événement. La fonction parcoure les nœuds parents de l'élément passé en argument jusqu'à tomber sur le premier dont le nodeName est celui passé en argument. Par précaution, prévoyons une porte de sortie au cas où la fonction est mal utilisée et que nous nous retrouvons sans cellule dans la généalogie de notre source (dans ce cas on retourne false). En cas de succès de la recherche, on crée un HtmlEdit et on lui passe le contenu textuel de la cellule. On supprime le callback sur le double-clic et on ajoute le HtmlEdit à la cellule.

Pour l'instant la classe HtmlEdit n'est pas implémentée. Aussi, si vous testez le programme en l'état, vous obtiendrez une erreur. Par contre, si vous commentez les lignes concernant le HtmlEdit dans la fonction HtmlEditTableHelper.DblClickHandler(), vous constaterez que les cellules se vident lorsque vous double-cliquez dessus. Au passage, sous IE vous aurez un bug d'affichage sur les bordures des cellules vidées, mais nous verrons cela plus tard.

IV-C. La maison d'édition : HtmlEdit

Et bien nous y voilà. Nous allons maintenant mettre en oeuvre le contrôle HtmlEdit grâce auquel notre table deviendra éditable.

IV-C-1. JS : HtmlEdit()

Décodons la cinématique d'édition d'une cellule. Nous avons vu plus tôt que, lorsque l'on double-clique sur une cellule, cette dernière est vidée de son contenu textuel et à la place se crée un edit via le contrôle HtmlEdit. Le comportement classique d'une cellule éditable est que l'édition s'arrête lorsque la cellule (i.e. l'input dans la cellule) perd le focus ou lorsque la touche Entrée est pressée.

Constructeur HtmlEdit
Sélectionnez
var HtmlEdit = function(value){
  var loseFocus = function(e){
    var src = Tools.Target(e);
    var cell = Tools.Node(src, "TD");

    Tools.Purge(cell);
    HtmlEditTableHelper.CellInitialize(cell, src.value);
  };

  this.control = document.createElement("input");
  this.control.type = "text";
  this.control.className = "HtmlEdit";
  this.control.onblur = loseFocus;
  this.control.onkeydown = function(e){
    if (Tools.KeyCode(e) == 13){
      loseFocus(e);
    }
  };

  this.control.value = value;
};

Intéressons-nous d'abord à la fin de cette fonction. La classe HtmlEdit possède un attribut control qui n'est autre qu'un input de type text (un edit en somme). Nous lui assignons une classe CSS ainsi que des callback sur les événements blur (perte du focus) et keydown (touche enfoncée lorsque le focus est sur l'input). L'événement blur déclenche le callback loseFocus(). L'événement keydown déclenche une fonction anonyme dans laquelle loseFocus() est appelé si la touche pressée a le code 13 (touche Entrée). La fonction utilitaire assure la compatibilité sur tous les navigateurs pour la détermination du code de la touche pressée à partir de l'objet Event.

Le coeur du comportement du HtmlEdit réside dans le callback loseFocus() défini au début du constructeur de la classe. Au passage, il s'agit là d'une fonction définie localement au constructeur (voir le tutoriel "Espaces de noms en JavaScript" pour de plus amples informations).

loseFocus() est appelé lorsque la touche Entrée est pressée, lorsque l'input a le focus et aussi lorsque cet input perd le focus. Donc l'élément cible src de ce callback est l'input et le nœud parent de l'input est la cellule. On purge la cellule et on l'initialise via la fonction HtmlEditTableHelper.CellInitialize() que l'on connaît. Voilà comment la cellule passe du mode édition au mode lecture seule.

IV-C-2. JS : AppendTo(parent)

Il nous reste à implémenter la fonction AppendTo() qui nous permet d'ajouter l'input à la cellule que nous voulons éditer. Cette fonction prend en paramètre le parent de l'input - la cellule.

HtmlEdit.prototype.AppendTo
Sélectionnez
HtmlEdit.prototype = {
  AppendTo: function(parent){
    if (document.all){
      this.control.style.height = parent.clientHeight - 2*parent.clientTop + "px";
      this.control.style.width = parent.clientWidth - 2*parent.clientLeft + "px";
    }
    else{
      this.control.style.height = parent.offsetHeight - 2*parent.clientTop + "px";
      this.control.style.width = parent.offsetWidth - 2*parent.clientLeft + "px";
    }

    Tools.Purge(parent);    
    parent.appendChild(this.control);

    this.control.select();
    this.control.focus();
  }
};

On dimensionne ensuite l'input pour qu'il prenne tout l'espace disponible dans la cellule. Ensuite on purge la cellule et on y ajoute l'input. Puis pour terminer, on sélectionne le texte de l'input et on lui donne le focus.

En fait, pour la taille de l'input, on ne se contente pas d'un simple 100% en largeur et en hauteur. Nous devons avoir recours à un subterfuge du fait qu'il est possible que la cellule ait une bordure, un padding... Et pour arranger les choses, la gestion des dimensions est différente suivant que vous utilisiez Internet Explorer ou un autre navigateur. La hauteur est calculée comme étant le clientHeight (offsetHeight pour Internet Explorer) du parent diminué, le cas échéant, de la taille des bordures inférieure et supérieure. La largeur est calculée de la même façon en fonction de clientWidth ou offsetWidth et des bordures gauche et droite.

IV-C-3. CSS : .HtmlEdit

Pour que le dimensionnement précédent fonctionne, il nous faut supprimer les bordures, le padding et le margin de notre input.

Il est préférable que la taille de la police soit la même que celle de la cellule, soit 1em, ce qui correspond à 1 fois la taille de la police de la cellule. La taille de l'espace d'affichage du texte de l'input est également fixée à 1em pour être sûr que le texte s'affiche en entier.

Au final, définissons le style suivant pour notre input :

.HtmlEdit
Sélectionnez
.HtmlEdit
{
  border: 0px;
  font-size: 1em;
  font-family: Courier;
  padding: 0px;
  text-align:right;
  margin:0px;
  line-height:1em;
  background-color:#F0F0F0;
}

IV-D. La fin est proche

Notre page de test vous permet de voir le résultat. Vous pouvez constater que nous avons atteint notre objectif de départ et que nous pouvons récupérer facilement les données entrées dans la table : voir la démo - télécharger les sources

Que peut-on faire de plus ? Il serait bien utile d'avoir quelques fonctionnalités supplémentaires telles que : obtenir la liste des colonnes, des lignes, la donnée d'une cellule particulière, etc.


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.