Retourner à la page d'accueil de TJS

Manipuler le document avec le Dynamic HTML

Que signifie DHTML ?

Le DHTML, signifie simplement Dynamic HTML. Il ne s'agit pas d'un langage particulier, mais d'une notion de développement web qui permet la manipulation de la page, de ses éléments, de leur contenu et leur apparence, une fois que la page est chargée.

Historiquement, le HTML ne permettait pas de modifier le contenu d'une page une fois que l'événement onload était déclenché. La seule solution pour le développeur était d'utiliser des formulaires pour y faire apparaitre des résultats.

Grâce aux travaux de normalisation des langages JS et HTML et des navigateurs, le DHTML est à l'origine de toutes les interfaces web qui remplacent les applications et logiciels exécutables traditionnels, comme les outils de messagerie mail, d'éditeurs de document, les tableurs ou même les sites comme Facebook.

Un premier exemple

Utilisons un premier exemple simple de ce que permet le DHTML : mise à jour de contenu, modification de l'apparence, réaction aux actions de l'utilisateur.

J'ai commencé la mise à jour de ce tutoriel le jour où le Bitcoin a dépassé les 10 000 $ (le 28/11/2018). L'occasion était évidente pour en faire un exemple didactique. (Envie d'en savoir plus sur le Bitcoin ?)

Le module a ce code HTML :

<div id="exemple">
	<div id="titre">Cours du Bitcoin (BTC)</div>
	<div id="cours" data-devise="USD"> </div>
	<div class="footer">
		<div id="euro" class="deviseOFF" onclick="clickDevise('euro')"> EUR </div>
		<div id="usd"  class="deviseON"  onclick="clickDevise('usd')"> USD </div>
	</div>
	<div id="info">Récupération de la valeur du Bitcoin...</div>
	<div id="chrono"> </div>
	<div id="legal">Cours provenant de cryptocompare.com</div>
</div>

Le code JS est assez long. Il n'est pas affiché, mais vous pouvez le voir en cliquant sur le lien :

/* Formatage de nombre */
function formatNombre(n) {
	var intlN=new Intl.NumberFormat();
	return intlN.format(n);
}

/* Formatage de date et heure */
function formatHeure(dt) {
	var options={hour12: false, hour: "2-digit", minute: "2-digit", second:"2-digit"};
	var intlD=new Intl.DateTimeFormat("fr-FR", options);
	return intlD.format(dt);
}

/* Gestion du clic sur EUR ou USD et mise à jour de l'affichage */
function clickDevise(devise) {
	if (devise=="euro") {
		document.getElementById("euro").className="deviseON";
		document.getElementById("usd").className="deviseOFF";
		document.getElementById("cours").dataset.devise="EUR";
	}
	if (devise=="usd") {
		document.getElementById("usd").className="deviseON";
		document.getElementById("euro").className="deviseOFF";
		document.getElementById("cours").dataset.devise="USD";
	}
	afficheCours();
}

/* Récupération des données de cours du Bitcoin */
function getCours() {
  /* Affichage de l'appel en cours */
  document.getElementById("info").innerHTML="Récupération de la valeur du Bitcoin...";
  document.getElementById("chrono").innerHTML=" ";
  
  /* Appel AJAX vers cryptocompare.com */
  var ajax = new XMLHttpRequest();
  ajax.onload = function() {
    if (this.status == 200) {
      var json=JSON.parse(this.response);
      var usd=json.USD, eur=json.EUR;
      var dt=new Date();
      var cours=document.getElementById("cours");
      cours.dataset.last=formatHeure(dt);
      cours.dataset.euro=json.EUR;
      cours.dataset.usd=json.USD; 
      cours.dataset.timestamp=dt.getTime()+60*1000; /* Timestamp du prochain appel ajax */
   	  afficheCours(); 
   	  chrono();
    }
  } 
  ajax.open("GET", "https://min-api.cryptocompare.com/data/price?fsym=BTC&tsyms=USD,EUR", true);
  ajax.send();
}


/* Mise à jour de l'affichage */
function afficheCours() {
	/* Recherche des infos de cours à utiliser */
    var cours=document.getElementById("cours");
	var devise=cours.dataset.devise;
	if (devise=="EUR") {
		var valeur=formatNombre(cours.dataset.euro)+" €";
	}
	if (devise=="USD") {
		var valeur=formatNombre(cours.dataset.usd)+" $";
	}
	cours.innerHTML = valeur;
    document.getElementById("info").innerHTML = "Cours récupéré à "+cours.dataset.last;
}

/* Fonction de chrono qui affiche le temps restant et lance l'appel AJAX  */
function chrono() {	
	var dt=new Date();
	var timestamp=parseFloat(document.getElementById("cours").dataset.timestamp);
	var delai=Math.round((timestamp-dt.getTime())/1000);
	var chrono=document.getElementById("chrono");
	chrono.innerHTML="Prochaine mise à jour du cours dans "+delai+" seconde"+( delai>1 ? "s":"" );
	if (delai <= 0) {
		getCours();
	} else {
		setTimeout("chrono()", 500);	
	}	
}

/* Démarrage du traitement */
getCours();

Ce qui donne ce module :

Emulation de la console

Le module d'affichage du cours du Bitcoin propose les fonctionnalités suivantes :
- Recherche du cours du Bitcoin via un appel AJAX vers le service de cryptocompare.com
- Traitement du retour au format JSON
- Enregistrement des données de cours dans le dataset
- Mise à jour de l'affichage
- Détection du clic de l'utilisateur sur les boutons de devises EUR ou USD et mise à jour de l'affichage
- Lancement d'un chrono pour mettre à jour le cours toutes les 60 secondes

Ce petit script utilise, en plus du DHTML qui sera détaillé dans les prochains paragraphes, de nombreux éléments du JavaScript comme le traitement du format JSON, les appels AJAX avec XMLHttpRequest, la création de timers avec setTimeout(), le formatage de nombre avec NumberFormat et le formatage de date et heure avec DateTimeFormat.

Retrouvez ce script dans un formulaire de conversion automatique de montants en BTC/$/US

La compatibilité entre navigateurs

Un mot sur la compatibilité entre navigateurs est indispensable, tant le souvenir de différences flagrantes hantent encore les rêves de développeurs web de plus d'une dizaine d'années d'ancienneté.

Il n'est maintenant plus nécessaire de prévoir plusieurs versions radicalement différentes de script en détectant le navigateur

Maintenant tous les navigateurs récents sur PC sont compatibles entre eux, au moins pour les fonctionnalités de base présentées ici. Les navigateurs mobiles sont eux encore plus en avance.

Dans tous les cas, la bonne habitude de tester vos développements sur plusieurs appareils et dans différentes configurations de connectivité et de performance reste d'actualité : Un script validé sur PC avec une connexion fibre risque de ne pas être aussi agréable sur un smartphone peu puissant avec un accès réseau limité en situation de mobilité.

Accéder aux éléments du DOM

Trouver un élément par son identifiant

La première fonction à laquelle on pense en parlant de DHTML est getElementById("identifiant") qui retourne un objet JavaScript de type HTMLElement correspondant au premier élément HTML du document ayant la propriété id="identifiant". C'est cet objet que le JavaScript va pouvoir manipuler pour modifier son contenu, son apparence, sa position, ses classes CSS et même le détruire.

Ainsi l'objet div suivant :

<div id="monDiv">Contenu du DIV</div>	

sera accessible en JavaScript par cette simple ligne :

var element=document.getElementById("monDiv")

Attention, getElementById() retourne null si aucun élément n'est trouvé dans le document. Il n'est donc pas possible de l'utiliser par la suite sans provoquer une erreur JavaScript.

De nombreuses autres fonctions, au fonctionnement similaire, sont également disponibles pour faciliter l'accès à tous les éléments du document. Ces fonctions retournent un ou plusieurs objets HTMLElement.

Trouver un élément par son tag

Pour récupérer tous les éléments dont la balise est <tag>, utilisez getElementsByTagName("tag"). Notez le s à getElements qui indique que le retour est un tableau d'éléments HTML. Pour traiter chacun des éléments retournés, il sera nécessaire de faire une boucle sur le tableau ou d'utiliser la méthode rapide forEach().

Trouver un élément par ses propriétés CSS

Pour récupérer tous les éléments possédant une certaine classe, utilisez getElementsByClassName("className").

Les méthodes querySelector() et querySelectorAll() attendent en paramètre une chaîne de caractères au format CSS.

Attention avec les sélecteurs trop complexes : les incompatibilités sont possibles dans certains navigateurs qui n'implémentent cette recherche que de manière plus récente.

Modifier le contenu d'un élément

Le contenu d'un élément est accessible et modifiable par sa propriété innerHTML. Cette propriété contient une chaîne de caractères au format HTML qu'il est possible de modifier par programmation. Chaque modification a un impact direct dans le rendu sur la page.

Il est donc très facile de modifier le contenu d'un élément. Dans l'exemple du bitcoin, nous utilisons la syntaxe :

document.getElementById("cours").innerHTML=valeur+" $";

Stocker des informations dans le document

Il existe un ensemble de propriétés de balise valides au sens de la norme W3C sur les. Toutes ces propriétés de balise sont accessibles par la notation pointée sur dataset. L'exemple Bitcoin utilise largement cette possibilité pour stocker les informations reçues de l'appel AJAX et les réutiliser lors des interactions de l'utilisateurs.

Dans l'exemple Bitcoin, les informations sont stockées avec à partir du retour au format JSON :

var cours = document.getElementById("cours");
cours.dataset.euro=json.EUR;
cours.dataset.usd=json.USD;

Dans le document, l'élément HTML id="cours" contient ce type de valeurs :

<div id="cours" 
 data-timestamp="1511885079594" 
 data-last="17:03:39" 
 data-euro="8356.15" 
 data-usd="9902.12" 
 data-devise="USD">
    9 902,12 $
</div>

Vous pouvez l'observer facilement sur la page grâce à la console du navigateur et à l'inspecteur de document.

Modifier le style des éléments

Les classes d'un élément sont accessibles par className, chaîne de caractères équivalente à la propriété class de la balise ou par classList, tableau contenant toutes les classes de l'élément.

Tous les éléments de style d'un élément HTML sont accessibles par la propriété style. Le JavaScript reprend l'ensemble des propriétés CSS. Leur nom est identique. Il suffit de remplacer les - dans les noms CSS par une majscule. Ainsi, background-color devient style.backgroundColor en JS.

Il est temps de regrouper les premières notions de DHTML dans un exemple complet, avec quelques éléments HTML à manipuler :

<div id="monDiv" class="monDiv1 monDiv2"></div>

Et la partie script :

var monDiv=document.getElementById("monDiv");
console.log(monDiv);	
console.log(monDiv.classList);
monDiv.innerHTML="Contenu ajouté par le script";
monDiv.style.border="2px solid #E00";

Le script accède à l'élément monDiv, affiche ses classes dans la console, modifie son contenu et lui ajoute une bordure rouge :

Emulation de la console

Créer et supprimer des éléments

Si innerHTML permet de modifier le contenu d'un élément avec du code HTML et donc d'ajouter des éléments facilement, il existe d'autres fonctions plus efficaces pour créer des éléments et les ajouter précisément dans le document.

Crééer un élément

createElement(tagName) retourne un objet HTML ayant la balise tagName.

L'appel à setAttribute() permet de lui affecter des propriétés et leurs valeurs.

Ajouter un élément au document

La méthode appendChild(enfant) ajoute l'objet enfant en dernière position sur l'objet sur lequel elle est appelée.

La méthode insertBefore(enfant, position) ajoute l'objet enfant avant l'objet position.

Supprimer un élément

De manière réciproque, la méthode removeChild(enfant) supprime l'objet enfant de l'objet sur lequel elle est appelée.

Ce script va récupérer tous les éléments h2 de la page pour les surligner :

Avec la fonction de surlignage :

Cliquez sur le bouton pour surligner le plan de la page :

Emulation de la console

Les frameworks

Des développeurs de talent ont partagé leurs travaux sur l'optimisation des accès au DOM, en rendant le code plus compact, plus lisible et en gérant les cas particuliers entre navigateurs de manière transparente pour l'utilisateur.

Lisez l'introduction à jQuery, le framework indispensable à connaître

Tutoriel écrit par webmaster mis à jour le

Nouveautés du moment sur le site

Maj tuto DHTML

Traiter un formulaire avec PHP

Les notifications du navigateur

Chercher une fonction, un objet, ...

Le 18/12/2017 15:42:34 sur php7 en 15.62 ms