Les événements
Un événement - quelque chose qui arrive - est traduit en JavaScript par une action qui peut appeler une fonction. Ajouter un événement à une balise est possible.
La plupart des événements sont des actions de l'utilisateur (via la souris ou clavier), d'autres non (fin du chargement de la page, d'une lecture audio/video, ...)
Principaux événements
De nombreux événements existent, notamment ceux spécifiques à certains appareils tactiles (tablette, smartphone)
Le principal événement est click.
- Événements souris * :
- click = appuyer puis relâcher (sur un bouton, une case à cocher, ...)
Cet événement n'a lieu qu'après le mouseup - dblclick * = double-cliquer
- mouseover = faire entrer la souris sur l'élément ou celui d'un de ses enfants
- mouseleave : faire sortir la souris sur l'élément
- mouseout = faire sortir la souris
- mousedown = appuyer sans relâcher
- mouseup = relâcher
- mousemove * = déplacer la souris
- ...
- click = appuyer puis relâcher (sur un bouton, une case à cocher, ...)
- Événements clavier :
- keydown = appuyer sans relâcher la touche
- keyup = relâcher la touche
- keypress = appuyer puis relâcher la touche
- Événements formulaire :
- focus = élément qui reçoit tout événement du clavier
-
blur = élément qui perd tout événement du clavier => pour tester la valeur après la saisie
NB : S'il existe une validation HTML, il est préférable de tester la validité des données lors du clic sur le bouton de soumission.
-
change = élément d'un formulaire dont la valeur change
(lors de la frappe dans un champ "texte" ou du clic dans une liste déroulante)document.getElementById('choice').onchange = function() { alert(this.value); }
- select = élément d'un formulaire sélectionné
- submit = valider un formulaire
- reset = réinitialiser le formulaire
- ...
- GlobalEventHandlers : (= pour toute balise)
- oncontextmenu="return false"(MDN)
- onpaste="return false" (notamment dans un champ d'un formulaire)
- oncopy="return false" (n'importe quel texte dans une <div>, ...)
document.getElementById("texte").oncopy=(e) => {e.preventDefault();}
- oncut="return false"
- onselectstart="return false"
- ondrag="return false"
- ondrop="return false"
- Autres événements :
- load : chargement de la page
- resize : changement de la taille de la fenêtre
- scroll
- unload
- ...
Les balises pouvant être ciblées sont celles qui peuvent réagir à un événement du clavier : input, a (via la touche "Enter"). Via la touche de tabulation, on passe à la balise-cible suivante.
* Certains événements JS n'ont pas de sens sur un smartphone. Par exemple, mouseover et mousemove ne réagissent pas car le mouvement du doigt sur l'écran fait défiler la page, dblclick ne se déclenche pas ... tapoter deux fois déclenche en général un zoom.
hover n'est pas un événement JS. Il n'appartient pas à l'interface MouseEvent. Cet événement est la combinaison de deux événements :
mouseover et mouseout sont souvent préférables à mouseenter et mouseleave qui envoyent un message à tous les éléments de sa hiérarchie. (One mouseenter event is sent to each element of the hierarchy when entering them) Plus d'info
Utilisation des événements
sans DOM
Une balise peut être liée à un (ou plusieurs) événement(s).
<p onclick="alert('Vous avez cliqué !')">Cliquez-moi !</p>
Dans le code JS de la fonction traitant l'événement, le mot-clé this représente la balise liée à cet événement.
<p onclick="alert(this.innerHTML)">Cliquez encore !</p>
Pratique à proscrire
Ne pas utiliser un lien pour provoquer une action autre qu'afficher une page (ou une section interne) ou télécharger un fichier.
Utilisé avec l'événement submit, return
false;
sert juste à bloquer l'action par défaut de l'événement. Tel que l'envoi
des données, l'appel d'une page web, ...
<a href="#" onclick="alert('Vous avez cliqué !'); return false;">Cliquez-moi ! (v2)</a>
return false;
car le dièse renvoie vers le haut de la page,
ce qui est un effet non désiré.
Certains événements appliqués à une balise-parent peuvent se transmettre aux balises-enfants, c'est le cas des événements : mouseover, mouseout et mousemove.
DOM-0
Ici, l'événement n'est pas géré via un attribut onNomEvénement d'une
balise. Mais, par la propriété .onNomEvénement d'un élément (récupéré
par une méthode telle que document.getElementById()
). Et, à cette propriété,
on lui affecte une fonction anonyme.
Code HTML :
<p id="viaDOM_0">Cliquez 3 !</p>
Code JS :
var cliquez3=document.getElementById("viaDOM_0"); cliquez3.onclick=function(evt){ alert("via DOM-0 et une fonction anonyme"); /* L'objet evt (dont les propriétés sont adaptées à l'événement) n'est pas utilisé ici */ }
Pourquoi faire simple, lorsqu'on peut faire compliqué !?
Re : Avec DOM-0, on peut avoir accès à l'objet Event ...
le DOM-2 apporte un réel avantage : lier différents événements à un même élément.
DOM-2
Le DOM Level 2, publié en 2000, a introduit l'objet Event et la méthode .addEventListener().
.addEventListener()
Code applicable :
var copyables = document.querySelectorAll(".copyable"); for (var item of copyables) { item.addEventListener('click', function(){ navigator.clipboard.writeText(item.textContent); // alert("Copié !"); }); }
Code HTML :
<p id="viaDOM_2">Cliquez encore, encore et encore !</p>
Code JS :
/* var cliquez4=document.getElementById("viaDOM_2"); cliquez4.addEventListener('click', function(){ alert("via DOM-2"); }); Et, plus court ci-dessous */ document.getElementById("viaDOM_2").addEventListener('click', function(){ alert("via DOM-2"); });
A priori, c'est encore plus "tordu" que DOM-0 !
Ce qui change par rapport à DOM-0, c'est que ce n'est plus une propriété qui est utilisé, mais la méthode .addEventListener() qui prend jusqu'à trois paramètres. Le premier, le nom de l'événement (sans "on"). Le second est souvent un appel de fonction anonyme ou, parfois, un appel à une fonction classique préalablement définie dans le code.
Le paramètre de cette fonction classique est passé par référence. Si
plusieurs écouteurs sont créés dans une boucle for et si le paramètre est le
compteur i, toutes les fonctions appelées par un des écouteurs auront le même
paramètre dont la valeur sera i+1. La solution consiste à passer le paramètre par
valeur, (par exemple, via une variable déclarée par le mot clé let)
Voir : addEventListeners_boucle.htm
Le troisième paramètre sera vu plus tard.
var cliquez4=document.getElementById("viaDOM_2"); /* cliquez4.addEventListener('click', function(){ alert("via DOM-2"); }); Ci-dessous, code équivalent */ var myFonction=function(){ alert("via DOM-2"); } cliquez4.addEventListener('click', myFonction);
De plus en plus "tordu" !
Avec le DOM-2, la création multiple d'événements (identiques ou non) pour un même élément est possible.
C'est fou ! Et, à quoi cela peut-il bien servir ?
Code HTML :
<button id="numero5">Clic n°5 !</button>
Code JS :
var clic5=document.getElementById("numero5"); clic5.addEventListener('click', function(){ alert("clic n°5 AAAA"); }); clic5.addEventListener('mouseenter', function(){ alert("clic n°5 BBBBBBBB"); });
Le nom de l'événement est toujours entièrement en minuscules.
L'ordre des événements peut rendre impossible l'action définie pour un autre
événement ...
La suppression des événements se fait avec la méthode .removeEventListener() en lui passant les mêmes paramètres [que la méthode .addEventListener()].
Code HTML :
<button id="clic6">Clic n°6 !</button>
Code JS :
var clic6=document.getElementById("clic6"); var myFonction6=function(){ alert("clic n°6"); } clic6.addEventListener('click', myFonction6); clic6.removeEventListener('click', myFonction6);
(le clic n'affiche pas de message d'alerte)
Avec la méthode .addEventListener() (et donc avec DOM-2), il est possible d'indiquer le sens du déclenchement (= troisième paramètre)
La capture (3ème paramètre = true) traite, pour le même événement, celui des balises supérieures puis celui des balises inférieures. Le bouillonnement (3ème paramètre = false), défini par défaut, traite, pour le même événement celui des balises inférieures puis celui des balises supérieures.
Moyen mnémotechnique : Lorsque cela bouillonne, cela remonte.
Event
L'objet Event est une mine d'informations sur l'événement déclenché : touches actuellement enfoncées, coordonnées du curseur, élément qui a déclenché l'événement, ...
Code HTML :
<button id="clic7">Clic n°7 !</button>
Code JS :
var clic7=document.getElementById("clic7"); clic7.addEventListener('click', function(e){ alert(e.target.innerHTML); });
Ici, le paramètre de la fonction anonyme est e
. C'est une
convention. Mais, cela peut être n'importe autre nom : param1, evenement, ...
La propriété .target, appliquée à l'objet e (de la classe Event), pointe sur la balise liée à cet événement.
(affiche le texte du bouton)
Position de la souris
.clientX = propriété, appliquée à l'objet e, pour connaître la position horizontale de la souris (depuis le bord gauche de la fenêtre) et .clientY pour la position verticale (depuis le bord supérieur de la fenêtre).
Touche tapée
Les événements keydown et keyup retourneront un caractère majuscule, que la touche Maj soit pressée ou non.
L'événement keypress sert à capter les caractère uniquement (donc pas les touches CTRL, MAJ, ...). Dès lors, il tient compte des caractères majuscules et minuscules
La propriété keyCode, appliquée à l'objet e, retourne un code ASCII
document.addEventListener('keypress', function(e) { press.innerHTML = e.keyCode; });
La méthode String.fromCharCode() retourne le caractère correspondant [au code ASCII passé en paramètre].
.preventDefault()
Le DOM-2 ajoute la méthode .preventDefault(), appliquée à l'objet
(généralement nommé "e"; ici, nommé "toto") bloque l'action par défaut (comme
return false;
)
Champ qui n'accepte pas le coller :
document.getElementById('no-paste').addEventListener('keydown',function (toto){toto.preventDefault()});
Champ qui n'accepte pas le coller et informe l'utilisateur que le collage est interdit :
document.getElementById('no-pasteWithMsg').addEventListener('keydown', function (x){ if (x.keyCode == 86){ // 86 = code de la lettre "v" alert("Coller dans ce champ est interdit"); x.preventDefault() } });
Champ qui accepte le coller mais bloque le bouton du formulaire (sans informer que le collage est interdit) :
document.getElementById('no-pasteBtnGray').addEventListener('keydown', function (y){ if (y.keyCode == 86){ // 86 = code de la lettre "v" document.getElementById('btnGray').disabled=true } });
.relatedTarget
Parfois, un événement [de la souris] appliqué sur un parent se propage à ses enfants et provoque des comportements inattendus. Heureusement, des solutions existent. Elles utilisent la propriété .relatedTarget appliquée à l'objet e.
Plus d'info : developpez.com/tutoriels/javascript/dom/
Les tableaux dynamiques
Il est possible de créer un tableau (régulier) dynamiquement. Il convient alors de déterminer le nombre de colonnes; puis, d'ajouter le nombre de lignes voulues.
Exemple : Déterminer la part de chacun à une table de restaurant.
De plus, dans chaque cellule est créé un écouteur d'événement. En l'occurrence, un clic permute le 0 en 1 et le 1 en 0.
Les formulaires
La propriété utilisé d'un élément correspondant à la balise <input> est value. Elle correspond à la valeur du champ. Cette propriété s'applique aussi à la balise <textarea>
document.getElementById("texte1").value="Texte ajouté via JS";
Les trois propriétés - .disabled, .checked et .readonly - retourne un booléen.
Dans le cas de boutons radio, il faut utiliser une boucle for pour vérifier
celui qui est coché : if (radios[i].checked==true) ...
Lorsqu'un bouton-radio est coché, sa propriété .checked vaut true.
=> on peut écrire : if (radios[i].checked) ...
document.querySelectorAll('input[type=radio]:checked')
permet
de retourner le bouton-radio coché.
Liste déroulante
Dans une liste déroulante, la propriété .options, appliquée à l'élément correspondant à la balise <select>, retourne un tableau de toutes les options.
Dans une liste déroulante, la propriété .selectedIndex, appliquée à l'élément correspondant à la balise <select>, retourne l'index de l'option sélectionnée.
la propriété .selectedIndex retourne l'index du premier élément sélectionné, si le <select multiple="multiple">.
tab.options[tab.selectedIndex].innerHTML
L'élément correspond à la balise <form> possède deux méthodes intéressantes. La première, .submit(), permet d'effectuer l'envoi d'un formulaire sans l'intervention de l'utilisateur. La deuxième, .reset()
NB : La méthode .submit() ne déclenche pas l'événement submit
La méthode .select(), en plus de donner le focus à l'élément, sélectionne le texte. Cette méthode ne fonctionne que sur des champs de texte ou bien un <textarea>
L'événement change ne fonctionne pas toujours comme son nom le suggère. Il attend que l'élément auquel il est attaché perde le focus avant de se déclencher.
<datalist>
La liste déroulante (<select>) est élément tellement important qu'une balise lui a été jointe : <datalist>
Gestion du CSS
La propriété .style d'un élément (de type 1) permet de modifier l'apparence d'une balise
element.style.width = '150px';
Ne pas oublier d'indiquer l'unité de la valeur (sans espace). Les valeurs sont de type String.
En JavaScript, les tirets sont interdits dans les noms des propriétés. La conversion des noms de propriété CSS en JS se fait supprimant le tiret et chaque lettre suivant ce tiret est remplacé par sa majuscule. Ainsi, par exemple, background-color (en CSS) devient backgroundColor (en JS)
Attention. La lecture du style via la propriété .style retourne la valeur de l'attribut style de la balise. Et non le style appliqué finalement à la balise, après avoir tenu compte de tous les héritages CSS.
Pour obtenir le style final, il faut utiliser la méthode getComputedStyle() de l'objet window.
getComputedStyle()
Les valeurs obtenues par le biais de [window.]getComputedStyle() sont en lecture seule. (Logique !)
L'unique paramètre obligatoire de getComputedStyle() est un nœud de type 1. Et, la valeur de retour .style est un objet CSSStyleDeclaration; raison pour laquelle, elle n'est pas récupérée directement. Est récupéré la valeur d'une des propriétés de cette valeur de retour, tel que la couleur du texte.
var balise = document.getElementById('myBalise'); var couleurTexte = getComputedStyle(balise).color;
Les .offset*
Certaines valeurs de positionnement ou de taille des éléments ne pourront pas être obtenues de façon simple avec getComputedStyle().
Les propriétés - offsetWidth, offsetHeight, offsetLeft, offsetTop, offsetParent - sont des propriétés, en lecture seule, d'un nœud (de type 1 = correspondant à une balise), et donc pas liées au CSS.
Les valeurs contenues dans ces propriétés (à part offsetParent) sont exprimées en pixels et sont donc de type Number.
- offsetWidth : Contient la largeur complète (width + padding + border) de l'élément.
- offsetHeight : Contient la hauteur complète (height + padding + border) de l'élément.
- offsetLeft : Surtout utile pour les éléments en position absolue. Contient la position de l'élément par rapport au bord gauche de son élément parent.
- offsetTop : Surtout utile pour les éléments en position absolue. Contient la position de l'élément par rapport au bord supérieur de son élément parent.
- offsetParent : Utile que pour un élément en position absolue ou relative ! Contient l'objet de l'élément parent par rapport auquel est positionné l'élément actuel.
Rappel : Un élément HTML en positionnement absolu se placera tout en haut à gauche de la page, par-dessus tous les autres éléments, s'il n'est pas déjà lui-même placé dans un élément en positionnement absolu.