Interractions utilisateur

Matériel

On peut utiliser les événements pour détecter quand l’utilisateur interragit avec la page en utilisant son clavier, sa souris, son doigt (dans le cas d’un écran tactile), voire sa manette.

Clavier

Événement Description
keydown L’utilisateur presse une touche. C’est à ce moment là que vous pouvez détecter si l’utilisateur tappe Entrée, tabulation, une flèche, etc.
keypress L’utilisateur tape un caractère, avant que celui-ci ne soit écrit. keypress n’est pas déclenché pour les caractères de contrôle, les flèches, et tout autre caractère qui n’est pas visible.
keyup L’utilisateur relache la touche, l’action a été déclenché. C’est à ce moment là que vous pouvez détecter si l’utilisateur a écrit quelque chose
change Le contenu du champ est modifié. Cette technique a l’avantage de fonctionner si l’utilisateur fait un copier/coller.
element.addEventListener('keydown', function(e){
    var code = e.keyCode || e.which || 0;

    if(code == 13) {
        console.log('Entrée');
    }
});
element.addEventListener('change', function(){
    console.log(this.value);
});

Souris

Événement Description
clic Clic
dblclick Double-clic
contextmenu Clic droit
scroll L’utilisateur fait défiler le contenu de l’élément
wheel La molette de la souris est scrollée. scroll n’est pas nécessairement déclenché (dans le cas où l’élément n’a pas d’overflow)
mousedown Le bouton de la souris est pressé
mouseup Le bouton de la souris est relaché
mousemove La souris est déplacée
mouseenter La souris entre à l’intérieur de l’élément
mouseleave La souris sort de l’élément
mouseover La souris entre à l’intérieur de l’élément ou à l’intérieur d’un de ses enfants. Exemple mouseenter vs mouseover
mouseout La souris sort de l’élément ou de l’un de ses enfants

Vous pouvez également implémenter le clic long en utilisant un timeout:

var timerClickDuration,
    clicLong = false;

element.addEventListener("mousedown", function(){
    timerClickDuration = setTimeout(function(){
        clicLong = true;
    }, 150);
});
element.addEventListener("mouseup", function(){
   clearTimeout(timerClickDuration);

   console.log(clicLong ? "clic long" : "clic court");
   clicLong = false;
});

Toucher du doigt

Il existe des événements indépendants des événements de la souris pour les écrans tactiles:

Événement Description
touchstart L’utilisateur touche l’écran
touchmove L’utilisateur déplace son doigt sur l’écran
touchend L’utilisateur enlève son doigt de l’écran
touchcancel L’événement touch est annulé, par exemple si l’utilisateur met 5 cinq doigts sur l’écran

Pointeur

Les événements de pointeur (point de contact) aident à gérer plus facilement l’interraction utilisateur, en normalisant le traitement de multiples formes d’entrée comme la souris, le toucher du doigt et la saisie au stylet.

Les événements génériques pour gérer la saisie du pointeur (tout contact avec l’écran, avec la souris ou le toucher) ressemblent beaucoup à ceux pour la souris:

Événement Description
pointerdown Un point de contact est établit
pointerup Le point de contact est levé
pointermove Le point de contact est déplacé
pointerenter Le point de contact entre à l’intérieur d’un élément
pointerover Le point de contact est à l’intérieur d’un élément
pointerleave Le point de contact sort d’un élément
pointerout Le point de contact n’est plus assigné à l’élément (leave, cancel)
pointercancel Le point de contact ne peut plus générer d’événements (ex: l’utilisateur met 5 cinq sur l’écran)

Les événements de pointeur ne sont pas encore beaucoup pris en charge par les navigateurs, mais vous pouvez utiliser le polyfill pointer.js.

Manette

Événement Description
gamepadconnected Une manette est connectée
gamepaddisconnected La manette est déconnectée

Actions

On peut utiliser les événements pour détecter quand l’utilisateur effectue une action particulière, comme l’impression ou le redimensionnement de la page; contrôler les actions effectuées, par exemple permettre de zoomer un élément en pinçant (sur smartphone); ou même utiliser des API déclencher des actions, comme la copie vers le presse papier.

touch-action

