Modifier le DOM avec JavaScript

Alexandre Niveau - Jean-Marc Lecarpentier
GREYC — Université de Caen
En partie adapté des cours de Hervé Le Crosnier

DOM : Document Object Model

Objectifs :

  • Disposer en mémoire vive d'une représentation en arbre d'un document
  • Définir comment modifier cet arbre de façon unifiée pour tous les navigateurs
  • Page web vue = Représentation graphique de l'arbre
  • Changement de l'arbre ⇒ changement de la vue
  • Document Object Model : spécification du WhatWG et norme du W3C

Nécessité du DOM

  • Les programmes en JavaScript sont exécutés par le navigateur
  • Leur but : modifier la page HTML/CSS en fonction des actions de l'internaute
  • Il faut donc avoir un modèle de la page et de sa structure, ainsi que des fonctions permettant de manipuler ce modèle : une API (application programming interface)
  • Le modèle des pages HTML (et XML) s'appelle le DOM, document object model
  • Langage utilisé dans les navigateurs : Javascript
  • Notions de DOM et fonctionnalités existantes dans d'autres langages (Python, Java, PHP, etc)

Historique du DOM

  • 1996 : sortie de JavaScript avec Netscape 2.0 puis de JScript avec IE 3.0
    • Les deux versions ont un DOM majoritairement compatible, JScript étant un portage de JavaScript
    • On l'appelle souvent DOM niveau 0
  • 1997 : Netscape et IE versions 4.0, développés en parallèle, introduisent des modifications incompatibles dans leurs DOM
  • Il fallait donc plusieurs versions de chaque programme pour une même page web…
  • 1998 : Standardisation par le W3C du DOM niveau 1
  • DOM 2 en 2000, DOM 3 en 2004, DOM 4 en 2014
  • DOM Living Standard édité par le WhatWG unifie les anciennes normes et les implémentations existantes dans les navigateurs

HTML ⇒ DOM ⇒ Vue

  • Navigateur = parseur HTML + moteur graphique
    • Parseur HTML : construit l'arbre DOM en mémoire
    • Moteur graphique : construit une représentation de l'arbre DOM, suivant les règles données dans les CSS
Du HTML au DOM puis la vue
Passage du HTML au DOM puis à la Vue

Modification du DOM

  • JavaScript : implémente l'API DOM ⇒ possibilité de transformer l'arbre
  • Toute modification de l'arbre DOM est immédiatement répercutée dans la représentation graphique
  • Attention : l'arbre DOM est modifié dans la mémoire du navigateur, mais « afficher le code source » montre toujours le code de départ !
  • Pour voir le code HTML correspondant à l'état réel du DOM à tout instant, utiliser l'inspecteur de Firefox ou Chrome
Modification du DOM reflétée dans la vue
Modification du DOM reflétée dans la Vue

Types de nœuds

Arbre DOM
Voir le code HTML de cet arbre
  • Noeud de type élément : représente un élément HTML
  • Possibilité de changer les nœuds éléments, par ex. changer ses attributs
  • Noeud de type texte : représente du texte
  • Possibilité de changer le texte seulement
  • Noeud de texte est forcément une feuille de l'arbre
  • Noeud de type élément a des fils de type élément et/ou texte
  • Noeud de type élément est une feuille de l'arbre si c'est un élément vide

Les principaux objets du DOM

Voir la référence sur MDN

  • Document : le document (élément racine) duquel on a construit le DOM
  • Node : les nœuds, qui peuvent être de différents types :
    • Element : nœuds éléments HTML
    • CharacterData : nœuds de texte

Implémentation de l'API DOM avec JavaScript

  • Les scripts en JavaScript permettent au navigateur d'agir sur l'arbre DOM du document en cours de visualisation
  • Les objets du DOM sont implémentés par des objets en JavaScript
  • On peut accéder aux propriétés des divers objets (document, nœud, élément, etc)
  • Des fonctions ou méthodes permettent d'agir sur ces objets

L'objet document

  • Modélise le document manipulé
  • L'élément racine du document : document.documentElement
  • L'élément <body> du document : document.body
  • Obtenir un élément par son identifiant : document.getElementById("toto")
  • Plus des fonctions pour créer des nœuds (cf. suite du cours)
Démo (en console)

Objets de de type Node

Modélise tous les types de nœuds, que ce soit des élements ou non
Fonctionnalités des objets Node :
  • Liste des fils : n.childNodes
  • Nombre de fils : n.childNodes.length
  • Premier fils : n.firstChild
  • Dernier fils : n.lastChild
  • Nœud parent : n.parentNode
  • Frère suivant : n.nextSibling
  • Frère précédent : n.previousSibling

Attention aux nœuds de texte vides !

Démo (en console)

Objet de type Element

Généralement, on préfère parcourir le DOM en ignorant les nœuds de texte (notamment à cause des nœuds de texte vides)

