Créer ses composants Touch avec HTML5 CSS3 et JavaScript – Partie 2/2
Ce billet est la deuxième partie d’une note de blog en deux volets, proposant des solutions pour adapter des composants de formulaires à destination d’une utilisation sur un écran tactile.
Dans ce deuxième article donc, nous allons utiliser les évènements Touch pour créer un slider (input de type range en HTML5 ou Toggle Switch dans Windows 8 / Phone). Si vous ne l’avez pas encore fait je vous conseille de commencer par découvrir dans la première partie l’adaptation de boutons et d’une liste déroulante pour des interface tactiles.
Pourquoi ne pas utiliser un input range HTML5?
On peut en effet se demander pourquoi faire le choix de créer son propre contrôle de type slider quand il existe un contrôle prévu pour cela en HTML5.
Les inputs de type range (c.f. note sur les nouveautés HTML5 pour les formulaires) permettent de:
- Sélectionner une valeur en faisant glisser un curseur le long d’un axe
- Définir un minimum et un maximum ainsi que des steps qui définiront les valeurs possibles
Ce type de contrôle donne le résultat suivant:

Pour:
- Respecte la norme HTML5 du W3C.
- Rien à coder (ou presque) pour le développeur, c’est le navigateur qui fait tout
- fonctionne à la souris et au touch
Contre:
- Support mitigé des navigateurs: c.f. CanIUse => Firefox ne le gère pas du tout et affiche un champs de texte à la place
- Expérience utilisateur très décevante sur Smartphones et Tablettes pour l’instant: suivi du doigt pas instantané et imprécision dans la manipulation dû au décalage entre le mouvement du doigt et le mouvement du curseur
- Aspect très peu personnalisable en CSS pour le moment
Pourquoi ne pas utiliser le slider JQuery?
Jquery UI et Jquery Mobile offrent des contrôles de type slider proposant un implémentation de ce type de contrôles à l’aide de javascript. Il s’agit d’un fallback Javascript pour les navigateurs ne supportant pas les inputs de type range.
Voici à quoi ressemble le slider JQuery:

Pour:
- Apporte le support des input de type range HTML5 aux navigateurs qui ne le supportent pas
- Rien à coder (ou presque) pour le développeur, c’est JQuery qui fait tout
- fonctionne à la souris et au touch
Contre:
- Expérience utilisateur très décevante sur Smartphones et Tablettes pour l’instant: suivi du doigt pas instantané et imprécision dans la manipulation dû au décalage entre le mouvement du doigt et le mouvement du curseur
- Aspect très peu personnalisable en CSS pour le moment
Il existe d’autres implémentations javascript et même JQuery des contrôles de type sliders et je ne les ai bien sûr pas tous testé. Le but de cet article est de montrer comment utiliser les évènement touch en javascript et la mauvaise expérience d’utilisation offerte sur Smatrphones et Tablettes par les solution citées ci-dessus, justifient la ré-écriture d’un composant.
Création du Slider
HTML et CSS: Aspect du contrôle
Comme nous faisons notre propre slider de zéro, autant en profiter pour en personnaliser le rendu. Pour cet exercice, j’ai opté pour un design inspiré des interfaces METRO de Windows 8 et Windows Phone pour le slider:

Le design est bien plus adapter à une utilisation tactile avec un large curseur et un contrôle plus haut pour une meilleur visibilité sous le doigt.
Pour obtenir ce rendu, j’utilise:
- Un div de fond définissant l’axe du slider: id=”picker1”
- Un div pour le curseur: id=”block”
En HTML, cela donne:
<div class="picker" id="picker1">
<div id="block"></div>
</div>
On y ajoute un peu de CSS:
#picker1 {
left: 200px;
width: 150px;
height: 50px;
border: 2px solid black;
position: relative;
}
.picker > #block {
height: 76px;
width: 50px;
position: relative;
top: -13px;
left: -2px;
background-color: #000;
border-right: 5px solid black;
}
Remarque: le curseur défini une position à left: –2px afin de compenser la border de 2px de son parent et ainsi se coller complètement à gauche.
Cela nous donne le résultat suivant:

