Le Document Object Model (DOM) est une interface de programmation normalisée par le W3C, qui permet à des scripts d'examiner et de modifier le contenu du navigateur web.
Le DOM est une représentation objet de ce qui est affiché dans le navigateur.
Javascript permet d'accéder et de manipuler dynamiquement cette structure.
Toute modification du DOM est immédiatement répercutée dans le navigateur.
Tip: La fonctionnalité "inspecter l'élément" des outils de développement du navigateur donne accès au DOM de la page en cours d'affichage.
Ces outils permettent généralement de manipuler le DOM dynamiquement.
Il est alors possible de modifier "en live" les propriétés des objets affichés (css, ...):
très utile pour faire des tests !
(NB: les modifications faites dans l'inspecteur ne sont pas sauvegardées dans le source.)
Les sélecteurs
Identifiant : document.getElementById(...)
En HTML, il est possible de donner un identifiant (unique) aux éléments.
Pour nommer un élément HTML, utiliser l'attribut id des balises.
ATTENTION: il est important de ne pas donner le même id à plusieurs éléments !
<!-- Insertion d'un nommé mon_bouton -->
<button id="mon_bouton">Bouton</button>
En Javascript, il est possible de récupérer et manipuler l'objet correspondant à un id.
Utiliser document.getElementById(id) pour récupérer l'objet dans un script.
<!-- Insertion du bounton nommé bouton_1 -->
<button id="bouton_1">Bouton 1</button>
<script>
(function (){
// récupération de l'objet bouton_1 grâce à son identifiant
let bouton = document.getElementById("bouton_1")
// optionnel : affichage du bouton1 dans la console (pour éventuel débuggage)
console.log(bouton)
// changement (dynamique) de la couleur du bouton1
bouton.style.color = 'blue'
})()
</script>
Resultat
Pour que document.getElementById(id) fonctionne,
il faut que l'élément HTML correspondant ait bien été déclaré/téléchargé dans la page avant son déclenchement.
Dans le cas contraire, l'objet ne sera pas trouvé, et la méthode renverra null.
Le code ci-dessous génère une erreur :
<!-- Insertion du bouton_2 avant l'exécution du script : ok ! -->
<button id="bouton_2">Bouton 2</button>
<script>
(function (){
let bouton2 = document.getElementById("bouton_2")
console.log(bouton2) // affichage du bouton_2 dans la console
bouton2.style.color = 'blue' // changement de la couleur du bouton_2
let bouton3 = document.getElementById("bouton_3")
console.log(bouton3) // affiche null car le bouton_3 n'existe pas encore...
bouton3.style.color = 'red' // ERREUR gb : bouton_3 n'existe pas encore !
})()
</script>
<!-- Insertion du bouton_3... mais après l'exécution du script : pas bon ! -->
<button id="bouton_3">Bouton 3</button>
Attendre que le document soit prêt : DOMContentLoaded
Comme l'indique l'exemple précédent, il faut être certain que les éléments du DOM ont
bien eu le temps d'être chargés dans la page avant d'utiliser les sélecteurs.
Pour ce faire, nous allons mettre en place un nouveau wrapping : la fonction de wrapping ne sera
plus déclenchée directement, mais elle attendra que le navigateur la prévienne que tout est bien
en place.
Afin d'être certains que le DOM ait bien été chargé avant d'utiliser les sélecteurs,
utiliser un wrapping déclenché par l'évènement 'DOMContentLoaded'
de l'objet document.
L'exemple ci-dessous présente donc une meilleur manière d'utiliser
getElementById.
<!-- Insertion du bounton nommé bouton_1 -->
<button id="bouton_1_bis">Bouton 1</button>
<script>
// le nouveau wrapping déclenché parf l'évènement 'DOMContentLoaded'
document.addEventListener('DOMContentLoaded', function () {
// récupération de l'objet bouton_1 grâce à son identifiant
let bouton = document.getElementById("bouton_1_bis")
// optionnel : affichage du bouton1 dans la console (pour éventuel débuggage)
console.log(bouton)
// changement (dynamique) de la couleur du bouton1
bouton.style.color = 'blue'
})
</script>
Resultat
NB: il s'agit ici d'écouter un évènement sur l'objet document, mais en version DOM.
Pour plus d'informations sur les évènements du DOM, c'est
ici.
Classe CSS : .getElementsByClassName(...)
Il est possible de récupérer tous les éléments qui ont une certaine classe CSS.
La méthode renvoie la liste des éléments trouvés.
Attention : cette méthode renvoie une
HTMLCollection
et ce type de liste est automatiquement mise à jour quand le document concerné change !
-> ne pas faire une boucle qui retire la classe des éléments qui ont été récupérés par cette méthode
en ciblant cette même classe...
<div class="classe1 classe2 classe3">Du texte</div>
<div class="classe2">Encore du texte</div>
<div class="classe1">Toujours du texte</div>
<div class="classe1 classe3">Et un peu plus de texte</div>
<script>
document.addEventListener('DOMContentLoaded', function () {
let elements = document.getElementsByClassName("classe1")
console.log(elements) // optionnel
for (let elem of elements){
elem.style.color = 'blue'
elem.style['font-weight'] = 'bold'
}
elements = document.getElementsByClassName("classe3")
for (let elem of elements){
elem.style['background'] = 'orange'
}
})
</script>
Resultat
Du texte
Encore du texte
Toujours du texte
Et un peu plus de texte
Nom de balise (Tag) : .getElementsByTagName(...)
Même principe que précédemment, mais pour les noms de balises.
Il est possible d'appeler ces méthodes non pas obligatoirement sur document,
mais aussi sur des sous-noeuds du DOM. Dans ce cas, la recherche est limitée
aux sous-noeuds de l'objet visé.
<!-- création d'un noeud/objet nommé mon_conteneur-->
<div id="mon_conteneur">
<div>Du texte</div>
<p>Encore du texte</p>
<div>Toujours du texte</div>
<p>Et un peu plus de texte</p>
</div>
<script>
document.addEventListener('DOMContentLoaded', function () {
// récupération du noeud/objet nommé mon_conteneur
let conteneur = document.getElementById("mon_conteneur")
// récupération LIMITÉE aux <p> contenus (sous-noeuds) dans le conteneur
let elements = conteneur.getElementsByTagName("p")
for (let elem of elements){
elem.style.color = 'blue'
elem.style.fontWeight = 'bold'
}
// récupération LIMITÉE aux <div> contenus (sous-noeuds) dans le conteneur
elements = conteneur.getElementsByTagName("div")
for (let elem of elements){
elem.style.background = 'orange'
}
})
</script>
Resultat
Du texte
Encore du texte
Toujours du texte
Et un peu plus de texte
Note: Si je n'avais pas limité la recherche des élément au conteneur,
tous les <p> de cette page de cours seraient maintenant en bleu,
et tous les <div> en orange... :)
A la différence de
HTMLCollection,
une NodeList
n'est pas modifiée dynamiquement si on change les éléments
-> bon pour les boucles qui voudraient modifier les éléments !
<div id="autre_conteneur">
<div class="classe_01">
<p>Du texte</p>
<p class="classe_01">Un peu texte</p>
<p>Un peu texte en plus</p>
</div>
<div class="classe_02">
<p class="classe_01">Encore du texte</p>
<p>Toujours du texte</p>
<p>Et un peu plus de texte</p>
</div>
</div>
<script>
document.addEventListener('DOMContentLoaded', function () {
// récupère le conteneur
let conteneur = document.getElementById("autre_conteneur")
// sélectionne :
// -> les p ayant la classe_01
// -> le dernier p fils direct des div ayant la classe_02
let elements = conteneur.querySelectorAll("p.classe_01, div.classe_02 > p:last-child")
for (let elem of elements){
elem.style.color = 'blue'
elem.style.fontWeight = 'bold'
}
})
</script>
Resultat
Du texte
Un peu texte
Un peu texte en plus
Encore du texte
Toujours du texte
Et un peu plus de texte
NB:.querySelector(...) vous permet de récupérer uniquement le 1er éléments.
Maintenant que nous savons récupérer un élément sur la page, nous pouvons lui ajouter
(et retirer) des écouteurs afin de gérer les réactions aux actions de l'utilisateur.
La plupart des éléments HTML ont des gestionnaires d'évènements de tous types.
(Ex. il est possible d'écouter un click sur un simple span,
un keydown sur un input, un resize ..., un scroll..., ...)
.addEventListener(type, callback) permet d'ajouter un écouteur d'évènement
de type type sur un élément. Lorsque l'évènement se produit, la fonction
de callback est déclenchée.
Ex. Écouter un click sur un bouton
<!-- Insertion du bouton en lui donnant un identifiant -->
<button id="bouton_coucou">Fais Coucou !</button>
<script>
document.addEventListener('DOMContentLoaded', function () {
// récupération de l'objet bouton grâce à son identifiant
let bouton = document.getElementById("bouton_coucou")
// ajout d'un écouteur d'évènement dans le gestionnaire de clicks du bouton
bouton.addEventListener('click', function (){
alert('Coucou !')
})
})
</script>
Resultat
Retirer un écouteur : .removeEventListener(...)
.removeEventListener(type, callback) permet de retirer le callback
du gestionnaire d'évènements de type type sur un élément.
Ex. Écouter 1 click, puis retirer l'écouter
<!-- Insertion du bouton en lui donnant un identifiant -->
<button id="capricieux" style="color: blue ; font-weight: bold">J'écoute !</button>
<script>
document.addEventListener('DOMContentLoaded', function () {
// récupération de l'objet bouton grâce à son identifiant
let bouton = document.getElementById("capricieux")
// il est possible de déclarer les fonctions comme des variables
let mon_traitement = function (){
// NB: le mot clé this fait référence à l'élément visé par l'évènement
alert("Je suis " + this + ", et c'est bon, j'ai entendu !")
// retrait de l'écouteur : le bouton n'écoutera donc qu'une fois...
this.removeEventListener('click', mon_traitement)
// pour le fun : changement de l'aspect du bouton
this.innerHTML ="Je n'écoute plus !"
this.style.color = "red"
}
// ajout d'un écouteur d'évènement dans le gestionnaire de clicks du bouton
bouton.addEventListener('click', mon_traitement)
})
</script>
Resultat
NB: après avoir retiré un écouteur avec removeEventListener,
il est toujours possible de le remettre avec un nouvel appel à addEventListener.
Détails des évènements
Il est souvent pratique (ou nécessaire) d'obtenir des détails sur un évènement.
C'est par exemple le cas lors d'un click pour savoir si l'utilisateur
a cliqué avec le bouton droit, ou le bouton gauche. De la même manière, lors d'un
keyup, on souhaite en général connaitre la touche sur laquelle
l'action a été effectuée.
Un exemple :
<!-- Création d'un élément -->
<!-- NB: pour tester le click droit : on désactive le menu contextuel du navigateur -->
<div id="div_evt_detail" oncontextmenu="return false"
style="width: 100%; height: 100px ;
display:flex ; align-items: center ; justify-content: center ;
border: 1px solid grey; ">
Play with me !
</div>
<script>
document.addEventListener('DOMContentLoaded', function () {
// récupération de l'élément
let element = document.getElementById("div_evt_detail")
// sauvegarde du texte initial
let initial_txt = element.innerHTML
// on écoute quand la souris se déplace sur l'élément,
// et on récupère l'évènement (event) en paramètre du callback,
// ce qui permet de l'utiliser dans la fonction
element.addEventListener('mousemove', function (event){
// un petit message avec des détails sur l'évènement...
let txt = event.type + " : " + event.offsetX + ", " + event.offsetY
// ... qu'on affiche dans le div (lui-même)
this.innerHTML = txt
// ... en changeant sa couleur
this.style.background = 'lightgreen'
})
// Quand la souris est enfoncée sur l'élément...
element.addEventListener('mousedown', function (event){
// optionnel : pour voir les détails dans la console
console.log(event)
// infos sur les touches enfoncées lors du click
let txt = event.type + " : \n"
+ "Bouton : " + event.button + "\n"
+ "Touche Alt : " + event.altKey + "\n"
+ "Touche Ctrl : " + event.ctrlKey
window.alert(txt)
})
// Quand la souris sort de l'élément...
element.addEventListener('mouseout', function (event){
// ... on remet le texte de départ ...
this.innerHTML = initial_txt
// ... et la couleur initiale
this.style.background = 'initial'
})
})
</script>
Cliquer pour plus d'infos
Play with me !
Gérer les comportements par défaut
Lors de la création d'une application Javascript, il faut parfois désactiver certains comportements
qui existent par défaut dans le navigateur (c'était par exemple le cas dans l'exemple précédent avec
le menu contextuel).
Grace au DOM, il est possible de désactiver, voire remplacer, certains de ces comportements.
.preventDefault()
Comme son nom l'indique, .preventDefault() permet d'empêcher un évèvement
de déclencher son action par défaut (comme par exemple le fait que lorsqu'on clique sur
une ancre <a href='...'>, le navigateur dirige automatiquement vers l'attribut
href associé).
Un exemple simple
<ul>
<li>
Ancre sur laquelle on va laisser le fonctionnement <b>par défaut</b> :
<a href="https://www.w3.org/TR/dom41/" target="_blank">
W3C
</a>
</li>
<li>
Ancre sur laquelle on va greffer un fonctionnement <b>custom</b> :
<a id="dummy_anchor"
href="https://www.w3.org/TR/dom41/">
W3C
</a>
</li>
</ul>
<script>
document.addEventListener('DOMContentLoaded', function () {
// récupération de l'élément
let dummy = document.getElementById("dummy_anchor")
// un écouteur sur les clicks
dummy.addEventListener('click', function (event){
// SUPPRESSION DU COMPORTEMENT PAR DEFAUT
// (ici une balise <a> : le navigateur n'ouvrira plus la page ciblée par le href)
event.preventDefault()
// traitement customisé
alert("Vous avez cliqué sur " + event.target.href)
})
})
</script>
Resultat
Ancre sur laquelle on va laisser le fonctionnement par défaut :
W3C
Ancre sur laquelle on va greffer un fonctionnement custom :
W3C
Tip: Pour que bootstrap fonctionne dans votre page, il faut compléter le <head> comme suit :
(cf. Boostrap - Getting started)
<head>
<!-- ici le contenu de votre balise head-->
<!-- partie à ajouter pour que bootstrap fonctionne -->
<script src="https://code.jquery.com/jquery-3.2.1.slim.min.js"
integrity="sha384-KJ3o2DKtIkvYIK3UENzmM7KCkRr/rE9/Qpg6aAZGJwFDMVNA/GpGFF93hXpG5KkN"
crossorigin="anonymous"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/popper.js/1.12.9/umd/popper.min.js"
integrity="sha384-ApNbgh9B+Y1QKtv3Rn7W3mgPxhU9K/ScQsAP7hUibX39j7fakFPskvXusvfa0b4Q"
crossorigin="anonymous"></script>
<script src="https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0/js/bootstrap.min.js"
integrity="sha384-JZR6Spejh4U02d8jOt6vLEHfe/JQGiRRSQQxSfFWpi1MquVdAyjUar5+76PVCmYl"
crossorigin="anonymous"></script>
</head>
<div class="dropdown" id="test_preventdefault">
<button class="btn btn-primary dropdown-toggle" type="button" data-toggle="dropdown">
Moteurs de Recherche <span class="caret"></span>
</button>
<div class="dropdown-menu" aria-labelledby="dropdownMenuButton">
<li><a class="dropdown-item" href="https://www.qwant.com/">Qwant</a></li>
<li><a class="dropdown-item" href="https://www.google.com/">Google</a></li>
<li><a class="dropdown-item" href="https://fr.yahoo.com/">Yahoo</a></li>
</div>
</div>
<script>
document.addEventListener('DOMContentLoaded', function () {
// récupération du conteneur
let container = document.getElementById("test_preventdefault")
// récupération des ancres du conteneur
let anchors = container.getElementsByTagName("a")
// pour chaque ancre : écraser le comportement par défaut, et mettre celui souhaité
for (item of anchors){
item.addEventListener('click', function (e){
// suppression comportement par défaut
e.preventDefault()
// comportement custom
let cible = e.target.href
let rep = confirm("Voulez-vous vraiment aller à " + cible + " ?")
if (rep){
window.open(cible) // permet d'ouvrir une nouvelle fenêtre
}
})
}
})
</script>
Comme on peut le constater sur le schéma, le DOM correspond
à une hiérarchie de noeuds.
Chaque noeud a un parent unique, et peut avoir un
ou des enfant(s)(child(ren)), ainsi qu'un ou des frère(s) (sibling), i.e. enfants du même parent.
NB: quand le DOM n'a pas été modifié, l'odre des noeuds correspond à celui
de leur déclaration dans le code source (HTML).
Les noeuds peuvent être de 2 types :
element : correspond aux balises HTML
text : correspond aux textes à l'intérieur des balises
Parcourir la hiérarchie
Différents attributs permettent de récupérer les fils, frères, parent, ...
<style>
#test_hierarchy *{
margin: 5px;
}
</style>
<div id="test_hierarchy" style="background: lightgray ; padding: 3px">
Texte 1.
<div style="background: lightgreen">Texte 2.
<span style="background: lightcoral">Un span</span>
Texte 3.
</div>
<div style="background: lightblue">
Texte 4.
</div>
Texte 5.
</div>
<script>
document.addEventListener('DOMContentLoaded', function () {
let container = document.getElementById("test_hierarchy")
// 1er fils (text ou element)
let texte1 = container.firstChild
console.log(texte1.textContent)
// 1er element
let div0 = container.firstElementChild
console.log(div0 )
// le parent (element)
console.log(div0.parentElement)
// dernier fils (text ou element)
let div_text3 = div0.lastChild
console.log(div_text3.textContent)
// dernier element
let div_p = div0.lastElementChild
console.log(div_p)
// frère précédent (text ou element)
let div_prev_sibling = div0.previousSibling
console.log(div_prev_sibling.textContent)
// element suivant
let div_next_sibling = div0.nextElementSibling
console.log(div_next_sibling.textContent)
// tous les elements
let children = container.children
console.log("Il y a " + children.length + " element(s)")
// tous les enfants (text ou element)
let child_nodes = container.childNodes
console.log("Il y a " + child_nodes.length + " noeud(s) (text et element)")
})
</script>
Resultat
Texte 1.
Texte 2.
Un span
Texte 3.
Texte 4.
Texte 5.
Modifier la hiérarchie
Il est aussi possible d'ajouter/retirer dynamiquement des enfants,
de modifier le contenu html, ...
<style>
#modify_hierarchy *{
margin: 5px;
}
</style>
<div id="modify_hierarchy" style="background: lightgray ; padding: 5px">
text
<div style="background: lightcoral">div 1</div>
<div style="background: lightgreen">div 2</div>
<div style="background: lightcyan">div 3</div>
<button class="btn btn-warning">Modifier</button>
</div>
<script>
document.addEventListener('DOMContentLoaded', function () {
let container = document.getElementById("modify_hierarchy")
let button = container.lastElementChild
button.addEventListener('click', function (){
// retrait du bouton
container.removeChild(button)
// retrait du 2nd élément
let target = container.children[1]
container.removeChild(target)
// creation d'un nouveau div
let new_node = document.createElement('div')
new_node.style.background = 'lightyellow'
new_node.innerHTML = "<b>div 4</b>"
// insertion en tant que 1er fils du conteneur
container.insertBefore(new_node, container.firstChild)
// création et insertion d'un <br> à la fin
new_node = document.createElement('br')
container.appendChild(new_node)
// insertion d'un nouveau texte à la fin
container.append("Voir : ")
// création et insertion d'une ancre à la fin
new_node = document.createElement('a')
new_node.innerText = 'HTML DOM appendChild() Method'
new_node.href = 'https://www.w3schools.com/jsref/met_node_appendchild.asp'
new_node.target = '_blank'
container.appendChild(new_node)
})
})
</script>
Resultat
text
div 1
div 2
div 3
Éditer les attributs
La structure d'une balise telle qu'on l'écrit dans le code HTML
est : <name attributes> innerHTML </name>
(ex. <a href='http://www.google.fr'>Google</a> )
>
Les attributs correspondent à des paires key=value. key correspond au nom de l'attribut visé, et value
est une chaine.
(ex. href='http://www.google.fr').
La chaine value peut être "complexe" et contenir des
(sous-)paires key: value.
(ex. style="background: 'blue' ; color: 'red' ; font-weight: 'bold'").
Les objets element du DOM correspondent aux balises HTML.
Les attributs d'une balise HTML correspondent aux attributs de l'objet element qui
la représente.
Lorsque la valeur d'attribut d'une balise contient des sous-paires key: value,
l'attribut key de l'objet elementest lui même un objet.
Javascript permet de lire/modifier dynamiquement les attributs des element.
C'est d'ailleurs ce que nous avons fait à plusieurs reprises en transformant l'attribut
style de certains éléments.
Accès par notation Objet
La plupart des attributs standard HTML sont accessible (en lecture et écriture) en Javascript
grâce à la notation 'pointée' des objets.
<div id="edit_dom_attr">
<h2>Un exemple d'accès aux attributs</h2>
<a href="http://www.google.fr" target="_blank">Moteur de recherche</a><br>
</div>
<script>
document.addEventListener('DOMContentLoaded', function () {
let container = document.getElementById("edit_dom_attr")
let a = container.getElementsByTagName('a')[0]
// lecture de l'attribut href de l'ancre
console.log("L'ancre pointait vers : " + a.href)
// modification...
a.href = 'https://www.qwant.com'
console.log("Maintenant, elle pointe vers : " + a.href)
let h2 = container.getElementsByTagName('h2')[0]
// modification de l'attribut fontSize de l'attribut style du h2
h2.style.fontSize = '0.8em'
// modification de l'attribut margin-top de l'attribut style du h2
h2.style['margin-top'] = '0px'
// ... la même écrite autrement :
h2['style'].marginTop = '0px'
})
</script>
Accès par .getAttribute(key) et .setAttribute(key, value)
En HTML, il est possible créer ses propres (clés d') attributs.
(ex. <div mon_attribut='une valeur qui me parle'>
Ces attributs 'non standards' permettent de faire des traitements custom en Javascript.
(C'est par exemple le cas dans certains Frameworks comme Bootstrap.)
Les méthodes .getAttribute(key) et .setAttribute(key,
value) permettent d'accéder à tout attribut, qu'il soit standard
(ex. href) ou non standard (ex. mon_attribut).
<div id="get_set_dom_attr">
<div perso="Bill">div Bill</div>
<div perso="Greg">div Greg</div>
<div perso="inconnu">div inconnu 1</div>
<div perso="Toto">div Toto</div>
<div perso="inconnu">div inconnu 2</div>
<br>
<button class="btn btn-default">Trouver les inconnus</button>
</div>
<script>
document.addEventListener('DOMContentLoaded', function () {
let container = document.getElementById("get_set_dom_attr")
// Accès à l'attribut 'perso' du 2nd div
let i = 2
let div = container.children[i-1]
let perso = div.getAttribute('perso')
console.log("Le div n° " + i + " parle de " + perso)
let button = container.getElementsByTagName('button')[0]
// Un click sur bouton va surligner les elements avec un attribut perso='inconnu'
button.addEventListener('click', function (event){
// div[perso='inconnu'] cible tous les div qui ont un attribut perso='inconnu'
let targets = container.querySelectorAll("div[perso='inconnu']")
// changement de style pour tous les éléments ciblés
for (t of targets){
let new_style = 'background: darkred ; color: white'
t.setAttribute('style', new_style)
}
})
})
</script>
Resultat
div Bill
div Greg
div inconnu 1
div Toto
div inconnu 2
Attributs spéciaux
Il existe des accesseurs d'attributs que je qualifierais de 'spéciaux' du
fait qu'il n'ont pas une représentation aussi 'directe' dans les balises.
Ajouter/retirer des classes CSS : .classList
Permet de lire/modifier la liste des classes CSS qui sont appliquées à l'élément.
Il s'agit littéralement d'ajouter ou retirer dynamiquement des classes CSS !
Cet attribut est très pratique car, contrairement à ce que nous avons fait jusqu'à présent,
il permet de modifier le style d'un élément sans avoir à énumérer toutes les sous-propriétés
une par une à chaque fois.
<style>
#class_dom_attr{
display: inline-block;
padding: 5px;
color: blue;
}
.ok{
background: greenyellow;
font-family: Arial;
}
.dangereux{
font-weight: bold;
border: 5px solid red ;
border-radius: 3px;
}
</style>
<div id="class_dom_attr" class="ok">Ce cours est important</div>
<br><br>
<button class="btn btn-default" onclick="goToDanger()">Go to Danger</button>
<script>
function goToDanger(){
let elem = document.getElementById("class_dom_attr")
elem.classList.remove('ok')
elem.classList.add('dangereux')
}
</script>
Resultat
Ce cours est important
NB: vous pouvez aussi ajouter/supprimer une classe en boucle grâce à classList.toggle(...)
La position, la taille , etc.
Il serait fastidieux de toutes les passer en revue, mais Javascript donne accès à d'autres
propriétés bien pratiques, comme celles qui permettent de connaitre la position d'un élément,
sa taille, etc.
Comme en Java, le try ... catch ... finally de Javascript
permet d'intercepter et de traiter les éventuelles erreurs qui peuvent se produire
pendant l'exécution d'un script.
La structure du code est en 3 parties : la dernières est optionnelle.
Le bloc try permet d'entourer un code sensible susceptible de générer des erreurs.
(ex. accès à un élément du DOM qui n'existe pas, pb réseau, ...)
Le bloc catch définit les instructions à exécuter en cas d'erreur.
Le bloc finally(optionnel) définit les instructions à exécuter après le bloc
try (et éventuellement catch), qu'il y ait eu erreur ou non.
try...catch
try{
// on tente de récupérer un élément qui n'existe pas...
let element = document.getElementById('Un div inexistant')
// l'accès à style est null, et donc l'accès à color déclenche un erreur !
element.style.color = 'red'
// du fait de l'erreur, le code ci-dessous ne sera pas exécuté
window.alert("je n'ai servi à rien")
} catch(e) { // l'erreur est interceptée
console.log("Erreur interceptée : " + e)
}
console.log('Tout va bien, je continue...')
try...catch...finally
<div id="div_tcf" style="margin: 10px 0 ; border: 1px solid black ; padding: 5px">Je suis un div</div>
<button id="btn_tcf_01" class="btn btn-primary">Ce bouton fonctionne</button>
<button id="btn_tcf_02" class="btn btn-danger">Ce bouton génère une erreur</button>
<!-- un div pour afficher les message -->
<div id="tcf_output"
style="margin: 10px 0 ; min-height: 50px ; padding: 5px ; background: black ; color: greenyellow">
</div>
<script>
document.addEventListener('DOMContentLoaded', function () {
let btn1 = document.getElementById('btn_tcf_01')
btn1.addEventListener('click', function (){
changerCouleur('div_tcf') // cherche un id existant
})
let btn2 = document.getElementById('btn_tcf_02')
btn2.addEventListener('click', function (){
changerCouleur('un div inexistant') // cherche un id inexistant
})
function changerCouleur(identifiant){
// si l'élément avec un id=identifiant n'existe pas, div sera null
let div = document.getElementById(identifiant)
try{
// si le div n'existe pas : génère une erreur
div.style.background = 'yellow'
// quand il n'y a pas eu d'erreur :
afficherMsg("Tout s'est bien passé.")
} catch(e) {
// quand l'erreur est interceptée
afficherMsg("Erreur interceptée : " + e, 'red')
} finally {
// ce bloc s'exécute quoi qu'il arrive
afficherMsg("Le finally est exécuté dans tous les cas.", 'lightblue')
}
}
// Juste pour afficher les messages
let output = document.getElementById('tcf_output')
function afficherMsg(msg, color='inherit'){
let txt = document.createElement('div')
txt.innerText = msg
txt.style.color = color
output.appendChild(txt)
}
})
</script>
Resultat
Je suis un div
Timers
L'objet window fournit des méthodes permettant de retarder ou répéter
des instructions au bout d'un certain temps.
Retarder une exécution : setTimeout / clearTimeout
setTimeout
window.setTimeout permet de déclencher l'exécution d'une fonction au bout d'un certain
temps. La fonction exécutée et le délai à attendre (en ms) sont passés en paramètres.
La méthode renvoie aussi un objet qui permettra de le stopper (cf. clearTimeout).
ex. setTimeout 'simple'
<button id="sto_btn" class="btn btn-default">Test exécution différée</button>
<div id="sto_out"
style="min-height: 50px ; margin: 10px 0 ; padding: 5px ; background: black ; color: greenyellow">
</div>
<script>
document.addEventListener('DOMContentLoaded', function () {
let button = document.getElementById('sto_btn')
button.addEventListener('click', function (){
// exécution instantanée (dès qu'on clique)
afficher(new Date().toLocaleTimeString() + ' : click')
// exécution différée...
window.setTimeout(function (){
afficher(new Date().toLocaleTimeString() + ' : message différé', 'aqua')
}, 1000) // ... 1000ms plus tard
})
// Juste pour afficher des messages
let output = document.getElementById('sto_out')
function afficher(msg, color='greenyellow'){
let element = document.createElement('div')
element.innerText = msg
element.style.color = color
output.append(element)
}
})
</script>
Resultat
clearTimeout
window.clearTimeout permet de stopper un timeout avant qu'il ne se déclenche.
La méthode prend en paramètre l'objet renvoyé par setTimeout.
ex. setTimeout + clearTimeout
<button id="cto_btn" class="btn btn-default">Test exécution différée</button>
<button id="cto_btn_stopper" class="btn btn-warning" style="visibility: hidden">Stopper le timer</button>
<div id="cto_out"
style="min-height: 50px ; margin: 10px 0 ; padding: 5px ; background: black ; color: greenyellow">
</div>
<script>
document.addEventListener('DOMContentLoaded', function () {
// servira à stocker le timeout
let timer = undefined ;
let button = document.getElementById('cto_btn')
let button_stop = document.getElementById('cto_btn_stopper')
let delay = 2000 // on attendra 2s
// évènement permettant de lancer le timeout
button.addEventListener('click', function (){
// exécution instantanée (dès qu'on clique)
afficher(new Date().toLocaleTimeString() +
" : le message différé s'affichera dans " + delay/1000 + "s...")
// exécution différée...
// (on stocke le timer pour pouvoir le bloquer au cas où...)
timer = window.setTimeout(function (){
afficher(new Date().toLocaleTimeString() + ' : message différé', 'aqua')
// cacher les bouton stop quand le message a été affiché
button_stop.style.visibility = 'hidden'
}, delay)
// affichage du bouton permettant de stopper le timer
button_stop.style.visibility = 'visible'
})
// évènement permettant de stopper le timeout (avant qu'il ne se déclenche)
button_stop.addEventListener('click', function (){
window.clearTimeout(timer)
afficher('Timer STOPPED', 'red')
button_stop.style.visibility = 'hidden'
})
// Juste pour afficher des messages
let output = document.getElementById('cto_out')
function afficher(msg, color='greenyellow'){
let element = document.createElement('div')
element.innerText = msg
element.style.color = color
output.append(element)
}
})
</script>
Resultat
Répéter une exécution : setInterval / clearInterval
window.setInterval permet de répéter l'exécution d'une fonction au bout d'un certain
temps. La fonction répétée et le délai entre 2 exécutions (en ms) sont passés en paramètres.
La méthode renvoie aussi un objet qui permettra de stopper la répétition.
window.clearInterval permet de stopper un Interval.
La méthode prend en paramètre l'objet renvoyé par setInterval.
ex. setInterval + clearInterval
<button id="si_btn" class="btn btn-default">start</button>
<script>
document.addEventListener('DOMContentLoaded', function () {
// pour stocker le timer quand il sera actif
let timer = undefined
// récupération du bouton 'horloge'
let clock = document.getElementById('si_btn')
// écouter les click pour :
// -- démarrer le timer s'il ne tourne pas (timer == undefined)
// -- stopper le timer s'il est déjà actif (timer != undefined)
clock.addEventListener('click', function (){
if(timer === undefined){ // il n'y a pas de timer actif
clock.innerHTML = new Date().toLocaleTimeString()
// mise à jour de l'horloge toutes les secondes
// (on stocke le timer pour pouvoir l'arrêter au prochain click)
timer = window.setInterval(function (){
clock.innerHTML = new Date().toLocaleTimeString()
}, 1000) // 1000ms = 1s
}else{ // le timer existe : il faut l'arrêter
window.clearInterval(timer)
timer = undefined
clock.innerHTML = 'Restart'
}
})
})
</script>
Cliquer pour démarrer/arrêter l'horloge
Technique du "callback"
La technique du callback ("fonction de rappel"), n'est pas uniquement liée aux timers,
mais elle peut s'avérer particulièrement utile dans ce contexte.
Un callback est une fonction envoyée en paramètre d'une autre fonction
pour que cette dernière puisse appeler le callback quand elle le souhaite
(par exemple, à la fin de son exécution).
Exemple de Callback avec un setInterval
Pour exemplifier, le code ci-dessous crée 2 boutons avec chacun leur traitement.
Cependant, un click sur un bouton ne lance pas ce traitement immédiatement :
l'exécution est différée et exécutée seuelment après un compte à rebours.
Cette exécution différée est mise en oeuvre par une fonction générique launch qui
prend en paramètre un callback, c.a.d. une autre fonction qui sera appelée uniquement
lorsqu'un compte à rebours est terminé.
Exemple de callback.
<button id="button-01">Traitement 01</button>
<button id="button-02">Traitement 02</button>
<br><br>
<div id="messages"></div> <!-- zone d'affichage des messages -->
<script>
let messages = undefined // sera initialisée dans le 'DOMContentLoaded'
// traitement du bouton 01
function traitement_01(){
messages.innerHTML += "<div style='color: blue'>TRAITEMENT 01 : TERMINÉ !</div>"
}
// traitement du bouton 02
function traitement_02(){
messages.innerHTML += "<div style='color: red'>TRAITEMENT 02 : TERMINÉ !</div>"
}
// Fonction qui prend en paramètre un 'callback', i.e. une autre fonction.
// Le callback sera déclenché quand le compte à rebours est terminé
function launch(callback){
let counter = 5 // on décompte de 5 à 0
const delay = 500 // vitesse du compte à rebours
let timer = window.setInterval(function (){
// traitement à chaque décompte
messages.innerHTML += "<span style='color: grey'>"+ counter + "</span> "
counter--
if (counter === -1){ // quand le compte à rebours est terminé
window.clearInterval(timer) // arrêt du timer
callback() // !!! déclenchement du callback !!!
}
}, delay)
}
document.addEventListener('DOMContentLoaded', function () {
messages = document.getElementById("messages")
let launcher01 = document.getElementById("button-01")
console.log(launcher01)
launcher01.addEventListener('click', function (){
launch(traitement_01) // la fonction traitement_01 est envoyée en callback
})
let launcher02 = document.getElementById("button-02")
launcher02.addEventListener('click', function (){
launch(traitement_02) // la fonction traitement_02 est envoyée en callback
})
})
</script>