Ajouter une touche à votre site

Les écrans tactiles sont disponibles sur de plus en plus d'appareils, des téléphones aux écrans des ordinateurs. Votre application doit réagir de façon intuitive et esthétique.

Les écrans tactiles sont disponibles sur de plus en plus d'appareils, allant des téléphones aux écrans d'ordinateur. Lorsque vos utilisateurs choisissent d'interagir avec votre interface utilisateur, votre application doit réagir à leur pression de manière intuitive.

Répondre aux états des éléments

Avez-vous déjà touché ou cliqué sur un élément d'une page Web pour vous demander si le site l'avait bien détecté ?

Il vous suffit de modifier la couleur d'un élément lorsque les utilisateurs touchent des parties de votre interface utilisateur ou interagissent avec elles pour s'assurer que votre site fonctionne. Non seulement cela atténue la frustration, mais cela peut également donner une impression rapide et réactive.

Les éléments DOM peuvent hériter de l'un des états suivants: par défaut, focus, survol et actif. Pour modifier l'interface utilisateur pour chacun de ces états, nous devons appliquer des styles aux pseudo-classes :hover, :focus et :active, comme indiqué ci-dessous:

.btn {
  background-color: #4285f4;
}

.btn:hover {
  background-color: #296cdb;
}

.btn:focus {
  background-color: #0f52c1;

  /* The outline parameter suppresses the border
  color / outline when focused */
  outline: 0;
}

.btn:active {
  background-color: #0039a8;
}

Essayer

Image illustrant différentes couleurs pour les états des boutons

Dans la plupart des navigateurs mobiles, les états de hover et/ou de hover s'appliquent à un élément dès que l'utilisateur appuie dessus.

Réfléchissez bien aux styles que vous définissez et à la façon dont ils s'afficheront pour l'utilisateur une fois qu'il aura terminé.

Supprimer les styles de navigateur par défaut

Une fois que vous avez ajouté des styles pour les différents états, vous remarquerez que la plupart des navigateurs implémentent leurs propres styles en réponse au clic de l'utilisateur. En effet, lors du lancement des appareils mobiles, un certain nombre de sites ne comportaient pas de style pour l'état :active. Par conséquent, de nombreux navigateurs ont ajouté une couleur ou un style de surbrillance supplémentaires pour fournir des commentaires aux utilisateurs.

La plupart des navigateurs utilisent la propriété CSS outline pour afficher un cercle autour d'un élément lorsqu'il est sélectionné. Vous pouvez le supprimer à l'aide de la commande suivante:

.btn:focus {
    outline: 0;

    /* Add replacement focus styling here (i.e. border) */
}

Safari et Chrome ajoutent une couleur de surbrillance en appuyant sur l'écran, qui peut être bloquée avec la propriété CSS -webkit-tap-highlight-color:

/* Webkit / Chrome Specific CSS to remove tap
highlight color */
.btn {
  -webkit-tap-highlight-color: transparent;
}

Essayer

Internet Explorer sur Windows Phone présente un comportement similaire, mais il est supprimé via une balise Meta:

<meta name="msapplication-tap-highlight" content="no">

Firefox a deux effets secondaires à gérer.

La pseudo-classe -moz-focus-inner, qui ajoute un contour sur les éléments tactiles, que vous pouvez supprimer en définissant border: 0

Si vous utilisez un élément <button> dans Firefox, un dégradé est appliqué. Vous pouvez le supprimer en définissant background-image: none.

/* Firefox Specific CSS to remove button
differences and focus ring */
.btn {
  background-image: none;
}

.btn::-moz-focus-inner {
  border: 0;
}

Essayer

Désactivation de la sélection de l'utilisateur

Lorsque vous créez votre UI, il peut arriver que vous souhaitiez que les utilisateurs interagissent avec vos éléments, mais que vous souhaitiez supprimer le comportement par défaut consistant à sélectionner du texte en appuyant de manière prolongée sur un appui prolongé ou en faisant glisser la souris sur votre UI.

