Javascript non-intrusif, chapitre 3 : créer du contenu et le détruire

Par Christian Heilmann

Cet article est le troisième d'une série qui en compte 5.

La principale force du DOM est la possibilité non seulement de lire, mais aussi de modifier le contenu et la structure du document. Nous disposons de plusieurs méthodes pour effectuer tout cela.

Créer du nouveau contenu

createElement(element)
Crée un nouvel élément
createTextNode(chaine)
Crée un nouveau nœud de texte dont la valeur est la chaîne de caractères chaine.

Les éléments nouvellement créés ne sont pas ajoutés au document tout de suite, ils restent in limbo jusqu'à ce qu'on les ajoute quelque part dans l'arbre à nœuds. Ces fonctions doivent être appliquées à l'objet document plutôt qu'à un nœud.

Javascript :
monNouveauParagraphe=document.createElement('p');
monNouveauTexte=document.createTextNode('ceci est un nouveau paragraphe');

Modifier le contenu existant

setAttribute(attribut,valeur)
Ajoute un nouvel attribut avec la valeur à l'objet.
appendChild(enfant)
Ajoute enfant en tant que nœud enfant (childNode) à l'objet. enfant doit être un objet, vous ne pouvez pas utiliser de chaîne de caractères.
cloneNode()
Copie le nœud entier avec tous les nœuds enfants.
hasChildNodes()
Vérifie si un objet possède des nœuds enfants (childNodes), et renvoie true si c'est le cas.
insertBefore(nouvelEnfant,ancienEnfant)
Insère nouvelEnfant avant ancienEnfant dans l'arbre du document.
removeChild(ancienEnfant)
Supprime le nœud enfant ancienEnfant.
replaceChild(nouvelEnfant,ancienEnfant)
Remplace ancienEnfant par nouvelEnfant.
removeAttribute(attribut)
Supprime l'attribut de l'objet.

Affichage d'images

Admettons que nous ayons des liens vers quelques images, et que nous voulions qu'ils s'ouvrent dans une nouvelle fenêtre en l'absence de Javascript, ou sous les liens si Javascript est activé.

HTML :
<ul id="imglist">
 <li><a href="home.gif" target="_blank">Accueil 
 (nouvelle fenêtre)</a></li>
 <li><a href="home_on.gif" target="_blank">Accueil actif 
 (nouvelle fenêtre)</a></li>
 <li><a href="jscsshtml.gif" target="_blank">HTML-CSS-Javascript 
 (nouvelle fenêtre)</a></li>
</ul>

Maintenant, quand Javascript et DOM sont disponibles, nous voulons :

Cette fonction devrait :

Le premier problème n'est pas difficile à résoudre :

Javascript :
function imgpop()
{
 var il,imga,imgatxt;

// récupère toutes les balises <li> dans la liste d'images,
// et boucle dessus 
 il=document.getElementById('imglist').getElementsByTagName('li');
 for(i=0;i<il.length;i++)
 {

// récupère le premier lien dans les balises <li>
 imga=il[i].getElementsByTagName('a')[0];

// supprime les mots (nouvelle fenêtre) dans le texte du lien
// (qui est la valeur (nodeValue) du premier nœud)  
 imgatxt=imga.firstChild;
 imgatxt.nodeValue=imgatxt.nodeValue.replace(/ \(nouvelle fenêtre\)/,'');

// ajoute le gestionnaire d'événements pour appeler popw();
 imga.onclick=function(){return popw(this);}
 //imga.onkeypress=function(){return popw(this);}
 }
}

En ce qui concerne la fonction popw(), nous allons utiliser quelques-unes des méthodes expliquées plus haut :

Javascript :
function popw(o)
{
 var newimg;
// s'il y a déjà une image dans le nœud parent (parentNode) (li) 
 if(o.parentNode.getElementsByTagName('img').length>0)
 {
// on la supprime
  o.parentNode.removeChild(o.parentNode.getElementsByTagName('img')[0]);
 } else {
// sinon, on crée une nouvelle image et on ajoute un gestionnaire qui 
// la supprime quand on clique dessus
  newimg=document.createElement('img');
  newimg.style.display='block';
  newimg.onclick=function(){this.parentNode.removeChild(this);};
  newimg.src=o.href;
  o.parentNode.appendChild(newimg)
 }
 return false;
}

