Up First Previous

Créer des pages interactives
DOM & DHTML

Next Last

Il ne s'agit pas à proprement parler d'un langage, mais de la combinaison de feuilles de styles (CSS), d'objets (DOM) et de scripts (pour la plupart en JavaScript). L'ensemble de ces trois outils permet de définir des pages dynamiques sans avoir recours à des langages tels que le Flash. DHTML est antérieur aux spécifications XHTML mais j'ai choisi de ne le présenter que maintenant afin de pouvoir exposer des exemples de pages dynamiques qui respectent les derniers standards du W3C.

 Les balises span et div et la propriété innerHTML 

Les balises span et div

Ces deux balises sont les alliées incontournables des CSS et du DHTML. Elles permettent de définir des groupes sémantiques à l'intérieur d'un document et de centaliser les définitions de style pour les éléments du groupe ainsi formé. Les zones qu'elles définissent peuvent être manipulées à l'aide du JavaScript, et plus particulièrement avec les fonctions proposées ci-dessous afin d'ajouter de la dynamique aux pages XHTML du côté client (d'où le nom de Dynamic HTML).

Hormis pour la gestion des styles (cf CSS), ces balises ne sont intéressantes que si elles sont utilisées avec un argument id qui permet d'identifier chaque instance, et donc de les manipuler. Leur utilisation classique est donc :
  <div id="nomdiv"> <!-- gros code XHTML --> </div>
  <span id="nomdiv"> <!-- petit contenu --> </span>.

Bien que rien n'empêche d'utiliser indifféremment div et span, il est fortement conseillé (par le W3C) de n'utiliser span que pour identifier de petites zones, par exemple un bout de texte. Au contraire, div permet de définir des zones sémantiques aussi grosses qu'on le désire, une utilisation pratique en est d'ailleurs l'identification d'une partie div id="content" qui comprend la zone principale d'une page XHTML, alors que les menus seront dans une zone div id="menu", ...

Nous reviendrons, après avoir vu les CSS, sur les utilisations des divs pour une mise en forme dynamique des pages XHTML. Pour l'instant, nous allons nous intéresser à leur propriété innerHTML qui permet de modifier interactivement le contenu même d'une page sans avoir à recharger quoi que ce soit.

La propriété innerHTML

Toutes les balises ont une propriété innerHTML qui représente leur contenu. Ainsi, si on considère le code XHTML <p>bla bli blou</p>, la propriété innerHTML de cette instance de p vaut bla bli blou.

Quel est l'intérêt ? Et bien tout simplement de pouvoir modifier interactivement le contenu de la page comme le montre l'exemple ci-dessous :
  voici une zone de texte non modifiable : "non modifiable"
  tapez un texte court : puis cliquez .

Il est donc possible de faire varier le contenu d'une zone selon les interactions avec l'utilisateur et les possibilités dynamiques côté client en deviennent quasi infinies : afficher ou pas la solution d'une énigme, ...

 Manipuler des objets (balises) 

En théorie, pour obtenir en JavaScript une référence sur un objet d'identifiant id (toutes les balises peuvent avoir un argument id="nom_instance_balise"), on devrait utiliser la fonction getElementById(id) définie par le W3C. De même, pour obtenir une référence à partir d'un nom (argument name), par exemple pour un champ de formulaire, on devrait utiliser la fonction getElementByName(name).

Malheureusement, les standards ne sont pas très bien respectés par les différents navigateurs qui cherchent à imposer leur vison des choses. A titre d'exemple, (certaines versions d')Internet Explorer© utilise(nt) un tableau associatif nommé all contenant tous les objets de la page ; on accède donc à l'objet recherché par la commande document.all[id].
A contrario, Netscape©, qui est à la base de la notion de calque, utilisait la balise layer (devenue div après standardisation), et à réservé un tableau particulier à ces calques : document.layers[idcalque].
Ces exemples montrent bien le besoin de standardisation afin que chaque Internaute ait accès à tous les sites quel que soit son "choix" de navigateur.

En attendant que les recommandations du W3c soient toutes adoptées par les différents navigateurs (on peut rêver), une fonction telle que la fonction findObj présentée ci-dessous permet une programmation transparente sans avoir à se préoccuper des problèmes de compatibilité.
attention ! Attention, les fonctions présentées ci-dessous ne font pas partie du JavaScript et ne sont pas connues des navigateurs. Pour les utiliser il faut télécharger le fichier standards.js et l'inclure en tant que script externe dans la partie head de vos pages qui en ont besoin (ce fichier de script contient également des fonctions d'ouverture de fenêtres, de préchargement d'images, ...).

function findObj(n, d)
{
  var p, i, x;
  if(!d)
    d = document;
  if((p=n.indexOf("?"))>0 && parent.frames.length)
  {
    d=parent.frames[n.substring(p+1)].document;
    n=n.substring(0,p);
  }
  if(!(x=d[n]) && d.all)
    x = d.all[n];
  for (i=0; !x && i<d.forms.length; i++)
    x = d.forms[i][n];
  for(i=0; !x && d.layers && i<d.layers.length; i++)
    x = findObj(n,d.layers[i].document);
  if(!x && d.getElementById)
    x = d.getElementById(n);
  return x;
}

