Cours 2 – Compléments avancés DOM & Évènements
Quelques points supplémentaires
Pêle-mêle :
innerText
vstextContent
innerText
regarde l’apparence du texte (est-il caché ? en majuscule ?), tandis quetextContent
renvoie le contenu texte brutdocument.cookie
accède aux cookies.- Pour des raisons de sécurité,
on doit limiter au maximum les cookies lisibles en JavaScript en leur donnant l’attribut
httpOnly
. - Éviter de mettre dans des cookies les données utiles seulement au client, car elles sont envoyées systématiquement au serveur.
UtilisezlocalStorage
etsessionStorage
pour stocker des données uniquement pour le client.
- Pour des raisons de sécurité,
on doit limiter au maximum les cookies lisibles en JavaScript en leur donnant l’attribut
.value
vssetAttribute("value", ...)
:
Attention,inputElement.value
modifie la valeur courante de l’<input>
(qui sera donc effacée par le boutonreset
du formulaire), tandis queinputElement.setAttribute("value", ...)
change la valeur par défaut (qui se voit tant que l’utilisateur ne l’a pas changée).
Source- Exemples d’utilisation de la propagation d’évènement
En gros,
this
pointe vers la balise possédant le gestionnaire d’évènement, commecurrentTarget
- Capturing events
On peut aussi écouter les évènements lors de la phase de capture, c-à-d lorsqu’ils cherchent leur cible en descendant dans le DOM - Inclure des données JSON dans une page Web
Utile dans un scénario où les données sont générées par le serveur - contentEditable
Pour permettre l’édition d’une partie de la page Web - Pour ouvrir un lien dans une nouvelle fenêtre, utilisez l’attribut
target="_blank"
de la balise<a>
. - Duck typing
Les interfaces n’existent pas en JavaScript. En effet, un prototype est dynamique et peut voir ses méthodes évoluer.
On peut tout de même émuler les interfaces en vérifiant la présence des méthodes voulues. - Que vaut
this
dans un gestionnaire d’évènement ?
Vulnérabilité XSS au TD2
Lors du TD2, vous avez pu être amenés à écrire le code suivant pour l’affichage du tableau de score du championnat de football
class Equipe {
toHTML() {
return `<tr><td>${this.classement}</td><td>${this.nom}</td></tr>`
}
}
// championnat.js
class Championnat {
afficherClassement() {
document.querySelectorAll("#bloc-classement tbody").insertAdjacentHTML(
'beforeend',
this.tabEquipes[i].toHTML()
)
}
}
Or, ce code a une vulnérabilité XSS. Le nom de l’équipe est saisi un utilisateur, qui pourrait provoquer un comportement non souhaité du navigateur.
Solution 1
On utilise une variante personnalisée des template string : `string text ${expression} string text`
.
Ces variantes récupèrent les morceaux de chaines de caractères du template string, ainsi que les valeurs des expressions.
Puis, il peut les traiter comme il le souhaite. Dans notre cas, nous allons échapper à la main les valeurs des expressions.
// library.js
function escapeHtml(text) {
return String(text)
.replace(/&/g, "&")
.replace(/</g, "<")
.replace(/>/g, ">")
.replace(/"/g, """)
.replace(/'/g, "'");
}
function safeTag(strings, ...values) {
let out = ""
for (let i = 0; i < strings.length; i++) {
out += strings[i]
if (i < values.length) {
safeValue = escapeHtml(values[i])
out += safeValue
}
}
return out
}
// equipe.js
class Equipe {
toHTML() {
return safeTag`<tr><td>${this.classement}</td><td>${this.nom}</td></tr>`
}
}
Source sur MDN pour les tagged templates tagFunction`string text ${expression} string text`
Solution 2
L’utilisation de la balise HTML <template>
peut faciliter la mise en place d’une solution.
Le <template>
permet de cloner facilement un morceau de page Web.
On peut alors cibler certaines balises, par exemple grâce à leur identifiant, pour modifier leur textContent
.
<!-- championnat.html -->
<template id="ligne">
<tr>
<td id="classement"></td>
<td id="nom"></td>
</tr>
</template>
// equipe.js
class Equipe {
toHTMLElement() {
let template = document.getElementById("ligne");
let ligne = template.content.cloneNode(true);
ligne.getElementById("classement").textContent = this.classement;
ligne.getElementById("nom").textContent = this.nom;
return ligne;
}
}
// championnat.js
class Championnat {
afficherClassement() {
document.querySelectorAll("#bloc-classement tbody").insertAdjacentElement(
'beforeend',
this.tabEquipes[i].toHTMLElement()
)
}
}