Le Web 2.0
Le rôle du JavaScript
L’évolution du Web
-
Web 1.0 : Pages statiques
Une adresse = une page qui ne bouge pas - Web 1.5 : Pages dynamiques
- Génération de page côté serveur (ex: PHP)
Mais une page ne varie pas entre deux requêtes - Script côté client (ex: JavaScript)
Permet des applications côté client (des calculs …)
Permet des activités sur la page sans la recharger
(changement de la page, de son style …)
- Génération de page côté serveur (ex: PHP)
- Web 2.0 (Cours prochain)
Communications asynchrones (non liés au chargement des pages) entre le serveur et le client
XMLHttpRequest, Ajax, WebSocket …
JavaScript dans un document HTML
Différentes syntaxes :
-
Chargement d’un script JavaScript externe
<script defer src="code/hello.js"></script>
defer
: attendre la fin du chargement de la page avant l’exécution duscript
-
Script directement dans le HTML (peu conseillé)
<script>alert("hello!");</script>
-
Actions directement dans le HTML (déconseillé)
<button onclick="alert('Boom!')">Do not press</button>
Structure du code
De la même manière que nous séparons le style CSS du document HTML, nous séparerons les actions JavaScript.
Question : Pourquoi séparer HTML, CSS et JS ?
- Clarté : Cela donne de la structure au code.
- Le document HTML décrit la structure du document et son sens (sa
sémantique). Par exemple, tel élément est un titre
<h1>
, tel élément est un menuclass=menu
. - Le CSS associe un style à chaque élément en fonction de son sens.
- Le JS rajoute des interactions avec le document.
- Le document HTML décrit la structure du document et son sens (sa
sémantique). Par exemple, tel élément est un titre
- Maintenabilité : Le style étant regroupé dans les feuilles CSS, il est plus simple de le retrouver et l’éditer. De plus, on évite les répétitions en associant plusieurs fois le même style à des éléments différents. C’est pareil pour les actions JS.
Structuration du code JavaScript :
- Dans un fichier JavaScript séparé
- Définition des actions
- Association des actions aux éléments HTML à l’aide des sélecteurs CSS.
- Ainsi, nous séparons le style, les actions et le contenu.
Le Document Object Model
Le DOM
Le modèle objet de document DOM (Document Object Model) est une interface de programmation (API) avec le document HTML.
Le DOM JavaScript est accessible via l’objet document
.
Exemples
document.documentElement; // Renvoie le <html>
document.head; // Renvoie le <head> de la page
document.body; // Renvoie le <body> de la page
document.URL; // Renvoie l'adresse
document.cookie; // Renvoie les cookies
document.title; // Renvoie le titre
// Renvoie l'URL de la page précédente
document.referrer;
La structure d’arbre du HTML
Les documents HTML ont une structure d’arbre
<!doctype html>
<html>
<head>
<title>Ma page d'accueil</title>
</head>
<body>
<h1>Ma page d'accueil</h1>
<p>Bonjour, je m'appelle Romain.</p>
<p>Allez voir mon cours de JavaScript à
<a href="http://romainlebreton.github.io">
cette adresse</a>.</p>
</body>
</html>
La structure d’arbre du HTML
Ce code HTML correspond à l’arbre suivant
Navigation dans l’arbre
Les méthodes de base pour naviguer dans l’arbre :
childNodes
,parentNode
,firstChild
,lastChild
,previousSibling
,nextSibling
Problème : En pratique, c’est plus compliqué à cause des espaces et sauts de lignes entre balises : Démo
Différents types de nœuds
Les nœuds de l’arbre sont du type Node
. Il existe différents types de Node
, que l’on peut distinguer par leur propriété nodeType
:
Exemple:
// body est un Element (= une balise)
document.body.nodeType; // → 1
// Son premier fils est un noeud texte
document.body.firstChild.nodeType // → 3
// Son premier fils Element est une balise
document.body.firstElementChild.nodeType // → 1
Navigation sur les Element
Tous les Node |
Seulement les Element |
---|---|
parentNode |
parentElement |
childNodes |
children |
firstChild |
firstElementChild |
lastChild |
lastElementChild |
previousSibling |
previousElementSibling |
nextSibling |
nextElementSibling |
Recherche dans le DOM
Problème : Trouver un élément particulier n’est pas très pratique
Solution : on utilise les méthodes de recherche
getElementById
, (renvoie un élément)getElementsByTagName
, (renvoie un tableau d’éléments)getElementsByClassName
. (renvoie un tableau d’éléments)
Exemple :
// Identifiant unique donc on renvoie un élément
let i1 = document.getElementById("id1");
// Tous les enfants de i1 de classe myclass
let tab_e = i1.getElementsByClassName("myclass");
Remarque : getElementsByTagName
et getElementsByClassName
sont des méthodes de Element
:
on peut les appeler sur document
ou toute balise HTML.
Les sélecteurs
Les sélecteurs en CSS permettent de faire des recherches avancées par nom de balise, classe …
Sélecteurs simples (tag
est toujours optionnel)
* /* Sélectionne tout */
tag /* Toute balise <tag> */
tag.class /* Tout <tag> de classe class */
#id /* La balise identifiée par id */
tag:pseudoclass /* Sélection de contenu spécial */
tag[att=val] /* Tout <tag> ayant attribut att égal à val */
Exemples:
li:nth-child(2*n)
– Éléments pairs d’une listea:hover
– Lien survolép::first-letter
– Première lettre d’un paragraphe
Rappels sur les sélecteurs
Combinateurs de sélecteurs
sel1, sel2 /* Chacun des sélecteurs */
parent child /* child s'il est un fils de parent */
parent > child /* child seulement s'il est un fils direct */
sister ~ brother /* brother s'il suit sister */
sister + brother /* brother s'il suit immédiatement sister */
Exemples:
- Q. Que sélectionne
body > * > p
? -
R. Les paragraphes qui sont des petits-fils exacts de
<body>
- Q. Que sélectionne
ul > * li
? - R. Les
<li>
qui sont au moins petit-fils d’un<ul>
Références : W3schools, le standard CSS3
Recherche avancée
querySelectorAll(selector)
:
tous lesElement
satisfaisant le sélecteur,querySelector(selector)
:
le premierElement
satisfaisant le sélecteur
Notes :
- Raccourci courant :
$$
pourdocument.querySelectorAll
.$
pourdocument.querySelector
.
-
Ce sont des méthodes de
Element
: valable sur toute balise// Recherche parmi les descendants de `element` element.querySelector(selector) // Recherche parmi les ancêtres de `element` element.closest(selector) // Indique si `element` satisfait le sélecteur element.matches(selector)
Modification du contenu
Attribut innerHTML
de Element
:
- représentation texte du contenu d’une balise,
en lecture et en écritureh1.innerHTML = "<u>coucou</u>"
- ⚠️ échappement des caractères spéciaux du HTML ⚠️
h1.innerHTML = "<script>alert('Boom!')</script>" h1.textContent = "<script>alert('Boom!')</script>"
element.textContent
(équivalent dehtmlspecialchars
)
encodeURI
/encodeURIComponent
(équivalent deurlencode
)
L’insertion de<script>
ci-dessus ne marche pas en pratique, mais il reste une faille de sécurité.
Démo
Attributs d’une balise
On peut changer la valeur de l’attribut d’une balise HTML. On peut aussi créer un attribut et lui donner une valeur, ou le supprimer.
let input = document.querySelector("input")
input.max=25
input.value=17
input.setAttribute("name", "monInput")
input.removeAttribute("min")
Modification des classes
On peut accéder à la liste des classes d’une balise HTML (voir TD1) :
let div = document.querySelector("div")
div.classList
div.classList.remove("c2")
div.classList.add("c4")
div.classList.toggle("cache")
div.classList.toggle("cache")
div.classList.replace("c4", "c2")
Insertion de balises HTML (1/2)
let div = document.querySelector("#div_p")
let paragraphes = ["coucou", "hello", "salut"]
for (let paragraphe of paragraphes) {
pHTML = `<p> ${paragraphe} </p>`
div.insertAdjacentHTML('beforeend', pHTML)
// Équivalent à (et plus rapide que)
// div.innerHTML += pHTML
}
- Préférer
insertAdjacentHTML
àinnerHTML += ...
:- équivalent à
div.innerHTML = div.innerHTML + pHTML
- évite de sérialiser et parser tout le HTML existant
- améliore les performances
- équivalent à
Insertion de balises HTML (2/2)
let newP = document.createElement("p")
newP.setAttribute("id","p2")
newP.textContent = "paragraphe 2"
let div = document.getElementById("div_p")
div.appendChild(newP)
div
Avantages de createElement
sur innerHTML
:
- Préserve les références aux
Element
existants
→ préserve les gestionnaires d’évènements de cesElement
- Attention à l’échappement HTML au sein de
innerHTML
Suppression de balises HTML (1/2)
Si l’objectif est de supprimer l’intégralité des balises qui
descendent d’une balise englobante, on peut brutalement affecter une
chaîne vide au innerHTML
de la balise englobante…
let div = document.querySelector("#div_p")
div.innerHTML = ""
Suppression de balises HTML (2/2)
Au choix :
parent.removeChild(child)
supprime la balisechild
, et retourne l’élément supprimébalise.remove()
supprime la balisebalise
let parent = document.querySelector("p")
let child = parent.children[0]
// Au choix
parent.removeChild(child)
// Ou
child.remove()
Les évènements en JavaScript
Gestionnaires d’évènements
Il y a 3 manières d’associer une action à un évènement
Par exemple, pour exécuter la fonction act()
lors d’un clic sur
un <button>
(variable b
) :
-
b.addEventListener('click',act);
-
b.onclick = act;
-
<button onclick='act()'>
Gestionnaires d’évènements
Utilisez la première syntaxe
b.addEventListener('click',act);
car
- on peut associer plusieurs actions au même évènement
-
on peut supprimer une action d’un évènement
b.removeEventListener('click',act);
- on peut ajouter des options avancées :
once
, …
L’objet évènement
La fonction donnée au gestionnaire reçoit un paramètre :
l’objet évènement du type Event
Exemple: La propriété button
indique le bouton cliqué de la souris
<button>Cliquez-moi de toutes les manières</button>
<script>
let button = document.querySelector("button");
button.addEventListener("mousedown", function(event) {
if (event.button == 0)
console.log("Left button");
else if (event.button == 1)
console.log("Middle button");
else if (event.button == 2)
console.log("Right button");
});
</script>
Types d’évènements courants
- Clavier :
keydown
: À chaque appui ou répétition d’une touchekeyup
: À chaque relâchement d’une touche
- Souris :
mousedown
,mouseup
,click
,dblclick
: Clics de sourismousemove
,mouseenter
,mouseout
: Déplacements
- Défilement d’écran :
scroll
- Élément actif :
focus
,blur
- Entrée de formulaire :
input
,change
- Chargement terminé :
load
: chargement terminé d’une ressource et de ses dépendancesDOMContentLoaded
: chargement terminé du DOM
Références : Liste complète des évènements
Classes d’évènements
Exemples :
class KeyboardEvent
(hérite deUIEvent
, qui hérite deEvent
)key
: valeurString
de la touche
Exemple : a-z, A-Z, é, €, ArrowUp, Entercode
: liée à la position physique de la touche,
donc pas lié à la disposition du clavier (azerty, qwerty)ctrlKey
,shiftKey
,altKey
,metaKey
(boolean
)repeat
:true
si appui long qui amène une répétition
class MouseEvent
screenX
,screenY
,clientX
,clientY
: positionctrlKey
,shiftKey
,altKey
,metaKey
(boolean
)button
: bouton appuyédetail
: compte le nombre de clic
Permet de différencier un clic, d’un double clic
Propagation des évènements
Un gestionnaire d’évènement va recevoir les évènements qui se produisent sur ses fils.
<p>Un paragraphe avec un <button>bouton</button>.</p>
<script>
let par = document.querySelector("p");
let button = document.querySelector("button");
par.addEventListener("mousedown", function() {
console.log("Gestionnaire du paragraphe.");
});
button.addEventListener("mousedown", function() {
console.log("Gestionnaire du bouton.");
});
</script>
Un paragraphe avec un .
Propagation des évènements
-
event.stopPropagation()
arrête la propagation vers la racine.
-
target
: cible réelle de l’évènement. -
currentTarget
: balise cible deaddEventListener
.
<p>Un paragraphe avec un <button>bouton</button>.</p>
<script>
let par = document.querySelector("p");
let button = document.querySelector("button");
par.addEventListener("mousedown", function(event) {
console.log("Gestionnaire du paragraphe : ", event.target);
});
button.addEventListener("mousedown", function(event) {
console.log("Gestionnaire du bouton : ", event.target);
if (event.button == 2)
event.stopPropagation();
});
</script>
Un paragraphe avec un .
Utilité de la propagation
Regrouper de nombreux EventListener
sur un ancêtre commun.
Par exemple, pour associer un comportement à un sélecteur
(similaire à l’association d’un style à un sélecteur dans un fichier CSS)
// Loggue tous les évènements des balises
// de classe "log"
document.addEventListener("click", function(event) {
if (event.target.matches(".log")) {
console.log(event);
}
})
Comportement par défaut
event.preventDefault()
stoppe l’action par défaut.
Exemple :
-
Si on clique sur le bouton “Envoyer” d’un formulaire, cela l’empêche de le soumettre
-
Sur on clique sur un lien, cela l’empèche de suivre l’URL
-
Si on clique sur une
checkbox
, elle ne se coche pas…
Biblio : Plus de détails sur la gestion du clavier, les mouvements de la souris, le déroulement de la page et les actions par défaut sur le site de Eloquent JavaScript.