Soyons francs : les exercices que nous avons fait précédemment n'étaient quand même pas très utiles sans une réelle interaction avec l'utilisateur. Les alert(), confirm() et prompt() c'est sympa un moment, mais on en a vite fait le tour ! Il est donc temps de passer à quelque chose de plus intéressant : un système de drag & drop ! Enfin… une version très simple !
Il s'agit ici d'un mini-TP, ce qui veut dire qu'il n'est pas très long à réaliser, mais il demande quand même un peu de réflexion. Ce TP vous fera utiliser les événements et les manipulations CSS. Tant que nous y sommes, si vous vous sentez motivés vous pouvez essayer de faire en sorte que votre script fonctionne aussi sous Internet Explorer (si vous avez bien suivi le cours, vous n'aurez aucun mal).
Tout d'abord, qu'est-ce que le drag & drop ? Il s'agit d'un système permettant le déplacement d'éléments par un simple déplacement de souris. Pour faire simple, c'est comme lorsque vous avez un fichier dans un dossier et que vous le déplacez dans un autre dossier en le faisant glisser avec votre souris.
Et je suis vraiment capable de faire ça ?
Bien évidemment ! Bon, il faut avoir suivi attentivement le cours et se démener un peu, mais c'est parfaitement possible, vous en êtes capables !
Avant de se lancer dans le code, listons les étapes de fonctionnement d'un système de drag & drop :
Alors ? Ça n'a pas l'air si tordu que ça, n'est-ce pas ?
Maintenant que vous savez à peu près ce qu'il faut faire, nous allons vous fournir le code HTML de base ainsi que le CSS, vous éviterez ainsi de vous embêter à faire cela vous-mêmes :
<div class="draggableBox">1</div> <div class="draggableBox">2</div> <div class="draggableBox">3</div> |
|
.draggableBox { position: absolute; width: 80px; height: 60px; padding-top: 10px; text-align: center; font-size: 40px; background-color: #222; color: #CCC; cursor: move; } |
Juste deux dernières petites choses. Il serait bien :
Sur ce, bon courage !
Vous avez terminé l'exercice ? Nous espérons que vous l'avez réussi, mais si ce n'est pas le cas ce n'est pas grave ! Regardez attentivement la correction, et tout devrait être plus clair.
(function() { // On utilise une IEF pour ne pas polluer l'espace global
var storage = {}; // Contient l'objet du div en cours de déplacement
function addEvent(element, event, func) { // Une fonction pour gérer les événements sous tous les navigateurs if (element.attachEvent) { element.attachEvent('on' + event, func); } else { element.addEventListener(event, func, true); } }
function init() { // La fonction d'initialisation var elements = document.getElementsByTagName('div'), elementsLength = elements.length;
for (var i = 0 ; i < elementsLength ; i++) { if (elements[i].className === 'draggableBox') {
addEvent(elements[i], 'mousedown', function(e) { // Initialise le drag & drop var s = storage; s.target = e.target || event.srcElement; s.offsetX = e.clientX - s.target.offsetLeft; s.offsetY = e.clientY - s.target.offsetTop; });
addEvent(elements[i], 'mouseup', function() { // Termine le drag & drop storage = {}; }); } }
addEvent(document, 'mousemove', function(e) { // Permet le suivi du drag & drop var target = storage.target;
if (target) { target.style.top = e.clientY - storage.offsetY + 'px'; target.style.left = e.clientX - storage.offsetX + 'px'; } }); }
init(); // On initialise le code avec notre fonction toute prête
})(); |
Pour la fonction addEvent(), pas besoin de vous expliquer comment elle fonctionne, vous avez déjà vu ça au chapitre sur les événements, vous pouvez le relire au besoin. Maintenant, votre seul problème dans ce code doit être la fonction init(). Quant à la variable storage, il ne s'agit que d'un espace de stockage dont nous allons vous expliquer le fonctionnement au cours de l'étude de la fonction init().
Commençons !
Notre fonction init() commence par le code suivant :
var elements = document.getElementsByTagName('div'), elementsLength = elements.length;
for (var i = 0 ; i < elementsLength ; i++) { if (elements[i].className == 'draggableBox') {
// Code...
} } |
Dans ce code, nous avons volontairement caché les codes d'ajout d'événements, car ce qui nous intéresse c'est cette boucle et la condition. Cette boucle couplée à la méthode getElementsByTagName(), vous l'avez déjà vue dans le chapitre sur la manipulation du code HTML, elle permet de parcourir tous les éléments HTML d'un type donné. Dans notre cas, nous parcourons tous les éléments <div>.
À chaque élément <div> trouvé, on vérifie que sa classe correspond bien à .draggableBox. Pourquoi faire cela ? Parce que si vous ajoutez d'autres éléments <div> ils ne seront pas pris en compte sauf si vous leur attribuez la bonne classe, ainsi vous pouvez utiliser des <div> sans qu'ils soient forcément déplaçables.
Dans notre boucle qui parcourt le code HTML, nous avons deux ajouts d'événements que voici :
addEvent(elements[i], 'mousedown', function(e) { // Initialise le drag & drop var s = storage; s['target'] = e.target || event.srcElement; s['offsetX'] = e.clientX - s['target'].offsetLeft; s['offsetY'] = e.clientY - s['target'].offsetTop; });
addEvent(elements[i], 'mouseup', function() { // Termine le drag & drop storage = {}; }); |
Comme vous pouvez le voir, ces deux événements ne font qu'accéder à la variable storage. À quoi nous sert donc cette variable ? Il s'agit tout simplement d'un objet qui nous sert d'espace de stockage, il permet de mémoriser l'élément actuellement en cours de déplacement ainsi que la position du curseur par rapport à notre élément (nous reviendrons sur ce dernier point plus tard).
Bref, dans notre événement mousedown (qui initialise le drag & drop), nous ajoutons l'événement ciblé dans la propriété storage['target'] puis les positions du curseur par rapport à notre élément dansstorage['offsetX'] et storage['offsetY'].
En ce qui concerne notre événement mouseup (qui termine le drag & drop), on attribue juste un objet vide à notre variable storage, comme ça tout est vidé !
Jusqu'à présent, notre code ne fait qu'enregistrer dans la variable storage l'élément ciblé pour notre drag & drop. Cependant, notre but c'est de faire bouger cet élément. Voilà pourquoi notre événement mousemove intervient !
addEvent(document, 'mousemove', function(e) { // Permet le suivi du drag & drop var target = storage['target'];
if (target) { // Si « target » n'existe pas alors la condition va renvoyer « undefined », ce qui n'exécutera pas les deux lignes suivantes : target.style.top = e.clientY - storage['offsetY'] + 'px'; target.style.left = e.clientX - storage['offsetX'] + 'px'; } }); |
Pourquoi notre événement est-il appliqué à l'élément document ?
Réfléchissons ! Si nous appliquons cet événement à l'élément ciblé, que va-t-il se passer ? Dès que l'on bougera la souris, l'événement se déclenchera et tout se passera comme on le souhaite, mais si je me mets à bouger la souris trop rapidement, le curseur va alors sortir de notre élément avant que celui-ci n'ait eu le temps de se déplacer, ce qui fait que l'événement ne se déclenchera plus tant que l'on ne replacera pas notre curseur sur l'élément. La probabilité pour que cela se produise est plus élevée que l'on ne le pense, autant prendre toutes les précautions nécessaires.
Un autre problème peut aussi surgir : dans notre code actuel, nous ne gérons pas le style CSS z-index, ce qui fait que lorsqu'on déplace le premier élément et que l'on place notre curseur sur un des deux autres éléments, le premier élément se retrouve alors en dessous d'eux. En quoi est-ce un problème ? Eh bien si on a appliqué le mousemove sur notre élément au lieu du document alors cet événement ne se déclenchera pas vu que l'on bouge notre curseur sur un des deux autres éléments et non pas sur notre élément en cours de déplacement.
La solution est donc de mettre l'événement mousemove sur notre document. Vu que cet événement se propage aux enfants, nous sommes sûrs qu'il se déclenchera à n'importe quel déplacement du curseur sur la page.
Le reste du code n'est pas bien sorcier :
Alors revenons sur un point important du précédent code : il nous a fallu enregistrer la position du curseur par rapport au coin supérieur gauche de notre élément dès l'initialisation du drag & drop :
La valeur X désigne le décalage (en pixels) entre les bordures gauche de l'élément et du curseur, la valeur Y fait de même entre les bordures supérieures
Pourquoi ? Car si vous ne le faites pas, à chaque fois que vous déplacerez votre élément, celui-ci placera son bord supérieur gauche sous votre curseur et ce n'est clairement pas ce que l'on souhaite.
Essayez donc par vous-mêmes pour vérifier !
Comme vous l'avez peut-être constaté, il est possible que l'utilisateur sélectionne le texte contenu dans vos éléments déplaçables, cela est un peu aléatoire. Heureusement, il est possible de résoudre simplement ce problème avec quelques propriétés CSS appliquées aux éléments déplaçables :
user-select: none; /* L'utilisateur ne pourra plus sélectionner le texte de l'élément qui possède cette propriété CSS */ -moz-user-select: none; -khtml-user-select: none; -webkit-user-select: none; |
Et voilà pour ce mini-TP, nous espérons qu'il vous a plu !
Créé avec HelpNDoc Personal Edition: Générateur facile de livres électroniques et documentation