Votre premier script interactif !

Parent Previous Next



Votre premier script interactif !



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).


Présentation de l'exercice


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 :

  1. Que vous utilisiez une IEF dans laquelle vous allez placer toutes les fonctions et variables nécessaires au bon fonctionnement de votre code, ce sera bien plus propre. Ainsi, votre script n'ira pas polluer l'espace global avec ses propres variables et fonctions ;
  2. Que votre code ne s'applique pas à tous les <div> existants mais uniquement à ceux qui possèdent la classe .draggableBox.


Sur ce, bon courage !


Correction


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 !


L'exploration du code HTML


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.


L'ajout des événements mousedown et mouseup


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é !


La gestion du déplacement de notre élément


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 !


Empêcher la sélection du contenu des éléments déplaçables


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 !


En résumé



Créé avec HelpNDoc Personal Edition: Générateur facile de livres électroniques et documentation

Site à deux balles