Voir l'afficheur d'images en action.

Sélecteur de dates

Imaginons cette fois qu'on ait un formulaire qui contienne des champs de dates, et qu'on veuille offrir à l'utilisateur équipé de Javascript un sélecteur de dates, les autres utilisateurs devant simplement entrer la date à la main. Mettons de côté la fonction du sélecteur de dates pour l'instant, et voyons d'abord comment l'appeler.

Nous commençons par le code HTML nécessaire. Pour savoir à quels éléments ajouter le lien du sélecteur de dates, on leur ajoute des classes portant le nom date.

HTML :
<h1>Réservation de vol</h1>
<form action="nosend.php" method="post" onsubmit="return check(this);">
<p>Étape 1 sur 4</p>
<h2>Veuillez sélectionner les dates</h2>
<p>
 <label for="dateDepart">Date de départ</label>
 <input type="text" class="date" id="dateDepart" name="dateDepart" />
</p>
<p>
 <label for="dateRetour">Date de retour</label>
 <input type="text" class="date" id="dateRetour" name="dateRetour" />
</p>
<p>
 <input type="submit" value="Envoyer" />
</p>
</form>

On va boucler sur tous les champs de saisie du document, et repérer ceux qui ont un nom de classe correspondant à date (souvenez-vous que les éléments HTML peuvent avoir plus d'un nom de classe dans leur attribut class !).

Si tel est le cas, on crée un nouveau lien, et un texte pour celui-ci. On ajoute le texte en tant qu'enfant du lien, et on ajoute un gestionnaire d'événements qui va lancer notre script de sélection de dates.

Une fois que le lien est créé, on l'ajoute juste après le champ de saisie.

Javascript :
function ajouteLienSelecteur()
{
 var champsSaisie,selectLien,selectTexte;

// boucle sur tous les champs de saisie
 champsSaisie=document.getElementsByTagName('input');
 for(i=0;i<champsSaisie.length;i++)
 {
  
// si la classe contient 'date'
  if(/date/.test(champsSaisie[i].className))
  {

// créer un nouveau lien et un nouveau texte
   selectLien=document.createElement('a');
   selectTexte=document.createTextNode('sélectionner une date');

// ajoute le texte en tant qu'enfant du lien
   selectLien.appendChild(selectTexte);

// change l'adresse de destination href à # et appelle le sélecteur
// lors d'un clic ou d'un focus sur le lien  
   selectLien.setAttribute('href','#');
   selectLien.onclick=function(){selecteur(this);return false;};
   //selectLien.onkeypress=function(){selecteur(this);return false;};

// ajoute le nouveau lien au parent du champ de saisie (ici, le paragraphe <p>)
   champsSaisie[i].parentNode.appendChild(selectLien)
  }
 }
}

Voir le sélecteur en action.

Tous les champs contenant des dates ont désormais après eux un lien qui pointe vers le sélecteur.

Tout ce dont nous avons besoin maintenant, c'est de dire à la fonction de sélection où attribuer sa valeur de retour.

Comme on envoie le lien lui-même en tant qu'objet à notre fonction de sélection, on a besoin d'accéder au champ de saisie qui le précède.

Javascript :
function selecteur(o)
{
 alert('Ceci est juste une simulation.') // pas de vraie fonction
 o.previousSibling.value='26/04/1975';          
}

On y est presque. Comme nous avons ajouté le lien en tant que dernier enfant du nœud parent du champ de saisie, il se pourrait très bien que le nœud qui précède (previousSibling) notre nouveau lien ne soit pas le champ de saisie lui-même mais une espace ! Du coup, il faut boucler sur les nœuds précédents jusqu'à ce qu'on tombe sur un élément.

Javascript :
function selecteur(o)
{
 alert('Ceci est juste une simulation.') // pas de vraie fonction
 while(o.previousSibling.nodeType!=1)
 {
  o=o.previousSibling;
 }
 o.previousSibling.value='26/04/1975';          
}

L'utilisation de boucles tient toujours un peu du bricolage et peut être assez lente. Pour éviter de boucler, nous devons changer notre fonction.

Changer la fonction ajouteLienSelecteur()

Il est très facile d'utiliser appendChild(), mais cela nous rend dépendant du balisage HTML. Que se passerait-il si, par exemple, nous devions ajouter par la suite une balise <span> avec une astérisque à côté du champ pour indiquer qu'il s'agit d'une information à remplir obligatoirement ?

L'astuce consiste à utiliser insertBefore() sur le nœud suivant notre champ de saisie (nextSibling).

Javascript :
function ajouteLienSelecteur()
{
 var champsSaisie,selectLien,selectTexte;

// boucle sur tous les champs de saisie
 champsSaisie=document.getElementsByTagName('input');
 for(i=0;i<champsSaisie.length;i++)
 {
                
// si la classe contient 'date'
   if(/date/.test(champsSaisie[i].className))
   {

// créer un nouveau lien et un nouveau texte
    selectLien=document.createElement('a');
    selectTexte=document.createTextNode('sélectionner une date');
        
// ajoute le texte en tant qu'enfant du lien
    selectLien.appendChild(selectTexte);

// change l'adresse de destination href à # et appelle le sélecteur
// lors d'un clic ou d'un focus sur le lien     
    selectLien.setAttribute('href','#'); 
    selectLien.onclick=function(){ajouteLienSelecteur(this)};
    //selectLien.onkeypress=function(){ajouteLienSelecteur(this)};

// ajoute le nouveau lien directement après le champ de saisie
    champsSaisie[i].parentNode.appendChild(selectLien)
    champsSaisie[i].parentNode.insertBefore(selectLien,champsSaisie[i].nextSibling);
    }
  }
}

Voir le sélecteur amélioré en action.

Choses à savoir

Voilà, c'est à peu près tout. Avec ces quelques outils nous sommes désormais capables d'accéder à n'importe quel élément d'un document et de le modifier, et nous pouvons améliorer l'expérience utilisateur sans devenir dépendant de Javascript.

C'est un peu déroutant de prime abord. Mais une fois que vous vous serez un peu habitués au DOM, plus vous l'utiliserez et plus ça deviendra facile.

Quelques obstacles habituels :

Et innerHTML, alors ?

innerHTML est né lorsqu'Internet Explorer 4 est sorti, et c'était à l'époque une façon rapide de générer et de changer du contenu. Il s'agit en fait de lire le contenu d'un élément d'une façon beaucoup plus simple qu'en utilisant les recommandations originelles du W3C. C'est particulièrement probant lorsqu'un élément contient des nœuds enfants (childNodes) qui sont eux-mêmes des éléments, et qu'on veut lire la totalité du contenu. Pour faire cela en utilisant seulement le DOM, vous devez subir les terribles épreuves[1] qui consistent à vérifier les types des nœuds (nodeTypes) et lire les valeurs de chaque nœud enfant. innerHTML est beaucoup plus souple à utiliser, mais possède lui aussi des inconvénients. Vous n'avez par exemple aucune référence sur les éléments que vous créez avec, puisque la valeur complète est une chaîne de caractères au lieu d'être une série d'objets. En outre, innerHTML est uniquement lié à HTML, et pas à XML, tandis que le DOM est défini pour n'importe quelle sorte de balisage. Si vous voulez une comparaison des techniques et du support de chaque navigateur, allez faire un tour dans la section DOM de Quirksmode.org[2] ou lisez la longue discussion de Developer-x[3].

Liens

» À suivre : « Javascript non-intrusif, chapitre 4 : l'appel des scripts de la forêt ».

Fiche technique

À propos de l'auteur

lang="en">Christian Heilmann
est un contributeur de Digital Web Magazine. La plupart de ses
publications sont écrites dans le métro, en voyageant
à travers Londres, parce qu’il n’y a simplement rien d’autre
à y faire. Un jour il aimerait remettre son propre livre
à ceux qui sont coincés là.

Articles du même auteur

Articles similaires

Voici la liste des dix articles les plus récents en rapport avec cet article :

Confirmé

JavaScript

© 2001-2016 Pompage Magazine et les auteurs originaux - Hébergé chez Nursit.com ! - RSS / ATOM - About this site