Vous pouvez le faire avec la propriété CSS user-select, mais sachez que cette opération sur le contenu peut être extremely gênante pour les utilisateurs qui souhaitent sélectionner le texte dans l'élément. Veillez donc à l'utiliser avec parcimonie et parcimonie.

/* Example: Disable selecting text on a paragraph element: */
p.disable-text-selection {
  user-select: none;
}

Implémenter des gestes personnalisés

Si vous avez une idée d'interactions et de gestes personnalisés pour votre site, tenez compte de ces deux points:

  1. Compatibilité avec tous les navigateurs.
  2. Conserver une fréquence d'images élevée

Dans cet article, nous examinerons exactement les sujets suivants, couvrant les API dont nous avons besoin pour toucher tous les navigateurs, puis nous verrons comment utiliser efficacement ces événements.

En fonction de ce que vous souhaitez faire, vous souhaitez probablement que l'utilisateur interagisse avec un élément à la fois ou qu'il puisse interagir avec plusieurs éléments en même temps.

Nous allons examiner deux exemples dans cet article, qui illustrent tous deux la compatibilité avec tous les navigateurs et comment maintenir une fréquence d'images élevée.

Exemple de GIF tactile sur un document

Le premier exemple permettra à l'utilisateur d'interagir avec un élément. Dans ce cas, vous pouvez souhaiter que tous les événements tactiles soient attribués à cet élément unique, à condition que le geste ait initialement commencé sur l'élément lui-même. Par exemple, le fait de déplacer un doigt hors de l'élément pouvant être balayé peut toujours le contrôler.

Cela est utile, car il offre une grande flexibilité à l'utilisateur, mais applique une restriction sur la façon dont il peut interagir avec votre interface utilisateur.

Exemple de GIF d&#39;appui sur un élément

Toutefois, si vous vous attendez à ce que les utilisateurs interagissent avec plusieurs éléments en même temps (à l'aide de l'écran tactile multipoint), vous devez limiter l'appui à l'élément en question.

Cette approche est plus flexible pour les utilisateurs, mais complique la logique de manipulation de l'interface utilisateur et est moins résistante aux erreurs des utilisateurs.

Ajouter des écouteurs d'événements

Dans Chrome (version 55 et ultérieures), Internet Explorer et EdgePointerEvents sont recommandés pour implémenter des gestes personnalisés.

Dans les autres navigateurs, il s'agit de l'approche appropriée : TouchEvents et MouseEvents.

L'avantage de PointerEvents est qu'il fusionne plusieurs types d'entrées, y compris les événements de souris, d'écran tactile et de stylet, en un seul ensemble de rappels. Les événements à écouter sont pointerdown, pointermove, pointerup et pointercancel.

Les équivalents dans les autres navigateurs sont touchstart, touchmove, touchend et touchcancel pour les événements tactiles. Si vous souhaitez implémenter le même geste pour la saisie à la souris, vous devez implémenter mousedown, mousemove et mouseup.

Si vous avez des questions sur les événements à utiliser, consultez ce tableau des événements tactiles, de souris et de curseur.

