JavaScript pour Pro

Document Object Model

= DOM

API = Application Programming Interface = ensemble d'outils qui permettent de faire communiquer entre eux plusieurs programmes, langages (règles)

En HTML, une balise HTML est une balise ouvrante, son contenu et sa balise fermante.
En JavaScript, une balise HTML est un objet appelé élément HTML.

Le DOM est une API (un ensemble d'outils intégré au navigateur) qui permet, via le JavaScript, d'accéder au code XML et/ou HTML.

L'objet : window

L'objet window est un objet global qui représente la fenêtre du navigateur. Il est implicite, c'est-à-dire qu'il n'est pas nécessaire de le spécifier.

alert() est une méthode de l'objet Windows.
parseInteger(), parseFloat(), isNaN(), ... sont des fonctions globales (n'appartenant à aucun objet).

On sait que la variable locale "écrase" la variable globale de même nom. Toutefois, nne variable globale est, en réalité, une propriété de l'objet window. Il est donc possible d'y accéder en écrivant : window.ma_variable_globale ...

Quelque soit l'endroit (à l'intérieur ou à l'extérieur d'une fonction), toute variable non déclarée - via le mot-clé var - devient une variable globale, donc une propriété de l'objet window.

screen

L'objet screen dispose de plusieurs propriétés. Les deux propriétés les plus utilisées sont : .width et .height qui retournent respectivement la largeur et la hauteur de l'écran, en pixels.

Plus d'info sur MDN

innerWidth, innerHeight

Ces deux propriétés retournent respectivement la largeur et la hauteur de la fenêtre de navigation (incluant les éventuelles barres de défilement), en pixels.

Plus d'info sur innerWidth et innerHeight

Code pour redimensionner un élément (identifié, de 400px de large et 420px de haut) en fonction de la taille de la fenêtre :

function newSize(){
  let rapport = innerWidth/screen.width
  document.getElementById("cerclesSVG").style.width  = 400*rapport+"px"
  document.getElementById("cerclesSVG").style.height = 420*rapport+"px"
}

window.addEventListener("resize", newSize)

L'objet : document

L'objet document est un sous-objet de l'objet window. Il représente est le fichier HTML (situé dans la mémoire du navigateur).

Le fichier HTML est vu comme un ensemble de balises dont la première (la racine) est <html>. Hormis la balise racine, toute balise a un parent et souvent des enfants.

Tout élément est appelé nœud (node en anglais). Sont aussi des éléments : le contenu de la balise, ainsi que ses attributs.

Le texte est un nœud de type text

Accéder à une balise

Pour accéder à un élément HTML (une balise), deux méthodes existent  : getElementById() et querySelector().

Cette dernière méthode est plus rarement utilisée.

Pour accéder à un tableau de tous éléments HTML, trois méthodes existent : querySelectorAll(), getElementsByTagName(), getElementsByName()

Ces deux dernières méthodes sont rarement utilisées.

Dans tous les cas, si la cible n'existe pas, ces méthodes retournent : null

getElementById()

Retourne qu'un seul élément (ou null)

Cette méthode ne s'utilise que sur l'objet document
=> var elem = document.getElementById(valeur_de_l_attribut_id_de_la_balise_visee)

querySelectorAll()

Retourne qu'un tableau d'éléments (ou null)

Le paramètre est une string représentant un sélecteur CSS.

Cette méthode ne s'utilise que sur l'objet document
=> var elem = document.querySelectorAll(selector_CSS)

Tous les éléments HTML sont du type : Node. Par exemple, un HTMLDivElement, est un sous-objet de HTMLElement qui est un sous-objet de Element qui est un sous-objet de Node.

Tous les objets ont des propriétés et des méthodes et les objets-enfants ont, en plus, les propriétés et méthodes de leur objet-parent. C'est héritage.

Interface Node

Interface Node

Une balise HTML peut contenir des attributs et/ou un contenu.

Le contenu d'une balise peut être une balise-fille.

Les attributs d'une balise

.getAttribute()

La méthode .getAttribute('nom_de_l_attribut') retourne une string (= valeur de l'attribut, qui peut être vide ("")) ou null

var lien = document.getElementById("myLien");
var URL = lien.getAttribute("href");

.setAttribute()

La méthode .setAttribute() modifie la valeur d'un attribut existant (ou ajoute un nouvel attribut). Le premier paramètre, de type string, est le nom de l'attribut; le second, de type string, sa valeur.

La valeur d'un attribut booléen, tel que disabled, doit être une chaîne vide (""). Quelle que soit la valeur, elle sera évaluée à true. L'absence de l'attribut signifie que sa valeur est false.

lien.setAttribute("href", "http://www.nouveauSite.com");
bouton1.setAttribute("disabled", "");

Hormis les attributs class et for (de la balise label) qui sont en JavaScript des mots réservés, les attributs sont directement accessibles.

var URL = lien.href;

Pour viser l'attribut HTML class, on utilisera, en JavaScript, la propriété className

document.getElementById('myColoredDiv').className = 'blue';

Pour viser l'attribut HTML for, on utilisera, en JavaScript, la propriété htmlFor

.removeAttribute()

La méthode .removeAttribute() supprime un attribut (existant). Le premier paramètre, de type string, est le nom de l'attribut.

.createAttribute()

Rarement utilisée, la méthode .createAttribut(), appliquée à document, permet de créer un attribut (un nœud de type 2).

.attributes

Rarement utilisée, la propriété .attributes est un tableau associatif (clé/valeur) ayant comme clés : name et value.

.hasAttributes()

Rarement utilisée, la méthode .hasAttributes(), appliquée à un noeud de type 1, retourne true ou false

.getAttributeNode()

Rarement utilisée, la méthode .getAttributeNode() Renvoie le nœud d'attribut spécifié pour l'élément courant

.setAttributeNode()

Rarement utilisée, la méthode .setAttributeNode(), appliquée à un noeud de type 1, ajoute un nouveau nœud Attr à l'élément courant

.removeAttributeNode()

Rarement utilisée, la méthode .removeAttributeNode(), appliquée à un noeud de type 1, enlève l'attribut spécifié de l'élément courant.

Le contenu d'une balise

Le contenu d'une balise est ce qui est entre sa balise ouvrante et sa balise fermante. Ce contenu peut donc contenir d'autres balises ...

.innerHTML

Cette propriété permet de récupérer le contenu d'une balise (sous la forme d'un code HTML) et, inversément, lui affecter du contenu (sous la forme d'un code HTML).

Toutefois, une des spécifications du HTML5, interdit, pour des raisons de sécurité, qu'une balise <script> insérée via .innerHTML puisse s'exécuter.

Il existe toujours un risque de sécurité chaque fois que vous utilisez innerHTML pour définir des chaînes sur lesquelles vous n'avez aucun contrôle. Pour cette raison, il est recommandé de ne pas utiliser innerHTML pour insérer du texte brut à la place, utilisez Node.textContent.

Il est toutefois possible de créer la balise <script> par le biais de la méthode createElement()

Code HTML "calculé" et affiché automatiquement de ce qui précède :


        

Ce code HTML correspond à celui qui est dans la mémoire du navigateur (et non celui du fichier HTML)

Code JavaScript de la fonction qui génère automatiquement le code HTML :

function codeHtml(id_zone, id_pre){
  /* id_zone = ID de la balise dont le contenu (code HTML) sera affiché
     id_pre  = ID de la balise <pre> qui affichera ce code HTML */
  var code = document.getElementById(id_zone).innerHTML.replace(/</g,"<");
  document.getElementById(id_pre).innerHTML=code;
}

.textContent

La propriété .textContent fonctionne comme .innerHTML hormis le fait que seul le texte est récupéré, et non les balises.

Naviguer entre les nœuds

Une balise n'a qu'un seul parent (sauf la balise <html>) et peut avoir des attribut et/ou contenu. Ce contenu est du texte et/ou une balise ou un ensemble composé de textes et de balises.

Une balise peut avoir plusieurs enfants. Naviguer entre ces balises = naviguer entre les nœuds de type balise.

Plus d'info sur MDN : Propriétés et méthodes de l'objet Node

.parentNode

La propriété parentNode permet d'accéder à son élément parent (à un nœud de type élément)

.nodeType

La propriété nodeType retourne un nombre qui correspond à un type de nœud.

Voici les trois types de nœud (les plus courants)

.nodeName

La propriété nodeName retourne une string dont la valeur correspond au nom de la balise, de l'attribut ou "#text"

La valeur de retour peut être en minuscules (pour des fichier XML) et en majuscules (pour des fichiers HTML)

.firstChild

La propriété firstChild, en lecture seule, permet d'accéder au premier enfant d'un nœud (d'un des trois types)

.lastChild

La propriété lastChild, en lecture seule, permet d'accéder au dernier enfant d'un nœud (d'un des trois types)

.firstChild

La propriété firstElementChild, en lecture seule, permet d'accéder au premier enfant d'un nœud de type 1 (élément)

.lastElementChild

La propriété lastChild, en lecture seule, permet d'accéder au dernier enfant d'un nœud de type 1 (élément)

.lastElementChild

La propriété lastChild, en lecture seule, permet d'accéder au dernier enfant d'un nœud de type 1 (élément)

.nodeValue

La propriété nodeValue retourne une string dont la valeur correspond à la valeur de l'attribut(si type=2), au texte (si type=3) ou null (si type=1)

.childNodes

La propriété childNodes retourne un tableau contenant la liste des enfants d'un élément

.hasChildNodes()

La méthode hasChildNodes() retourne le nombre de nœuds dans un tableau.

.nextSibling

La propriété nextSibling, en lecture seule, permet d'accéder au nœud suivant.

.previousSibling

La propriété previousSibling, en lecture seule, permet d'accéder au nœud précédent.

.nextElementSibling

La propriété nextElementSibling, en lecture seule, permet d'accéder au nœud suivant de type 1.

.previousElementSibling

La propriété previousElementSibling, en lecture seule, permet d'accéder au nœud précédent de type 1.

Attention. Les espaces entre les éléments et les retours à la ligne sont considérés comme des nœuds textuels !

Créer et insérer des éléments

L'ajout d'un élément HTML se fait en deux temps :

  1. Création de l'élément (avec ses attributs et contenu)
  2. Insertion dans le document

.createElement()

La méthode .createElement(), appliquée à l'objet document, permet de créer un élément (un nœud de type 1).

  var paragraphe = document.createElement("p");

Puis, on ajoute des attributs.

  paragraphe.id = "myParagraphe";
  paragraphe.setAttribute("title","monParagraphe");

Puis, le texte entre les balises (pas d'autres balises).

  paragraphe.textContent = "Texte créé en un clic !";

La balise ainsi créée (dans la mémoire du navigateur) ne sera ajoutée qu'après avoir utilisé une seconde méthode : appendChild()

.createTextNode()

La méthode .createTextNode(), appliquée à document, permet de créer un texte (un nœud de type 3).

  var nœudText = document.createTextNode("Texte créé en un clic !");

Le texte ainsi créé (dans la mémoire du navigateur) ne sera ajouté qu'après avoir utilisé une seconde méthode : appendChild()

La méthode .innerHTML() de l'objet document est plus puissante car elle permet d'ajouter (ou récupérer) du contenu HTML (texte + balise) et devoir ensuite passer ensuite par une seconde méthode (appendChild())

.cloneNode()

Peu utilisée, cette méthode, appliquée sur le nœud, retourne une copie du nœud. Son paramètre, unique et facultatif, indique la "profondeur" de la copie. Par défaut, il vaut false. Ce qui signifie que les enfants ne seront pas repris dans le nœud copié (= copie superficielle). S'il vaut true, ils seront repris (= copie profonde).

Le nœud ainsi créé (dans la mémoire du navigateur) ne sera ajouté qu'après avoir utilisé une seconde méthode : appendChild()

Attention. Il est alors possible de dupliquer la valeur de l'attribut id d'une balise. Or, cette valeur doit être unique dans tout le document. De plus, même en cas de copie profonde, les événements créés ( avec addEventListener()  que nous aborderons prochainement) et associés au nœud, ne sont pas copiés.

.appendChild()

La méthode appendChild() est appliquée sur le nœud parent et a pour paramètre l'élément créé (qui sera alors le dernier enfant du parent). Cet enfant, paramètre de cette méthode, est soit une balise sans contenu soit le contenu - uniquement - textuel d'une balise.

document.getElementById(IDparent).appendChild(enfant);

Le texte entre la balise de début et de fin est un nœud (textuel)

paragraphe.appendChild(nœudText);

La valeur renvoyée par la méthode appendChild() est l'enfant ajouté

var span = document.body.appendChild(document.createElement('span'));
span.innerHTML = 'Du texte en plus !';

.removeChild()

La méthode removeChild() est appliquée sur le nœud parent et a pour paramètre l'élément à supprimé.

Elle retourne l'enfant supprimé. L'enfant ainsi récupéré permet ainsi de le réinsérer dans n'importe quelle autre partie du document)

parent.removeChild(enfant);

Code HTML :


        

Code JavaScript :

function addParagraphe(idParent,idEnfant){
  var paragraphe = document.createElement("p");
  paragraphe.id = idEnfant;
  paragraphe.setAttribute("title","monParagraphe");
  paragraphe.textContent = "Texte créé en un clic !";
  document.getElementById(idParent).appendChild(paragraphe);
}

function addParagraphe2(IDparent,IDchild){

  /* Il est plus performant de créer tous les éléments au début, */
  /* puis les différentes opérations d'affection. */
  /* Enfin, l'insertion des éléments les uns dans les autres */
  /* et, pour terminer, l'insertion dans le document. */

  var paragraphe = document.createElement("p");
  paragraphe.id = IDchild;
  paragraphe.setAttribute("title","monParagraphe");

  var nœudText = document.createTextNode("Texte créé en un  !");

  paragraphe.appendChild(nœudText);
  document.getElementById(IDparent).appendChild(paragraphe);
}

function removeParagraphe(IdParent,IdEnfant){
  var parent = document.getElementById(IdParent);
  var enfant = document.getElementById(IdEnfant);
  parent.removeChild(enfant);
}

La méthode removeChild() n'est pratique que lorsque plusieurs enfants existent. Pour les supprimer tous, écrire .innerHTML="" est plus simple.

.replaceChild()

La méthode replaceChild() est appliquée sur le nœud parent et a deux paramètres le nouveau nœud et l'ancien nœud.

parent.replaceChild(enfantNew,enfantOld);

.insertBefore()

La méthode insertBefore() appliquée sur le nœud parent et a deux paramètres Le premier est l'enfant à insérer, le second est l'enfant avant lequel le premier va être inséré.

parent.insertBefore(noeudNew, noeudSuivant);

Si le second paramètre, obligatoire, est null, cette méthode ajoutera alors après le dernier enfant. (comme la méthode .appendChild())

La méthode .insertAfter() n'existe pas. Toutefois, il est possible de créer une fonction.

function insertAfter(newElement, afterElement) {
    var parent = afterElement.parentNode;

    if (parent.lastChild === afterElement) {
      /* Si le dernier élément est le même que l'élément après lequel on veut insérer,
       il suffit de faire appendChild()*/
        parent.appendChild(newElement);
    } else { /* Dans le cas contraire, on fait un insertBefore() sur l'élément suivant */
        parent.insertBefore(newElement, afterElement.nextSibling);
    }
}