Voyons plus en détail les nÅuds DOM.
Dans ce chapitre, nous verrons plus en détail ce quâils sont et découvrirons leurs propriétés les plus utilisées.
Classes de nÅud DOM
Différents nÅuds DOM peuvent avoir des propriétés différentes. Par exemple, un nÅud élément correspondant à la balise <a> a des propriétés liées aux liens, et celui correspondant à <input> a des propriétés liées aux entrées, etc. Les nÅuds texte ne sont pas identiques aux nÅuds élément. Mais il existe également des propriétés et des méthodes communes à chacun dâentre eux, car toutes les classes de nÅuds DOM forment une hiérarchie unique.
Chaque nÅud DOM appartient à la classe intégrée correspondante.
La racine de la hiérarchie est EventTarget, hérité par Node, et dâautres nÅuds DOM en héritent.
Voici lâimage, les explications à suivre :
Les classes sont :
-
EventTarget â est la classe racine âabstraiteâ pour tout.
Les objets de cette classe ne sont jamais créés. Ils servent de base, afin que tous les nÅuds DOM supportent les soi-disant âévénementsâ, nous les étudierons plus tard.
-
Node â est également une classe âabstraiteâ, servant de base aux nÅuds DOM.
Elle fournit la fonctionnalité de lâarborescence de base :
parentNode,nextSibling,childNodeset ainsi de suite (ce sont des getters). Les objets de la classeNodene sont jamais créés. Mais il existe dâautres classes qui en héritent (et héritent donc de la fonctionnalitéNode). -
Document, pour des raisons historiques souvent héritées par
HTMLDocument(bien que la dernière spécification ne le dicte pas) â est un document dans son ensemble.Lâobjet global
documentappartient exactement à cette classe. Il sert de point dâentrée au DOM. -
CharacterData â une classe âabstraiteâ, héritée par :
-
Element â est la classe de base des éléments DOM.
Elle fournit une navigation au niveau des éléments comme
nextElementSibling,childrenet des méthodes de recherche commegetElementsByTagName,querySelector.Un navigateur prend en charge non seulement HTML, mais également XML et SVG. Ainsi, la classe
Elementsert de base à des classes plus spécifiques :SVGElement,XMLElement(nous nâen avons pas besoin ici) etHTMLElement. -
Enfin, HTMLElement est la classe de base pour tous les éléments HTML. Nous travaillerons avec lui la plupart du temps.
Elle est héritée par des éléments HTML concrets :
- HTMLInputElement â la classe pour les éléments
<input>, - HTMLBodyElement â la classe pour les éléments
<body>, - HTMLAnchorElement â la classe pour les éléments
<a>, - â¦etc.
- HTMLInputElement â la classe pour les éléments
Il existe de nombreuses autres balises avec leurs propres classes qui peuvent avoir des propriétés et des méthodes spécifiques, tandis que certains éléments, tels que <span>, <section>, <article> nâont pas de propriétés spécifiques, ce sont donc des instances de la classe HTMLElement.
Ainsi, lâensemble complet des propriétés et des méthodes dâun nÅud donné est le résultat de la chaîne de lâhéritage.
Par exemple, considérons lâobjet DOM pour un élément <input>. Il appartient à la classe HTMLInputElement.
Il obtient les propriétés et les méthodes en superposition de (répertoriées dans lâordre dâhéritage) :
HTMLInputElementâ cette classe fournit des propriétés spécifiques à lâentrée,HTMLElementâ elle fournit des méthodes dâélément HTML communes (et des getters/setters),Elementâ fournit des méthodes dâélément génériques,Nodeâ fournit des propriétés de noeud DOM communes,EventTargetâ apporte du support aux événements (à couvrir),- â¦et finalement il hérite de
Object, donc les méthodes âplain objectâ commehasOwnPropertysont également disponibles.
Pour voir le nom de la classe de noeud DOM, nous pouvons rappeler quâun objet a généralement la propriété constructor. Il fait référence au constructeur de classe, et constructor.name est son nom :
alert( document.body.constructor.name ); // HTMLBodyElement
â¦Or we can just toString it:
alert( document.body ); // [object HTMLBodyElement]
Nous pouvons également utiliser instanceof pour vérifier lâhéritage :
alert( document.body instanceof HTMLBodyElement ); // true
alert( document.body instanceof HTMLElement ); // true
alert( document.body instanceof Element ); // true
alert( document.body instanceof Node ); // true
alert( document.body instanceof EventTarget ); // true
Comme nous pouvons le voir, les nÅuds DOM sont des objets JavaScript normaux. Ils utilisent des classes basées sur des prototypes pour lâhéritage.
Câest aussi facile à voir en sortant un élément avec console.dir (elem) dans un navigateur. Là , dans la console, vous pouvez voir HTMLElement.prototype, Element.prototype et ainsi de suite.
console.dir(elem) versus console.log(elem)La plupart des navigateurs prennent en charge deux commandes dans leurs outils de développement : console.log et console.dir. Elles sortent leurs arguments dans la console. Pour les objets JavaScript, ces commandes font généralement la même chose.
Mais pour les éléments DOM, elles sont différents :
console.log(elem)affiche lâarborescence DOM de lâélément.console.dir(elem)affiche lâélément en tant quâobjet DOM, bon pour explorer ses propriétés.
Essayez les sur document.body.
Dans la spécification, les classes DOM ne sont pas décrites en utilisant JavaScript, mais une Interface description language spéciale (IDL), qui est généralement facile à comprendre.
Dans IDL, toutes les propriétés sont précédées de leurs types. Par exemple, DOMString, boolean et ainsi de suite.
En voici un extrait, avec des commentaires :
// Definir HTMLInputElement
// Le signe deux-points ":" signifie que HTMLInputElement hérite de HTMLElement
interface HTMLInputElement: HTMLElement {
// ici les propriétés et méthodes des éléments <input>
// "DOMString" signifie que la valeur d'une propriété est une chaîne de caractères
attribute DOMString accept;
attribute DOMString alt;
attribute DOMString autocomplete;
attribute DOMString value;
// propriété de valeur booléenne (true/false)
attribute boolean autofocus;
...
// maintenant la méthode : "void" signifie que la méthode ne renvoie aucune valeur
void select();
...
}
La propriété ânodeTypeâ
La propriété nodeType fournit une autre méthode âà lâancienneâ pour obtenir le âtypeâ dâun nÅud DOM.
Il a une valeur numérique :
elem.nodeType == 1pour les nÅuds élément,elem.nodeType == 3pour les nÅuds texte,elem.nodeType == 9pour lâobjet document,- il y a peu dâautres valeurs dans la spécification.
Par exemple :
<body>
<script>
let elem = document.body;
// examinons ceci : quel type de nÅud est dans elem ?
alert(elem.nodeType); // 1 => element
// et son premier enfant est...
alert(elem.firstChild.nodeType); // 3 => text
// pour l'objet document, le type est 9
alert( document.nodeType ); // 9
</script>
</body>
Dans les scripts modernes, nous pouvons utiliser instanceof et dâautres tests basés sur les classes pour voir le type de nÅud, mais parfois nodeType peut être plus simple. Nous pouvons seulement lire nodeType, pas le changer.
Balise : nodeName et tagName
Ãtant donné un nÅud DOM, nous pouvons lire son nom de balise dans les propriétés nodeName ou tagName :
Par exemple :
alert( document.body.nodeName ); // BODY
alert( document.body.tagName ); // BODY
Y a-t-il une différence entre tagName et nodeName ?
Bien sûr, la différence se reflète dans leurs noms, mais câest en effet un peu subtile.
-
La propriété
tagNameexiste uniquement pour les nÅudsElement. -
Le
nodeNameest défini pour toutNode:- pour les éléments, cela signifie la même chose que
tagName. - pour les autres types de nÅuds (texte, commentaire, etc.), il a une chaîne de caractères avec le type de nÅud.
En dâautres termes,
tagNameest uniquement pris en charge par les nÅuds élément (car il provient de la classeElement), tandis quenodeNamepeut dire quelque chose sur dâautres types de nÅuds. - pour les éléments, cela signifie la même chose que
Par exemple, comparons tagName et nodeName pour le document et un nÅud de commentaire :
<body><!-- commentaire -->
<script>
// pour le commentaire
alert( document.body.firstChild.tagName ); // undefined (pas un élément)
alert( document.body.firstChild.nodeName ); // #comment
// pour le document
alert( document.tagName ); // undefined (pas un élément)
alert( document.nodeName ); // #document
</script>
</body>
Si nous ne traitons que des éléments, nous pouvons utiliser à la fois tagName et nodeName â il nây a pas de différence.
Le navigateur a deux modes de traitement des documents: HTML et XML. Habituellement, le mode HTML est utilisé pour les pages Web. Le mode XML est activé lorsque le navigateur reçoit un document XML avec lâen-tête : Content-Type: application/xml+xhtml.
En mode HTML, tagName/nodeName est toujours en majuscule : câest BODY pour <body> ou <BoDy>.
En mode XML, la casse est conservée âen lâétatâ. De nos jours, le mode XML est rarement utilisé.
innerHTML: les contenus
La propriété innerHTML permet dâobtenir le HTML à lâintérieur de lâélément sous forme de chaîne de caractères.
Nous pouvons également le modifier. Câest donc lâun des moyens les plus puissants de modifier la page.
Lâexemple montre le contenu de document.body puis le remplace complètement :
<body>
<p>A paragraph</p>
<div>A div</div>
<script>
alert( document.body.innerHTML ); // lit le contenu actuel
document.body.innerHTML = 'The new BODY!'; // le remplace
</script>
</body>
Nous pouvons essayer dâinsérer du code HTML invalide, le navigateur corrigera nos erreurs :
<body>
<script>
document.body.innerHTML = '<b>test'; // oublié de fermer la balise
alert( document.body.innerHTML ); // <b>test</b> (corrigé)
</script>
</body>
Si innerHTML insère une balise <script>dans le document â elle devient une partie du HTML, mais ne sâexécute pas.
Attention : âinnerHTML+=â fait un écrasement complet
Nous pouvons ajouter du HTML à un élément en utilisant elem.innerHTML+="more html".
Comme ceci :
chatDiv.innerHTML += "<div>Hello<img src='smile.gif'/> !</div>";
chatDiv.innerHTML += "How goes?";
Mais nous devons faire très attention à le faire, car ce qui se passe nâest pas un ajout, mais une réécriture complète.
Techniquement, ces deux lignes font de même :
elem.innerHTML += "...";
// is a shorter way to write:
elem.innerHTML = elem.innerHTML + "..."
En dâautres termes, innerHTML+= fait ceci :
- Lâancien contenu est supprimé.
- Le nouveau
innerHTMLest écrit à la place (une concaténation de lâancien et du nouveau).
Comme le contenu est âremis à zéroâ et réécrit à partir de zéro, toutes les images et autres ressources seront rechargées.
Dans lâexemple chatDiv au-dessus de la ligne chatDiv.innerHTML+="How goes?" recrée le contenu HTML et recharge smile.gif (espérons quâil est mis en cache). Si chatDiv a beaucoup dâautres textes et images, alors le rechargement devient clairement visible.
Il existe également dâautres effets secondaires. Par exemple, si le texte existant a été sélectionné avec la souris, la plupart des navigateurs supprimeront la sélection lors de la réécriture de âinnerHTMLâ. Et sâil y avait un <input> avec un texte entré par le visiteur, alors le texte sera supprimé. Etc.
Heureusement, il existe dâautres façons dâajouter du HTML en plus de innerHTML, et nous les étudierons bientôt.
outerHTML : HTML complet de lâélément
La propriété outerHTML contient le code HTML complet de lâélément. Câest comme innerHTML plus lâélément lui-même.
Voici un exemple :
<div id="elem">Hello <b>World</b></div>
<script>
alert(elem.outerHTML); // <div id="elem">Hello <b>World</b></div>
</script>
Attention : contrairement à innerHTML, lâécriture dans outerHTML ne modifie pas lâélément. Au lieu de cela, il le remplace dans le DOM.
Oui, cela semble étrange, et câest étrange, câest pourquoi nous en faisons une note séparée ici. Jetez-y un oeil.
Prenons lâexemple :
<div>Hello, world!</div>
<script>
let div = document.querySelector('div');
// remplace div.outerHTML avec <p>...</p>
div.outerHTML = '<p>A new element</p>'; // (*)
// Wow! 'div' est toujours la même !
alert(div.outerHTML); // <div>Hello, world!</div> (**)
</script>
Ãa a lâair vraiment bizarre, non ?
Dans la ligne (*) nous avons remplacé div par <p>A new element</p>. Dans le document externe (le DOM), nous pouvons voir le nouveau contenu au lieu de <div>. Mais, comme nous pouvons le voir dans la ligne (**), la valeur de lâancienne variable div nâa pas changé !
Lâaffectation outerHTML ne modifie pas lâélément DOM (lâobjet référencé, dans ce cas, la variable âdivâ), mais le supprime du DOM et insère le nouveau HTML à sa place.
Donc, ce qui sâest passé dans div.outerHTML=... est :
diva été supprimé du document.- Un autre morceau du HTML
<p>A new element</p>a été inséré à sa place. diva toujours son ancienne valeur. Le nouveau HTML nâa été enregistré dans aucune variable.
Il est si facile de faire une erreur ici : modifiez div.outerHTML puis continuez à travailler avec div comme sâil contenait le nouveau contenu. Mais ce nâest pas le cas. Ce genre de chose est correcte pour innerHTML, mais pas pour outerHTML.
Nous pouvons écrire dans elem.outerHTML, mais nous devons garder à lâesprit que cela ne change pas lâélément dans lequel nous écrivons (âelemâ). Il place le nouveau HTML à sa place. Nous pouvons obtenir des références aux nouveaux éléments en interrogeant le DOM.
nodeValue/data : contenu du nÅud texte
La propriété innerHTML nâest valide que pour les nÅuds élément.
Dâautres types de nÅuds, tels que les nÅuds texte, ont leur contrepartie : les propriétés nodeValue et data. Ces deux sont presque les mêmes pour une utilisation pratique, il nây a que des différences de spécifications mineures. Nous allons donc utiliser data, car il est plus court.
Un exemple de lecture du contenu dâun nÅud texte et dâun commentaire :
<body>
Hello
<!-- Commentaire -->
<script>
let text = document.body.firstChild;
alert(text.data); // Hello
let comment = text.nextSibling;
alert(comment.data); // Commentaire
</script>
</body>
Pour les nÅuds texte, nous pouvons imaginer une raison de les lire ou de les modifier, mais pourquoi des commentaires ?
Parfois, les développeurs incorporent des informations ou des instructions de modèle dans HTML, comme ceci :
<!-- if isAdmin -->
<div>Welcome, Admin!</div>
<!-- /if -->
â¦Ensuite, JavaScript peut le lire à partir de la propriété data et traiter les instructions intégrées.
textContent: texte pur
Le textContent donne accès au texte à lâintérieur de lâélément : seulement le texte, moins tous les <tags>.
Par exemple :
<div id="news">
<h1>Headline!</h1>
<p>Martians attack people!</p>
</div>
<script>
// Headline! Martians attack people!
alert(news.textContent);
</script>
Comme nous pouvons le voir, seul le texte est renvoyé, comme si tous les <tags> étaient supprimés, mais le texte quâils contenaient est resté.
En pratique, la lecture dâun tel texte est rarement nécessaire.
Ecrire dans textContent est beaucoup plus utile, car il permet dâécrire du texte de âmanière sûreâ.
Disons que nous avons une chaîne de caractères arbitraire, par exemple entrée par un utilisateur, et que nous voulons lâafficher.
- Avec
innerHTMLnous allons lâinsérer âau format HTMLâ, avec toutes les balises HTML. - Avec
textContentnous allons lâinsérer âen tant que texteâ, tous les symboles sont traités littéralement.
Comparez les deux :
<div id="elem1"></div>
<div id="elem2"></div>
<script>
let name = prompt("What's your name?", "<b>Winnie-the-Pooh!</b>");
elem1.innerHTML = name;
elem2.textContent = name;
</script>
- La première
<div>obtient le nom âen HTMLâ : toutes les balises deviennent des balises, nous voyons donc le nom en gras. - La seconde
<div>obtient le nom âsous forme de texteâ, donc nous voyons littéralement<b>Winnie-the-pooh!</b>.
Dans la plupart des cas, nous attendons le texte dâun utilisateur et souhaitons le traiter comme du texte. Nous ne voulons pas de HTML inattendu sur notre site. Une affectation à textContent fait exactement cela.
La propriété âcachéeâ
Lâattribut âhiddenâ (caché) et la propriété DOM spécifient si lâélément est visible ou non.
Nous pouvons lâutiliser dans le HTML ou lâattribuer en utilisant JavaScript, comme ceci :
<div>Both divs below are hidden</div>
<div hidden>With the attribute "hidden"</div>
<div id="elem">JavaScript assigned the property "hidden"</div>
<script>
elem.hidden = true;
</script>
Techniquement, hidden fonctionne de la même manière que style="display:none". Mais câest plus court à écrire.
Voici un élément clignotant :
<div id="elem">A blinking element</div>
<script>
setInterval(() => elem.hidden = !elem.hidden, 1000);
</script>
Plus de propriétés
Les éléments DOM ont également des propriétés supplémentaires, en particulier celles qui dépendent de la classe :
valueâ la valeur pour<input>,<select>et<textarea>(HTMLInputElement,HTMLSelectElementâ¦).hrefâ le âhrefâ pour<a href="...">(HTMLAnchorElement).idâ la valeur de lâattribut âidâ, pour tous les éléments (HTMLElement).- â¦et beaucoup plusâ¦
Par exemple :
<input type="text" id="elem" value="value">
<script>
alert(elem.type); // "text"
alert(elem.id); // "elem"
alert(elem.value); // value
</script>
La plupart des attributs HTML standard ont la propriété DOM correspondante, et nous pouvons y accéder comme ça.
Si nous voulons connaître la liste complète des propriétés prises en charge pour une classe donnée, nous pouvons les trouver dans la spécification. Par exemple, HTMLInputElement est documenté à https://html.spec.whatwg.org/#htmlinputelement.
Ou si nous voulons les obtenir rapidement ou encore si nous sommes intéressés par une spécification concrète de navigateur â nous pouvons toujours sortir lâélément en utilisant console.dir(elem) et lire les propriétés. Ou explorez les âpropriétés DOMâ dans lâonglet Ãléments des outils de développement du navigateur.
Résumé
Chaque nÅud DOM appartient à une certaine classe. Les classes forment une hiérarchie. Lâensemble complet des propriétés et des méthodes résulte de lâhéritage.
Les propriétés principales du nÅud DOM sont :
nodeType- Nous pouvons lâutiliser pour voir si un nÅud est un texte ou un nÅud dâélément. Il a une valeur numérique:
1pour les éléments,3pour les nÅuds de texte et quelques autres pour les autres types de nÅuds. Lecture seulement. nodeName/tagName- Pour les éléments, nom de balise (en majuscules sauf en mode XML). Pour les nÅuds non-élément,
nodeNamedécrit ce que câest. Lecture seulement. innerHTML- Le contenu HTML de lâélément. Peut être modifié.
outerHTML- Le code HTML complet de lâélément. Une opération dâécriture dans
elem.outerHTMLne touche paselemlui-même. Au lieu de cela, il est remplacé par le nouveau HTML dans le contexte externe. nodeValue/data- Le contenu dâun nÅud non élément (texte, commentaire). Ces deux sont presque les mêmes, nous utilisons généralement
data. Peut être modifié. textContent- Le texte à lâintérieur de lâélément : le HTML moins tous les
<tags>. Lâécriture met le texte à lâintérieur de lâélément, avec tous les caractères spéciaux et balises traités exactement comme du texte. Peut insérer en toute sécurité du texte généré par lâutilisateur et protéger contre les insertions HTML indésirables. hidden- Lorsquâil est défini sur
true, fait la même chose que CSSdisplay:none.
Les nÅuds DOM ont également dâautres propriétés en fonction de leur classe. Par exemple, les éléments <input> (HTMLInputElement) prennent en charge value, type, tandis que les éléments <a> (HTMLAnchorElement) prennent en charge href etc. La plupart des attributs HTML standard ont une propriété DOM correspondante.
Cependant, les attributs HTML et les propriétés DOM ne sont pas toujours les mêmes, comme nous le verrons dans le chapitre suivant.
Commentaires
<code>, pour plusieurs lignes â enveloppez-les avec la balise<pre>, pour plus de 10 lignes - utilisez une sandbox (plnkr, jsbin, codepenâ¦)