La propriété CSS touch-action permet de spécifier si le navigateur doit appliquer son comportement par défaut aux interractions tactiles, comme zoomer ou scroller.

Valeurs possibles

touch-action: pan-y pinch-zoom;

Zoom

Pour détecter si l’utilisateur essaie de zoomer/dézoomer en pinçant/étirant l’écran, il est nécessaire d’utiliser les gestionnaires d’événement de pointeur et de vérifier le nombre de pointeurs utilisé et le type de mouvement effectué. Codepen pincer/étirer.

Il est également possible d’utiliser des librairies, comme Hammer.js pour détecter les événements complexes - zoom, swipe, rotate… Swipe.js permet de détecter le swipe.


Contenu éditable

Tout élément DOM peut être rendu éditable en utilisant l’attribut contenteditable.

<div contenteditable="true">

    Ce texte peut être édité par l'utilisateur.
</div>

On peut utiliser cette fonctionnalité pour créer un éditeur rich-text par exemple. Article MDN


Plein écran

Si vous avez besoin de présenter un élément en plein écran (comme une vidéo par exemple), vous pouvez appeler requestFullscreen() sur cet élément. Le plein écran peut être quitté par l’utilisateur en pressant Echap ou avec JavaScript en appelant document.exitFullscreen().

function openFullscreen(element) {
  if (element.requestFullscreen) {
    element.requestFullscreen();
  } else if (element.msRequestFullscreen) {
    element.msRequestFullscreen();
  } else if (element.mozRequestFullScreen) {
    element.mozRequestFullScreen();
  } else if (element.webkitRequestFullscreen) {
    element.webkitRequestFullscreen();
  }
}
document.addEventListener("fullscreenchange", function(event) {
  if(document.fullscreenEnabled) {
    console.log(document.fullscreenElement);
  }
});
document.addEventListener("fullscreenerror", function(event) {
  console.error("Fullscreen error");
});

Vous pouvez utiliser Fscreen pour normaliser les préfixes fournisseur (webkit, mos, ms).


Redimensionner

La fenêtre peut être redimensionnée en JavaScript avec

L’événement resize est déclenché après le redimensionnement de la fenêtre, initié par l’utilisateur ou par JavaScript.
Le même événement existe sur les éléments redimensionnables (comme textarera).

window.onresize = function(){
  console.log(window.innerWidth, window.innerHeight);
};

Scroller la page

Il est possible de scroller la page en JavaScript avec

L’événement scroll est déclenché quand la fenêtre est scrollée. Comme la fréquence de cet événement peut être élevée, le gestionnaire d’événement lié ne devrait pas effectuer d’opération couteuse. À la place, temporiser l’événement avec requestAnimationFrame ou setTimeout.

Les propriétés pageXOffset et pageYOffset sont des propriétés en lecture seule, retournant le nombre de pixels actuellement scrollés. scrollX et scrollY sont des alias de ces propriétés.

window.addEventListener('scroll', function(e) {
  console.log(window.scrollX, window.scrollY);
});

Positionner un élément

offset

Les propriétés offsetWidth et offsetHeight renvoient la largeur et hauteur visible de l’élément, comprenant les bordures, le padding, et la scrollbar.

Les propriétés offsetLeft et offsetTop renvoient la position du coin supérieur gauche de l’élément par rapport à offsetParent. C’est utile si vous voulez positionner une popup en absolu à côté de l’élément par exemple.

offsetParent est l’ancêtre le plus proche de l’élément qui a une position autre que statique. Ainsi, pour connaître la position de l’élément dans la page, il est nécessaire d’utiliser une boucle.

function getElementPosition(element) {
  var x = 0,
      y = 0;

  while(element) {
    x += element.offsetLeft;
    y += element.offsetTop,

    element = element.offsetParent;
  }
  return {x: x, y: y};
}

getBoundingClientRect

La méthode getBoundingClientRect() renvoie la taille et la position de l’élément par rapport au viewport, elle prend donc en compte le scroll alors que les propriétés offset non. Codepen getBoundingClientRect().top vs offsetTop.

client

clientWidth et clientHeight sont comme offsetwidth et offsetHeight mais n’incluent pas la taille de la bordure, uniquement le contenu et padding.