L'utilisation de ces événements nécessite d'appeler la méthode addEventListener() sur un élément DOM, ainsi que le nom d'un événement, une fonction de rappel et une valeur booléenne. La valeur booléenne détermine si vous devez intercepter l'événement avant ou après que d'autres éléments aient eu l'occasion d'intercepter et d'interpréter les événements. (true signifie que vous souhaitez que l'événement soit placé avant les autres éléments.)

Voici un exemple d'écoute du début d'une interaction.

// Check if pointer events are supported.
if (window.PointerEvent) {
  // Add Pointer Event Listener
  swipeFrontElement.addEventListener('pointerdown', this.handleGestureStart, true);
  swipeFrontElement.addEventListener('pointermove', this.handleGestureMove, true);
  swipeFrontElement.addEventListener('pointerup', this.handleGestureEnd, true);
  swipeFrontElement.addEventListener('pointercancel', this.handleGestureEnd, true);
} else {
  // Add Touch Listener
  swipeFrontElement.addEventListener('touchstart', this.handleGestureStart, true);
  swipeFrontElement.addEventListener('touchmove', this.handleGestureMove, true);
  swipeFrontElement.addEventListener('touchend', this.handleGestureEnd, true);
  swipeFrontElement.addEventListener('touchcancel', this.handleGestureEnd, true);

  // Add Mouse Listener
  swipeFrontElement.addEventListener('mousedown', this.handleGestureStart, true);
}

Essayer

Gérer les interactions à un seul élément

Dans le court extrait de code ci-dessus, nous n'avons ajouté l'écouteur d'événements de démarrage que pour les événements de souris. En effet, les événements de souris ne se déclenchent que lorsque le curseur passe la souris sur l'élément auquel l'écouteur d'événements a été ajouté.

TouchEvents suit un geste une fois qu'il a démarré, quel que soit l'endroit où il se produit, et PointerEvents suit les événements, quel que soit l'endroit où le toucher se produit après l'appel de setPointerCapture sur un élément DOM.

Pour les événements de mouvement et de fin de la souris, nous ajoutons les écouteurs d'événements dans la méthode de début des gestes et les écouteurs au document, ce qui signifie qu'il peut suivre le curseur jusqu'à ce que le geste soit terminé.

Voici les étapes à suivre pour ce faire:

  1. Ajoutez tous les écouteurs TouchEvent et PointerEvent. Pour les événements "MouseEvents", ajoutez uniquement l'événement de début.
  2. Dans le rappel du geste de début, liez les événements de déplacement et de fin de la souris au document. De cette façon, tous les événements de souris sont reçus, qu'ils se produisent ou non sur l'élément d'origine. Pour PointerEvents, nous devons appeler setPointerCapture() sur l'élément d'origine pour recevoir tous les autres événements. Gérez ensuite le début du geste.
  3. Gérez les événements de déplacement.
  4. Lors de l'événement de fin, supprimez le mouvement de la souris et les écouteurs de fin du document, puis mettez fin au geste.

Vous trouverez ci-dessous un extrait de notre méthode handleGestureStart(), qui ajoute les événements de déplacement et de fin au document:

// Handle the start of gestures
this.handleGestureStart = function(evt) {
  evt.preventDefault();

  if(evt.touches && evt.touches.length > 1) {
    return;
  }

  // Add the move and end listeners
  if (window.PointerEvent) {
    evt.target.setPointerCapture(evt.pointerId);
  } else {
    // Add Mouse Listeners
    document.addEventListener('mousemove', this.handleGestureMove, true);
    document.addEventListener('mouseup', this.handleGestureEnd, true);
  }

  initialTouchPos = getGesturePointFromEvent(evt);

  swipeFrontElement.style.transition = 'initial';
}.bind(this);

Essayer

Le rappel de fin que nous ajoutons est handleGestureEnd(). Il supprime les écouteurs d'événements de déplacement et de fin du document et libère la capture du pointeur lorsque le geste est terminé, comme ceci:

// Handle end gestures
this.handleGestureEnd = function(evt) {
  evt.preventDefault();

  if (evt.touches && evt.touches.length > 0) {
    return;
  }

  rafPending = false;

  // Remove Event Listeners
  if (window.PointerEvent) {
    evt.target.releasePointerCapture(evt.pointerId);
  } else {
    // Remove Mouse Listeners
    document.removeEventListener('mousemove', this.handleGestureMove, true);
    document.removeEventListener('mouseup', this.handleGestureEnd, true);
  }

  updateSwipeRestPosition();

  initialTouchPos = null;
}.bind(this);

Essayer

En suivant ce modèle d'ajout de l'événement de déplacement au document, si l'utilisateur commence à interagir avec un élément et déplace son geste en dehors de l'élément, nous continuerons à recevoir les mouvements de la souris, quel que soit leur emplacement sur la page, car le document reçoit les événements.

Ce schéma montre ce que font les événements tactiles lorsque nous ajoutons les événements de déplacement et de fin au document lorsqu'un geste commence.

Illustration de la liaison d&#39;événements tactiles avec un document dans &quot;touchstart&quot;

Répondre efficacement au toucher

Maintenant que les événements de début et de fin sont réglés, nous pouvons répondre aux événements tactiles.

Pour tous les événements de démarrage et de déplacement, vous pouvez facilement extraire x et y d'un événement.

L'exemple suivant vérifie si l'événement provient d'une TouchEvent en vérifiant si targetTouches existe. Si c'est le cas, il extrait clientX et clientY dès la première pression. Si l'événement est un PointerEvent ou un MouseEvent, il extrait clientX et clientY directement de l'événement lui-même.

function getGesturePointFromEvent(evt) {
    var point = {};

    if (evt.targetTouches) {
      // Prefer Touch Events
      point.x = evt.targetTouches[0].clientX;
      point.y = evt.targetTouches[0].clientY;
    } else {
      // Either Mouse event or Pointer Event
      point.x = evt.clientX;
      point.y = evt.clientY;
    }

    return point;
  }

Essayer

Un TouchEvent comporte trois listes contenant des données tactiles:

  • touches: liste de toutes les touches actives à l'écran, quel que soit l'élément DOM sur lequel elles se trouvent.
  • targetTouches: liste des points de contact actuellement sur l'élément DOM auquel l'événement est lié.
  • changedTouches: liste des modifications apportées à l'événement, qui ont été déclenchées.

Dans la plupart des cas, targetTouches vous offre tout ce dont vous avez besoin et dont vous avez besoin. Pour en savoir plus sur ces listes, consultez Listes tactiles.

Utiliser requestAnimationFrame

Étant donné que les rappels d'événements sont déclenchés sur le thread principal, nous voulons exécuter le moins de code possible dans les rappels de nos événements, ce qui permet de maintenir une fréquence de frames élevée et d'éviter les à-coups.

requestAnimationFrame() nous permet de mettre à jour l'interface utilisateur juste avant que le navigateur ne dessine un cadre, ce qui nous aide à retirer certaines tâches de nos rappels d'événements.

Si vous ne connaissez pas requestAnimationFrame(), cliquez ici pour en savoir plus.

Une implémentation type consiste à enregistrer les coordonnées x et y à partir des événements de démarrage et de déplacement, puis à demander une image d'animation dans le rappel de l'événement de déplacement.

Dans notre démonstration, nous stockons la position initiale du contact dans handleGestureStart() (recherchez initialTouchPos):

// Handle the start of gestures
this.handleGestureStart = function(evt) {
  evt.preventDefault();

  if (evt.touches && evt.touches.length > 1) {
    return;
  }

  // Add the move and end listeners
  if (window.PointerEvent) {
    evt.target.setPointerCapture(evt.pointerId);
  } else {
    // Add Mouse Listeners
    document.addEventListener('mousemove', this.handleGestureMove, true);
    document.addEventListener('mouseup', this.handleGestureEnd, true);
  }

  initialTouchPos = getGesturePointFromEvent(evt);

  swipeFrontElement.style.transition = 'initial';
}.bind(this);

La méthode handleGestureMove() stocke la position de son événement avant de demander une image d'animation si nécessaire, en transmettant notre fonction onAnimFrame() comme rappel:

this.handleGestureMove = function (evt) {
  evt.preventDefault();

  if (!initialTouchPos) {
    return;
  }

  lastTouchPos = getGesturePointFromEvent(evt);

  if (rafPending) {
    return;
  }

  rafPending = true;

  window.requestAnimFrame(onAnimFrame);
}.bind(this);

La valeur onAnimFrame est une fonction qui, lorsqu'elle est appelée, modifie notre interface utilisateur pour la déplacer. En transmettant cette fonction dans requestAnimationFrame(), nous disons au navigateur de l'appeler juste avant de mettre à jour la page (c'est-à-dire d'appliquer toutes les modifications apportées à la page).

Dans le rappel handleGestureMove(), nous vérifions initialement si rafPending est "false", ce qui indique si onAnimFrame() a été appelé par requestAnimationFrame() depuis le dernier événement de déplacement. Cela signifie qu'un seul requestAnimationFrame() est en attente d'exécution à la fois.

Lorsque notre rappel onAnimFrame() est exécuté, nous définissons la transformation sur tous les éléments que nous souhaitons déplacer avant de définir rafPending sur false, ce qui permet au prochain événement tactile de demander une nouvelle image d'animation.

function onAnimFrame() {
  if (!rafPending) {
    return;
  }

  var differenceInX = initialTouchPos.x - lastTouchPos.x;
  var newXTransform = (currentXPosition - differenceInX)+'px';
  var transformStyle = 'translateX('+newXTransform+')';

  swipeFrontElement.style.webkitTransform = transformStyle;
  swipeFrontElement.style.MozTransform = transformStyle;
  swipeFrontElement.style.msTransform = transformStyle;
  swipeFrontElement.style.transform = transformStyle;

  rafPending = false;
}

Contrôler les gestes à l'aide des actions tactiles

La propriété CSS touch-action vous permet de contrôler le comportement tactile par défaut d'un élément. Dans nos exemples, nous utilisons touch-action: none pour empêcher le navigateur d'effectuer quoi que ce soit lorsque l'utilisateur appuie dessus, ce qui nous permet d'intercepter tous les événements tactiles.

/* Pass all touches to javascript: */
button.custom-touch-logic {
  touch-action: none;
}

L'utilisation de touch-action: none est une option nucléaire, car elle empêche tous les comportements par défaut du navigateur. Dans de nombreux cas, l'une des options ci-dessous est une meilleure solution.

touch-action vous permet de désactiver les gestes implémentés par un navigateur. Par exemple, IE10+ prend en charge un double tapotement pour zoomer. En définissant une touch-action sur manipulation, vous empêchez le comportement par défaut du double appui.

Cela vous permet d'implémenter vous-même un geste de double tapotement.

Vous trouverez ci-dessous une liste des valeurs touch-action couramment utilisées:

Paramètres des actions tactiles
touch-action: none Aucune interaction tactile ne sera gérée par le navigateur.
touch-action: pinch-zoom Désactive toutes les interactions avec le navigateur, telles que "touch-action: none", à l'exception de "pinch-zoom", qui est toujours gérée par le navigateur.
touch-action: pan-y pinch-zoom Gérez les défilements horizontaux en JavaScript sans désactiver le défilement vertical ni le zoom par pincement (par exemple, les carrousels d'images).
touch-action: manipulation Désactive le double tapotement, ce qui évite tout délai de clic du navigateur. Permet de faire défiler l'écran et de pincer pour zoomer sur le navigateur.

Compatibilité avec les anciennes versions d'IE

Si vous souhaitez prendre en charge IE10, vous devez gérer les versions de PointerEvents précédées du fournisseur.

Pour vérifier la prise en charge de PointerEvents, vous recherchez généralement window.PointerEvent, mais dans IE10, vous recherchez window.navigator.msPointerEnabled.

Les noms d'événements avec les préfixes de fournisseur sont les suivants: 'MSPointerDown', 'MSPointerUp' et 'MSPointerMove'.

L'exemple ci-dessous vous montre comment vérifier la compatibilité et modifier le nom des événements.

var pointerDownName = 'pointerdown';
var pointerUpName = 'pointerup';
var pointerMoveName = 'pointermove';

if (window.navigator.msPointerEnabled) {
  pointerDownName = 'MSPointerDown';
  pointerUpName = 'MSPointerUp';
  pointerMoveName = 'MSPointerMove';
}

// Simple way to check if some form of pointerevents is enabled or not
window.PointerEventsSupport = false;
if (window.PointerEvent || window.navigator.msPointerEnabled) {
  window.PointerEventsSupport = true;
}

Pour en savoir plus, consultez cet article de Microsoft sur les mises à jour.

Reference

Pseudo-classes pour les états tactiles

Classe Exemple Description
:hover
Bouton à l&#39;état pressé
Entrée lorsqu'un curseur est placé sur un élément. Les modifications apportées à l'interface utilisateur lorsque l'utilisateur pointe dessus sont utiles pour encourager les utilisateurs à interagir avec les éléments.
:focus
Bouton avec état de sélection
Entrée lorsque l'utilisateur parcourt les éléments d'une page par onglet. L'état de sélection permet à l'utilisateur de savoir avec quel élément il interagit actuellement et de naviguer facilement dans l'interface utilisateur à l'aide d'un clavier.
:actif
Bouton à l&#39;état pressé
Valeur saisie lors de la sélection d'un élément, par exemple lorsqu'un utilisateur clique ou appuie dessus.

La référence définitive est disponible sur la page Événements tactiles du W3C.

Événements tactiles, de souris et de curseur

Ces événements sont les éléments de base permettant d'ajouter de nouveaux gestes à votre application:

Événements relatifs au toucher, à la souris et au pointeur
touchstart, mousedown, pointerdown Cette méthode est appelée lorsqu'un doigt touche un élément pour la première fois ou lorsque l'utilisateur clique sur le bouton de la souris.
touchmove, mousemove, pointermove Cette méthode est appelée lorsque l'utilisateur déplace son doigt sur l'écran ou effectue un déplacement avec la souris.
touchend, mouseup, pointerup Cette action est appelée lorsque l'utilisateur soulève son doigt de l'écran ou relâche la souris.
touchcancel pointercancel Cette action est appelée lorsque le navigateur annule les gestes tactiles. Par exemple, un utilisateur appuie sur une application Web, puis change d'onglet.

Listes tactiles

Chaque événement tactile comprend trois attributs de liste:

Attributs des événements tactiles
touches Liste de toutes les touches actuelles à l'écran, quels que soient les éléments touchés.
targetTouches Liste des interactions ayant commencé sur l'élément cible de l'événement en cours. Par exemple, si vous liez à un <button>, vous n'obtiendrez que les points de contact actuellement sur ce bouton. Si vous liez au document, vous aurez toutes les touches actuellement sur le document.
changedTouches Liste des éléments tactiles qui ont été modifiés et qui ont déclenché l'événement :
  • Pour l'événement touchstart : liste des points de contact qui viennent d'être actifs avec l'événement en cours.
  • Pour l'événement touchmove : liste des points de contact qui ont été déplacés depuis le dernier événement.
  • Pour les événements touchend et touchcancel : liste des points de contact qui viennent d'être supprimés de la surface.

Activer la prise en charge de l'état actif sur iOS

Malheureusement, Safari sur iOS n'applique pas l'état active par défaut. Pour que cela fonctionne, vous devez ajouter un écouteur d'événements touchstart au corps du document ou à chaque élément.

Vous devez effectuer cette opération derrière un test user-agent afin de ne l'exécuter que sur les appareils iOS.

L'ajout d'un début tactile au corps a l'avantage de s'appliquer à tous les éléments du DOM, mais cela peut entraîner des problèmes de performances lors du défilement de la page.

window.onload = function() {
  if (/iP(hone|ad)/.test(window.navigator.userAgent)) {
    document.body.addEventListener('touchstart', function() {}, false);
  }
};

L'alternative consiste à ajouter les écouteurs de démarrage tactile à tous les éléments interactifs de la page, afin de réduire certains problèmes de performances.

window.onload = function() {
  if (/iP(hone|ad)/.test(window.navigator.userAgent)) {
    var elements = document.querySelectorAll('button');
    var emptyFunction = function() {};

    for (var i = 0; i < elements.length; i++) {
        elements[i].addEventListener('touchstart', emptyFunction, false);
    }
  }
};