Client DOM - Vue.js
Le Document Object Model (DOM) est une interface de programmation pour les documents HTML et XML. Il fournit une représentation structurée du document et une manière d’accéder à cette structure. Cela permet notamment :
- de parcourir cet arbre (cibler un élément, les enfants/parents, etc.)
- de modifier la structure du document (ajouter, enlever, déplacer des éléments)
- d’accéder au informations des nœuds (contenu, attribut, etc.)
- d’ajouter des événements (interactions utilisateur)
- de modifier les styles CSS d’un élément
- etc.
Cela se fait grâce aux attributs et méthodes d’objets de différents éléments.
L’objet document
#
L’objet global document
est l’objet principal d’un document HTML. Il contient les méthodes de ciblage d’éléments :
document.getElementById(id)
cible l'élément par sonid
document.querySelector(selecteur)
cible le premier élément qui correspond au sélecteur CSSdocument.querySelectorAll(selecteur)
cible tous les éléments qui correspondent au sélecteur CSS
// Cible l'élément avec l’id menu
let menu = document.getElementById('menu');
// Cible le premier paragraphe de la page
let texte = document.querySelector('p');
// Cible tous les liens de la div id=menu
let liens = document.querySelectorAll('#menu a');
Mais également une méthode de création d’objet DOM :
document.createElement('str')
crée un nouveau noeud DOM (str
est une chaîne de caractère représentant le type d'élément HTML souhaité)
// Crée une nouvelle <div> en mémoire (pas encore insérée dans la page)
let monElement = document.createElement('div');
Enfin, les éléments de base de la page peuvent être ciblés plus facilement :
document.documentElement
est l'élément principal du document (html
)document.head
est l'élémenthead
document.body
est l'élémentbody
Liste complète des attributs et méthodes de document
Les éléments#
Voici quelques attributs et méthodes applicables aux éléments d’une page HTML, qu’ils soient créés en JS ou déjà présents dans la page :
Attributs#
monElement.id
retourne (ou définit) l’identifiant d’un élémentmonElement.classList
est une interface pour ajouter/modifier/connaitre les classes sur un élément :monElement.classList.add('maClasse')
monElement.classList.remove('maClasse')
monElement.classList.toggle('maClasse')
monElement.classList.contains('maClasse')
monElement.setAttribute(attr, value)
définit la valeur de l’attribut spécifiémonElement.removeAttribute(attr)
supprime l’attribut spécifiémonElement.getAttribute(attr)
retourne la valeur de l’attribut spécifié
Parcours DOM#
monElement.children
retourne tous les enfants d’un élément (un tableau d’éléments)monElement.parentNode
retourne l’élément parent d’un élément
Modification DOM#
monElement.innerText
retourne (ou définit) le contenu textuelmonElement.innerHTML
retourne (ou définit) le contenu HTML (attention à la sécurité)monElement.value
retourne la valeur d’un champ de formulaire (<textarea>
,<input>
, etc.)
// récupère le contenu texte
let texte = monElement.innerText;
// définit un nouveau contenu
monElement.innerText = 'Nouveau contenu';
monElement.innerHTML = '<p>Hello</p>';
monElement.appendChild(element)
ajoute un nœud enfant à la finmonElement.removeChild(element)
supprime un nœudmonElement.insertBefore(elementAInserer, elementCible)
insère le nœud a insérer juste avant l’élément cible, dans le nœud courant
Mise en forme#
monElement.style
est l’objet pour modifier les styles CSS
// changer le couleur d’un élément
monElement.style.color = 'red';
Liste complète des attributs et méthodes des éléments
Exercice - Manipulations simples du DOM#
-
dans une page HTML (vous pouvez utiliser la page
today.php
)- créez un paragraphe contenant le texte "00:00:00"
-
au chargement, en JS
- générez l’heure au format HH:MM:SS (avec
new Date().toLocaleTimeString('fr')
) - modifiez le texte du paragraphe avec l’heure
- générez l’heure au format HH:MM:SS (avec
-
faites en sorte que l’heure se mette à jour automatiquement toutes les secondes (avec
setTimeout()
) -
affichez l’heure en rouge et en gras (en CSS) si les secondes sont multiples de 10 (15:52:00, 15:52:10, etc.)
Les évènements#
Les évènements permettent notamment d’interagir avec l’internaute. Il est possible d’effectuer des actions, comme :
- récupérer le mouvement de la souris, ou le click
- intercepter les touches du clavier
- détecter la validation d’un formulaire
- etc.
Pour cela, il nous faut «écouter» l’évènement de notre choix :
monElement.addEventListener(type, listener)
ajoute un gestionnaire d’évènements de typetype
. La fonctionlistener
sera exécutée dès que l’évènement se déclenchera (l’évènement peut être passé en paramètre).
Voici quelques types d’évènements :
click
,mousedown
,mouseup
,mousemove
,mouseover
,mouseout
,load
,submit
,scroll
ouwheel
,keydown
,keyup
,keypress
,input
,change
Affiche en console lors du click sur un élément
let monElement = document.getElementById("toto"); // cible #toto
monElement.addEventListener("click", function(){
console.log("L'événement click a été déclenché");
});
Exercice - Manipulations simples du DOM (suite)#
- créez un bouton HTML (balise
<button>Switch timezone</button>
) - ajoutez un événement, et lors du click :
- récupérez une timezone aléatoire avec
Intl.supportedValuesOf('timeZone')
- vous aurez encore besoin de
Math.random()
- vous aurez encore besoin de
- utilisez l’option
timeZone
pour pour générer l’heure dans cette timezone (new Date().toLocaleTimeString('fr', { timeZone: '...' }
) - affichez la timezone dans la page (dans un autre paragraphe par exemple)
- récupérez une timezone aléatoire avec
- vous pouvez également utiliser la même fonction au chargement et récupérer la timezone du client (
Intl.DateTimeFormat().resolvedOptions().timeZone
)
L’objet event
#
L’objet event
passé automatiquement à la fonction de callback a également ses propres attributs et méthodes
event.target
est l’élément cible sur lequel l’évènement a eu lieuevent.type
est le nom de l’évènementevent.preventDefault()
empêche la propagation de l’évènement. Utile pour arrêter l’action par défaut du navigateur par exemple.
Récupère les coordonnées du click
let monElement = document.getElementById("toto"); // cible #toto
monElement.addEventListener("click", function(event){
console.log(event.clientX, event.clientY);
});
Autres considérations#
Un évènement bien pratique est DOMContentLoaded
sur l’objet window
. En effet, cet évènement est déclenché au chargement complet du DOM. Il est ainsi possible d’exécuter du code JS après la création complète de l’arbre, et ainsi appeler ses fichiers JS depuis le <head>
du document HTML.
Détection du chargement complet du DOM
window.addEventListener("DOMContentLoaded", function(){
// DOM chargé
});
L’évènement load
permet de détecter la fin complète du chargement de la page (après exécution des scripts potentiellement asynchrones)
Attention, dans le cas d’une fonction nommée, listener
doit être une référence à la fonction, et pas un appel à cette fonction.
function onClick () {
console.log('Click');
}
// OK
monElement.addEventListener('click', onClick)
// Pas OK: la fonction sera appelée avant même le click
monElement.addEventListener('click', onClick())
Pour passer des paramètres à cette fonction, il n’est donc pas possible de faire
// NOK
monElement.addEventListener('click', onClick(param))
Dans ce cas, deux solutions :
// fonction anonyme qui appelle notre fonction
monElement.addEventListener('click', function() { onClick(param) })
// utilisation de bind (changement de contexte)
monElement.addEventListener('click', onClick.bind(null, param))
Vue.js#
Vue.js est un framework qui permet d’accélerer le développement, tout en structurant le code client.
Voir le tutoriel détaillé dans la partie JavaScript/Outils
Exercice - Tweetbox Twitter X#
Le but de cet exercice est de créer le champ texte sur le principe de celui utilisé par Twitter. C’est à dire, un simple champ texte, limité à un nombre défini de caractères, un bouton de validation et un bouton d’ajout de photo qui limite encore plus le nombre de caractères.
Coté HTML (voir code plus bas) :
- créez un champ texte pour écrire le message
- affichez la limite de caractères max dans un paragraphe (10 pour vos tests)
- créez un bouton de validation
- chargez la librairie Vue
En JS :
- créez une nouvelle instance de
Vue
- utilisez un élément parent pour binder l’application
- stockez les données globales dans
data
(le texte, le nombre de caractères max) - utilisez
max
dans votre HTML
- créez une propriété calculée
nombreRestants
qui renvoie le nombre de caractères restants et utilisez cette valeur pour l’affichage (à la place demax
) - utilisez
v-model
pour mettre à jour la valeur du texte (le nombre de caractères restants devrait se mettre à jour automatiquement) - créez une seconde propriété calculée pour vérifier si la limite est atteinte (doit retourner
true
oufalse
). Utilisez cette propriété pour :- afficher le texte en rouge (en ajoutant une classe)
- désactiver le bouton (attribut
disabled
)
Ensuite :
- en HTML, ajoutez une case à cocher pour simuler l’ajout de une photo (nous ne voulons pas créer la fonctionnalité) et un label associé avec le texte «Pas de photo»
- coté JS, ajoutez dans
data
une propriétéphoto
qui vautfalse
- utilisez
v-model
pour binder cette valeur - modifiez la propriété
nombreRestants
pour enlever ou pas des caractères en fonction de si la case est cochée ou non - le texte du label doit devenir «✓ Photo ajoutée»
- testez les différents cas dans l’interface
Code HTML envisagé (sans code Vue)
<div id="app">
<form action="">
<textarea></textarea>
<p>10</p>
<button>Tweet</button>
<input id="photo" type="checkbox">
<label for="photo">Pas de photo</label>
</form>
</div>
Instance Vue
Vue.createApp({
data() {
return {};
},
computed: {
}
}).mount('#app');
Enfin, il faut pouvoir simuler l’action de tweeter et que ses tweets apparaissent sous la tweetbox (la partie serveur n’est pas gérée ici). Pour cela :
- coté JS :
- créez une liste (vide) de tweets dans
data
- ajoutez l’événement
submit
sur le formulaire, et :- empêchez la validation classique (utilisez
@submit.prevent
directement) - ajoutez les données du tweet actuel (message, image) dans la liste sous forme d’objet littéral
- empêchez la validation classique (utilisez
- créez une liste (vide) de tweets dans
- coté HTML (template) :
- générez des blocs HTML basés sur cette liste (utilisation de
v-for
,v-if
) - note: pour les images, utilisez un service de récupération d’image aléatoire comme https://picsum.photos avec un paramètre
random
, exemple:<img src="https://picsum.photos/200/200?random=2">
- stylez en CSS
- générez des blocs HTML basés sur cette liste (utilisation de
Exercice - Validation de formulaire#
En JavaScript, il est possible de vérifier et valider les champs de formulaire avant d'envoyer les informations au serveur. Cela permet de s'assurer que les données sont correctement formatées (ex: email bien formé, n° de téléphone à 10 chiffres, date ...).
Note: Nous n'aborderons pas ici le traitement des données du formulaire qui s'effectue sur le serveur. Une deuxième vérification des données est alors nécessaire pour valider les formulaires qui n'auraient pas été vérifié par JavaScript (JavaScript désactivé sur le navigateur, tentative de hack, etc.).
Prenons comme exemple ce formulaire HTML :
Formulaire HTML
<form action="#" method="get">
<fieldset>
<legend>Inscription</legend>
<p><label>Nom<input type="text" name="nom"></label></p>
<p><label>Prénom<input type="text" name="prenom"></label></p>
<p><label>Tel<input type="text" maxlength="10" name="telephone"></label></p>
<p><label>Email<input type="text" name="mail"></label></p>
<p><button>Envoyer</button></p>
</fieldset>
</form>
Le résultat est le suivant :
- premièrement, créez votre application Vue
- créez 4 propriétés pour les 4 champs, et binder avec
v-model
Vue.createApp({
data() {
return {
nom: '',
prenom: '',
tel: '',
mail: ''
};
}
}).mount('...');
<input type="text" name="nom" v-model="nom">
<input type="text" name="prenom" v-model="prenom">
<input type="text" maxlength="10" name="tel" v-model="tel">
<input type="text" name="mail" v-model="mail">
- créez 4 propriétés calculées, pour chacun des champs, pour en vérifier la conformité
- champ
nom
etprenom
non vides, champtel
numérique à 10 chiffres, champemail
un email
- champ
Vue.createApp({
computed: {
validNom() {
return this.nom != '';
},
validTel() {
return this.tel.length == 10 && !isNaN(this.tel);
},
validMail() {
let regex = /^[a-z0-9]+([_|\.|-]{1}[a-z0-9]+)*@[a-z0-9]+([_|\.|-]{1}[a-z0-9]+)*[\.]{1}[a-z]{2,6}$/;
return regex.exec(this.mail);
}
}
});
- ajoutez ensuite l’événement
submit
sur le formulaire, et exécutez une fonction - cette fonction doit empêcher la validation du formulaire si au moins un champ est mal rempli
<form action="#" method="get" @submit="validate">
Vue.createApp({
methods: {
validate(event) {
if ( ... ) {
event.preventDefault();
}
}
}
});
Enfin, ajoutez une classe erreur
sur chaque champ mal rempli. Pour cela :
- créez une propriété
errors
, qui doit être un objet littéral avec 4 propriétés pour les 4 champs
Vue.createApp({
data() {
return {
errors: {
nom: false,
prenom: false,
tel: false,
mail: false
}
};
}
});
- utilisez ces propriétés pour ajouter conditionnellement une classe
<input type="text" name="nom" v-model="nom" :class="{ error: errors.nom }">
- pensez à changer à
true
dans la fonctionvalidate
Vue.createApp({
methods: {
validate(event) {
this.errors.nom = !this.validNom;
...
}
}
});
- ajoutez également en CSS le changement de style
.error {
background: red;
}