Les fonctionnalités des objets de type Element permettent de le faire simplement :
  • Liste des éléments fils : e.children
  • Nombre d'éléments fils : e.childElementCount plus efficace que e.children.length
  • Premier/dernier fils qui est un élément : e.firstElementChild et e.lastElementChild
  • Frère suivant/précédent qui est un élément : e.nextElementSibling et e.previousElementSibling
  • Obtenir un attribut : e.getAttribute(nomAttribut)
  • Modifier un attribut : e.setAttribute(nomAttribut, nouvelleValeur)
Démo (en console)

Obtenir des éléments de l'arbre DOM

  • Avec l'identifiant d'une élément : document.getElementById("toto")
  • Avec un sélecteur CSS :
    • document.querySelector("#tutu div.erreur") : renvoie le premier élément qui correspond au sélecteur CSS donné
    • document.querySelectorAll("#tutu div.erreur") : renvoie une liste statique de tous les éléments correspondant au sélecteur CSS donné
Démo (en console)

Modifier le style CSS

  • On peut modifier les propriétés de style de chaque objet DOM :
    document.getElementById("toto").style.color="green";
    document.getElementById("toto").style.backgroundColor="blue";
    document.getElementById("toto").style.display="none";
    
  • Chaque propriété CSS correspond à un attribut (les tirets sont remplacés par du camelCase)
Démo

Récupérer le style CSS

  • Attention, les propriétés récupérées avec .style correspondent uniquement au contenu de l'attribut HTML style="…"
  • Pour récupérer le style couramment appliqué par le navigateur (depuis une feuille de style par exemple), il faut utiliser getComputedStyle(element)
Démo

Manipuler les classes

  • En général on ne manipulera pas directement le style : séparation entre présentation (CSS) et comportement (JS)
  • La façon propre de faire est de passer par des classes, dont le style est défini indépendamment du script
  • Pour manipuler les classes, on utilisera la propriété classList des éléments (ne marche pas pour IE<10) :
    toto = document.getElementById("toto");
    toto.classList.add("tutu");
    toto.classList.remove("titi");
    if (toto.classList.contains("foobar"))
        toto.classList.toggle("erreur");
    

Modifier le texte d'un nœud

  • Pour modifier le texte, par ex. d'un paragraphe, on peut récupérer son nœud textuel et modifier son attribut nodeValue :
    let para = document.querySelector("p");
    alert(para.firstChild.nodeValue);  // affiche le texte
    para.firstChild.nodeValue = "nouveau texte !";
    
  • Pas très robuste, car le paragraphe peut contenir d'autres nœuds (par ex. un élément em), auquel cas on ne remplace pas tout.
  • Solution plus simple : attribut textContent, qui correspond au texte concaténé de tous les descendants du nœud
  • En modifiant textContent on remplace tous les descendants du nœud par un unique nœud de texte

L'attribut innerHTML

  • Parfois on veut remplacer le contenu du nœud par d'autres nœuds
  • innerHTML fonctionne de la même façon que textContent, mais en « gardant les éléments HTML »
  • Peut provoquer des failles de sécurité (injections) et n'est pas très efficace
  • Très pratique pour les tests et les bidouilles rapides, mais :
  • interdiction de l'utiliser les TPs

Créer des branches de l'arbre DOM

  • On peut créer une branche d'arbre DOM
    let newP = document.createElement("p");
    let newTxt = document.createTextNode("contenu");
    newP.appendChild(newTxt);
    
  • puis l'attacher comme fils à un nœud existant
    let maDiv = document.getElementById("toto");
    maDiv.appendChild(newP);
    

Créer des éléments avec des attributs

Les éléments que l'on crée n'ont aucun attribut, il faut les ajouter explicitement

let monA = document.createElement("a");
monA.setAttribute("href", "http://example.com");
monA.setAttribute("title", "Exemple");
let monText = document.createTextNode("Le site example.com");
monA.appendChild(monText);
maDiv.appendChild(monA);

Autres manipulations

  • Supprimer un fils : papa.removeChild(toto)
  • Insérer un fils avant un autre : papa.insertBefore(nouveau, toto)
  • Cloner un nœud : toto.cloneNode(true)
    • le paramètre indique qu'on veut cloner le nœud avec tous ses descendants (c'est généralement ce qu'on veut)
    • attention, il faut attacher le nouveau nœud dans l'arbre ensuite)
  • Remplacer un fils par un autre : papa.replaceChild(nouveau, toto)

Bilan de cours

  • Le DOM permet :
    • de construire une représentation en arbre d'un document
    • d'offrir une interface commune d'accès et modification du document, quelque soit le langage applicatif
  • Grâce à l'implémentation de l'interface DOM dans le langage JavaScript, nous pouvons directement dans le navigateur :
    • repérer un élément dans l'arbre DOM
    • modifier ses attributs, notamment ses classes, pour modifier son apparence
    • ajouter ou supprimer des éléments du DOM