Les propriétés clientLeft et clientTop ne sont pas très utiles: elles retournent la distance entre le bord extérieur du padding et le bord extérieur de la bordure. Si l’élément a des scrollbars et qu’elles sont placés en haut ou à gauche (ce qui est inhabituel), ces valeurs incluent également la taille de la scrollbar.

scroll

scrolWidth et scrollHeight renvoient la largeur du contenu, du padding et du contenu en overflow. Quand l’élément n’a pas d’overflow, ces valeurs sont également à clientWidth et clientHeight.

Les propriétés scrollLeft et scrollTop donnent les positions des scrollbars de l’élément. Ces propriétés sont en lecture/écriture, il est donc possible de scroller un élément en mettant à jour ces valeurs.

element.scrollLeft += 10;

Lorsque l’élément est scrollé, l’événément scroll est déclenché.

element.addEventListener('scroll', function(e) {
  console.log(element.scrollTop);
});

scrollIntoView

La méthode scrollIntoView fait défiler la page de manière à rendre l’élément visible. Il est possible de passer des options (optionnel):

element.scrollIntoView({
  behavior: "instant",
  block: "end",
  inline: "nearest"
});

Impression

L’événement beforeprint est déclenché juste avant que la page ne soit imprimée ou que l’aperçu d’impression ne soit affiché. afterprint quand l’impression a commencé ou que l’aperçu d’écran est fermé.


Sélection

Sélectionner un input

L’évènement select est déclenché quand du texte est sélectionné à l’intérieur d’un élément input ou textarea.
Le raccourcis Ctrl + a ne déclenche pas cet événement.

var input = document.getElementById('input');

input.addEventListener('select', function() {
  alert('La sélection a changé !');
}, false);

La méthode select() quant à elle permet de sélectionner tout le contenu d’un élément input ou textarea.

input.select();

Sélectionner un div

L’API Selection est plus intéressante, elle permet de récupérer et manipuler la sélection.
Pour ce faire, on récupère un objet Selection avec la méthode window.getSelection().

var selection = window.getSelection();

if(!selection) { // Opera!
  selection = document.selection.createRange();
}

Cet objet nous permet de

Manipuler la sélection

Lorsque des élements sont sélectionnés, on peut alors

JSFiddle Selection API
Consulter la doc pour la liste complète des fonctionnalités de l’API Selection.

Événements

L’événement selectstart est déclenché quand l’utilisateur commence à sélectionner du texte et selectChange lorsque l’utilisateur relache la souris.

var selection;

document.onselectionchange = function() {
  console.log('New selection made');
  selection = window.getSelection();
};

Désactiver la sélection

La sélection peut être désactivée via CSS

.noselect {
  -webkit-touch-callout: none; /* iOS Safari */
    -webkit-user-select: none; /* Safari */
     -khtml-user-select: none; /* Konqueror HTML */
       -moz-user-select: none; /* Firefox */
        -ms-user-select: none; /* Internet Explorer/Edge */
            user-select: none; /* Non-prefixed version, currently
                                  supported by Chrome and Opera */
}

Ou via JavaScript

element.onselectionchange = function() { return false; }

Copier/coller


Drag & Drop

Le Drag & Drop, ou glisser-déposer en français, s’effectue en trois temps

  1. cliquer sur un élement
  2. tout en gardant le clic enfoncé, glisser l’élément à un autre endroit
  3. relacher le bouton de la souris pour déposer l’élément à cet endroit

Récapitulatif des événements:

Événement Description
dragstart Déclenché sur l’élément quand l’utilisateur comme à le déplacer (obligatoire pour autoriser le drag)
drag Déclénché sur l’élément toutes à peu près les 100ms lors du glissement
dragend Déclenché sur l’élément quand l’utilisateur le lache
   
dragenter Déclenché sur l’élément survolé lorsque l’utilisateur en survole un
dragover Déclenché sur l’élément survolé à peu près toutes les 100ms (obligatoire pour autoriser le drop)
dragleave Déclenché sur l’élément qui était survolé lorsque l’utilisateur quitte la zone de l’élément
drop Déclenché sur l’élément cible s’il autorise le dépot