Cette fonction permet de retrouver tout objet (instance d'une balise) identifié par un argument id ou par un argument name pour les champs de formulaires.

Les paramètres de la fonction sont l'identifiant n et l'objet dans lequel il faut effectuer la recherche. Ce second paramètre est optionnel et n'est généralement pas utilisé. Dans ce cas, il est initialisé par défaut à la valeur document, indiquant que la recherche se fera dans toute la page courante.

Sans rentrer dans le détail du code, on peut voir que cette fonction teste l'existence des différents tableaux possibles puis recherche dans ces tableaux lorsqu'ils existent ; et seulement en dernier ressort utilise la fonction standardisée getElementById.

Quelles que soient vos habitudes de codage, je vous incite fortement à abandonner les notations spécifiques telles que document.all pour privilégier à faible coût des pratiques plus robustes et universelles.

function showHideLayers()
{
  var i, p, v, obj, args=showHideLayers.arguments;
  for (i=0; i<(args.length-2); i+=3)
    if ((obj=findObj(args[i])) != null)
    {
      v = args[i+2];
      if (obj.style)
      {
        obj = obj.style;
        v = (v=='show') ? 'visible' : (v=='hide')?'hidden':v;
      }
      obj.visibility = v;
    }

}
function moveLayers()
{
  var i, p, v, obj, args=moveLayers.arguments;
  for (i=0; i<(args.length-2); i+=3)
    if ((obj=findObj(args[i])) != null)
    {
      if (obj.style)
      {
        obj = obj.style;
	  }
      obj.left = args[i+1];
      obj.top = args[i+2];
    }
}

Tout cela peut paraître compliqué mais la compatibilité avec l'ensemble des navigateurs actuels est à ce prix. D'aute part, leur utilisation s'avère en fait assez simple comme l'illustre l'exemple suivant.

Ainsi on déplacera par exemple N zones obj1 à objN toutes les 40ms par la fonction JavaScript suivante :

function BougeMesObjets()
{
  moveLayers('obj1', Math.floor(positionX++), Math.floor(positionY--),
             ...,
             'objN', Math.floor(positionX--), Math.floor(positionY++));
  setTimeout('BougeMesObjets()', 40);
}

 Le DOM : Document Object Model 

Le DOM interprète chaque élément qui constitue une page web. Cet élément est considéré comme un objet. Ce procédé a été mis au point par le W3C, afin d'établir une norme pour assurer une compatibilité parfaite (lors de la lecture d'une page XHTML Dynamique) entre les principaux navigateurs du marché.

Comme nous l'avons vu (plus haut), la pratique ne rejoint malheureusement pas encore tout à fait la théorie. Aussi, le Document Object Model présenté ci-dessous montre des éléments du standard du W3C, mais également des objets spécifiques à telle ou telle version de navigateur. Dans ce cas, le nom du navigateur et de sa version est indiqué à côté.

Les principaux objets sont :

  • navigator
  • screen
  • window
    • location
    • history
    • document
      • head
      • body
      • all (IE4)
      • layers, images, ... (NN4)

Le détail des propriétés et des méthodes de ces objets dépasse très largement la cadre de ce cours, mais une liste exhaustive est disponible sur le site de développement de Microsoft®. Notons que comme en C++ c'est le point qui est l'opérateur d'appartenance.

Néammoins, voici les propriétés qui pourront vous intéresser en tout premier lieu :

  • screen.width, screen.height : résolution de l'écran

 Exercices 

A partir de ce cours et de celui sur le JavaScript (et de ses exercices corrigés), vous devez être capables de réaliser :

  1. un diaporama dont les caractéristiques sont les suivantes :
    • Les adresses des images affichables sont stockées dans un tableau associatif défini en variable globale dont la taille n'est pas connue a priori par les autres fonctions. Nous modifierons plus tard cet exemple pour générer ce tableau dynamiquement mais du côté serveur à l'aide de PHP.
    • A gauche et à droite de l'image centrale, deux petites images doivent présenter les images précédente et suivante.
    • En cliquant sur l'image centrale, on passe (de manière cyclique) à l'image suivante. En cliquant sur l'une des deux vignettes, on affiche l'image correspondante.
    • Des boutons doivent permettre d'afficher directement la première et la dernière image. Vous pouvez utiliser les images de boutons de ce site.
    • L'infobulle de l'image centrale doit être du type "image i / N". Cette information doit également apparaitre en dessous de l'image en temps que texte simple.
    • modifiez la fonction de changement d'image pour quelle gère un changement aléatoire d'image. Cette option doit être gérée par une variable supplémentaire optionnelle de la fonction de changement d'image. Le choix de changement linéaire ou aléatoire d'image doit se faire à l'aide de boutons.
  2. les bases d'un jeu de billard :
    • projet de cette année pour les auditeurs CNAM... (voir l'énoncé)