C’est un bon début mais pour le moment notre div ne peut être manipulé et reste fixe.
Javascript: Implémentation du comportement
Le comportement de ce type de sliders respecte plusieurs règles que nous allons implémenter:
- Lorsque l’on touch le curseur et que l’on déplace le doigt le long de l’axe, le curseur suit le doigt
- Lorsque le curseur se déplace la couleur de fond à gauche du curseur est différente et suit le curseur (ici: blanc à droite et vert à gauche)
- Si le slider définit des points d’ancrage, le fait de relâcher le doigt ira placer le curseur sur le point le plus proche
Déplacement du curseur au doigt
Le déplacement de notre curseur en fonction des actions de l’utilisateur vont être entièrement gérée en JavaScript.
Tout d’abords, on définit des variable pour simplifier la manipulation de notre slider et du curseur:
var slider = document.getElementById("picker1");
var cursor = document.getElementById("block");
Ensuite, on définit les positions minimales et maximales en pixel sur laquelle pourra se déplacer le curseur dans l’axe du slider:
var minX = -2;
var maxX = slider.scrollWidth - cursor.scrollWidth;
Remarquez que la valeur minimale tiens compte de la marge du parent et que la valeur maximale quand à elle tient compte de la largeur du du curseur, afin que celui-ci se sorte pas de l’axe.
Pour obtenir la position du doigt à l’intérieur du slider, il faut soustraire la position du doigt à l’espace entre le bord de la page te le bord du slider:

On définit la variable offsetLeft qui nous fournit la valeur à soustraire:
var offsetLeft = document.querySelector("#picker1").offsetLeft;
On définit enfin la fonction slide, qui prend en paramètre un évènement touch et qui calcule la positon du doigt pour déplacer le curseur:
function slide(touch) {
var pageX = touch.pageX - offsetLeft;
left = pageX - (cursorWidth / 2) + minX;
cursor.style.left = left+"px";
}
Enfin on abonne l’évènement touchmove du slider à cette méthode, et on lui passe le premier élément du tableau touches fourni par l’event. (Un event touch passe un tableau d’élément touch correspondant chacun à un doigt détecté)
slider.addEventListener('touchmove', function (event) {
event.preventDefault();
slide(event.touches[0]);
}, false);
Cela va appeler notre méthode à chaque fois que le navigateur lèvera l’évènement touchmove afin de mettre à jour la position du curseur.
Notre curseur se déplace maintenant en suivant le doigt de l’utilisateur!!! En testant sur mobile et tablette on se rend compte que la réactivité est excellente.
Déplacer le fond avec le curseur
Les Toggle Switch METRO de Windows 8 et Windows Phone donnent une indication sur la valeur en remplissant l’axe avec une couleur différente en suivant le curseur (comme le ferais une barre de progression). Dans notre cas la couleur de fond de vide est le blanc et la couleur de remplissage est le vert.
Pour que la couleur remplisse la partie à gauche du curseur automatiquement, nous allons définir une image de fond à notre slider avec les deux couleurs.
Vous pouvez récupérer l’image de fond ci-dessous:

[TIPS] L’image ne fait que 1 pixel de haut et sera répétée indéfiniment sur l’axe vertical afin de remplir l’espace disponible. (pensez-y pour alléger vos pages) [/TIPS]
Nous ajoutons donc les propriétés de background à notre élément #picker1:
#picker1 {
left: 200px;
width: 150px;
height: 50px;
border: 2px solid black;
position: relative;
background-image: url('images/pickerBack.png');
background-position: 0;
background-clip: content-box;
background-repeat: repeat;
}
L’attribut background-repeat permet de répéter le background sur l’axe vertical (pour la raison citée ci dessus), mais également sur l’axe horizontal. Le fait de déplacer notre background vers la droite en même temps que le curseur fera donc apparaitre la couleur verte de par cette répétition.
Si l’on observe le résultat, rien n’a encore changé car le background est fixe. Il suffit alors d’ajouter à notre fonction slide la modification de la propriété CSS background-position afin de déplacer notre background avec le curseur:
function slide(touch) {
var pageX = touch.pageX - offsetLeft;
left = pageX - (cursorWidth / 2) + minX;
cursor.style.left = left + "px";
var bgleft = left - minX;
slider.style.backgroundPosition = bgleft + "px 0";
}
Ainsi à chaque fois que l’on bouge le curseur, on décale le fond avec.
Définir des point d’ancrage et attirer le curseur
A ce stade, notre curseur peut-être placé n’importe où sur l’axe ce qui convient très bien dans le cas d’un réglage sur un large panel de valeur et ne nécessitant pas une grande précision (luminosité d’une photo, son d’une vidéo…).
Dans cas ou le slider ne propose qu’un nombre limité de valeurs, il est intéressant de définir des points d’ancrages afin de s’assurer que le curseur aille se placer exactement sur une de ces valeurs au moment ou l’utilisateur cessera de le déplacer.
La première chose à faire est de créer les points d’ancrages correspondant à nos steps (comme sur un input range). Pour cela on définit une méthode, prenant en paramètres la valeur minimale, la valeur maximale et la valeur du step qui n’est autre que la valeur qui séparera 2 de nos points et défini ainsi la précision du contrôle.
La méthode va simplement peupler une liste de steps (stepsTab) et associer une valeur à des coordonnées.
function SetSteps(min, max, step) {
var nbsteps = Math.floor(((max - min) / step));
stepWidth = (maxX - minX) / nbsteps;
var stepsTab = [];
for (var i = 0; i < nbsteps; i++) {
stepsTab.push({
posx: minX + i * stepWidth,
value: min + i*step
})
}
stepsTab.push({
posx: maxX,
value: max
})
return stepsTab;
}
Il suffit alors d’appeler une fois la méthode au chargement de notre slider afin de disposer de ces points d’ancrage.
Pour pouvoir facilement associer la position de notre curseur à une valeur, il suffit de définir une fonction, qui prend en paramètre la position du curseur et renvoi le points le plus proche de ce dernier:
function GetClosestStep(posX) {
var halfStep = stepWidth / 2;
for (var i = 0; i < steps.length; i++) {
if (posX<steps\[i\].posx+halfStep) {
return steps\[i\];
}
}
}
Maintenant que l’on est capable de savoir quel est le point le plus proche de notre curseur à partir de sa position, il reste à créer la méthode qui va nous permettre de déplacer le curseur sur la position exacte du point le plus proche quand l’utilisateur décollera son doigt du curseur.
function toggle(step) {
var leftpos = step.posx;
cursor.style.left = leftpos+"px";
slider.style.backgroundPosition = leftpos+"px 0";
pickerValue = step.value;
}
Enfin, pour que le curseur soit effectivement attiré vers le point d’ancrage le plus proche à chaque fois que l’utilisateur fini un déplacement, on abonne l’évènement ontouchend du slider à la méthode toogle:
slider.addEventListener('touchend', function (event) {
event.preventDefault();
toggle(GetClosestStep(left));
}, false);
Résultat
Vous pouvez tester le résultat avec votre smartphone/tablette en cliquant sur ce lien ou en scan le QR Code suivant:

Remarque: Le résultat a été testé sur Ipad/Iphone, Android (2.3+) et Tablette Windows 8. Cela fonctionne bien partout sauf sur Windows Phone qui ne gère pas les évènements Touch dans Internet Explorer. Cela est du à la version 9 d’Internet Explorer incluse dans Mango, et cela sera heureusement corrigé dans la version 8 de Windows Phone prévue pour la rentrée.
Optimisation
Le code décris dans cet article a pour but de faire découvrir comment se servir des évènement JavaScript pour créer son propre composant. De nombreuses optimisations sont à envisager:
- La gestion des events onmousemove, onmouseup, afin de permettre une utilisation à la souris (et sur les devices ne gérant pas le touch: Les évènements touch ne sont pas gérer sur internet explorer de Windows Phone 7.5 Mango. Cela va changer avec l’arrivée de la version8)
- La gestion de la valeur sélectionnée avec une copie dans un champs de formulaire afin de pouvoir exploiter la valeur
- Une orientation verticale du contrôle (type niveau de son)
Il y en a surement pleins d’autres encore, il ne tiens qu’à vous de les trouver et de les implémenter!
Conclusion
Pour:
- Compatible avec tous les navigateurs qui acceptent le JavaScript
- Aspect et fonctionnement du contrôle entièrement personnalisable (et adaptable en CSS pour chaque type de device)
- Bonne expérience sur tactile
Contre ?:
- Il ne s’agit pas d’un contrôle standard: d’ou l’utilisation d’un champ input pour contenir sa valeur
Au final cela pourra s’avérer utile pour définir un contrôle mieux adapté au type de donnée à saisir que les composants standards et d’améliorer l’expérience utilisateurs sur les devices tactiles. Le seul point “négatif” que j’y vois est justement le fait que l’on utilise pas un contrôle standard qui pourrai être potentiellement plus dur à traiter par la suite. Mais voilà, ceci est facilement comblé par l‘utilisation d’un input contenant la valeur du contrôle.
J’espère que ces deux articles sur l’adaptation des interfaces web à destination des devices touch vous aura permis de découvrir, si ce n’est des techniques, un aspect important dans la conception de vos interfaces d’aujourd’hui, qui prend de plus en plus d’importance avec l’explosion des tablettes et smartphones.
Pour tester les 3 composants touch créés tout au long de ces deux billets, rendez-vous sur la page regroupant les 3 :

Ce post vous a plu ? Ajoutez le dans vos favoris pour ne pas perdre de temps à le retrouver le jour où vous en aurez besoin :