Note: Les événements dragstart et dragend ne sont pas déclenchés lorsque l’utilisateur drag & drop un fichier à partir de son système d’exploitation.


Orientation de l’appareil

Certains appareils peuvent avoir une orientation, par rapport au sol. C’est notamment le cas des smartphones.
Il existe deux types d’événements JavaScript pour gérer l’orientation:


Orientation de l’écran

L’orientation de l’écran peut être en portrait (hauteur > largeur) ou en paysage (largeur > portrait). Si un appareil n’a pas forcemment la capacité de détecter sa propre orientation, un écran lui en possède toujours une.

Il y a deux manières de gérer l’orientation de l’écran:


Capturer la souris

La capture de souris permet de désigner un élément spécifique comme étant la cible des événements de pointeur jusqu’à ce que le bouton de la souris soit relâché ou que la capture soit explicitement libérée. Lorsqu’un élément capture les événement, cela a pour effet de supprimer les événements de pointeur sur tous les autres éléments. JsFiddle setCapture

La méthode setCapture permet de capturer les événements et releasePointerCapture de libérer la capture.

div.onpointerdown = function(e) {
   div.setPointerCapture(e.pointerId);
};
div.onpointercancel = function(e) {
  div.releasePointerCapture(e.pointerId);
};

Lorsqu’un élément reçoit ou perd la capture, les événements gotpointercapture ou lostpointercapture sont déclenchés sur l’élément (respectivement).


Verrouiller le pointeur

Il est possible de verrouiller le pointeur à un élément donné et de cacher le curseur de la souris. C’est utile par exemple pour les jeux qui ont besoin d’écouter la souris pour contrôler les mouvements. Les joueurs peuvent cliquer sur les boutons et déplacer le curseur de la souris sans se soucier de quitter la zone de jeu, les événements continuent d’être déclenchés sur l’élément verrouillé. Pointer lock demo (code source).

Le verrouillage de pointeur (API Pointer Lock) partage des similitudes avec la capture de souris mais diffère sur les points suivants:

  1. La capture de souris offre un flot ininterrompu d’évènements sur un élément cible quand la souris bouge mais s’arrête quand le bouton de la souris est relaché. Pointer lock ne libère pas la capture tant qu’un appel explicite à l’API n’a pas été effectué - il continue de déclencher des évènements peu importe l’état des boutons de la souris.
  2. Ne se limite pas aux bordures du navigateur ou de l’écran.
  3. Cache le curseur.

Pour verrouiller le pointeur, on utilise la méthode requestPointerLock(). Par exemple:

canvas.requestPointerLock = canvas.requestPointerLock
    || canvas.mozRequestPointerLock
    || canvas.webkitPointerLockElement;
canvas.requestPointerLock()

document.pointerLockElement contient l’élément actuellement verrouillé.

document.pointerLockElement = document.pointerLockElement
    || document.mozPointerLockElement
    || document.webkitPointerLockElement;
console.log(document.pointerLockElement);

La méthode document.exitPointerLock() permet de libérer le pointeur.

document.exitPointerLock = document.exitPointerLock
    || document.mozExitPointerLock
    || document.webkitExitPointerLock;
document.exitPointerLock();

Quand l’état de verrouillage du pointeur change, l’événement pointerlockchange est envoyé au document.

document.addEventListener('pointerlockchange', pointerLockChange, false);
document.addEventListener('mozpointerlockchange', pointerLockChange, false);
document.addEventListener('webkitpointerlockchange', pointerLockChange, false);

Le verrouilage du pointeur ne peut concerner qu’une seule iframe à la fois. Quand vous verrouillez une iframe, vous ne pouvez pas essayer de verrouiller une autre iframe et y transférer la cible; une erreur sera levée. Pour éviter cette limitation, déverrouillez d’abord la première iframe, puis verrouillez la seconde. Si l’appel à requestPointerLock() déclenche une erreur, l’événement pointerlockerror est envoyé au document.

document.addEventListener('pointerlockerror', lockError, false);
document.addEventListener('mozpointerlockerror', lockError, false);
document.addEventListener('webkitpointerlockerror', pointerLockChange, false);