• 12 heures
  • Moyenne

Ce cours est visible gratuitement en ligne.

course.header.alt.is_video

course.header.alt.is_certifying

J'ai tout compris !

Mis à jour le 20/11/2019

Ajoutez une fonctionnalité de glisser-déposer

Connectez-vous ou inscrivez-vous gratuitement pour bénéficier de toutes les fonctionnalités de ce cours !

Principe du glisser-déposer

Dans cette partie, nous allons voir comment introduire une fonctionnalité de glisser-déposer.

Nous allons voir que cette fonctionnalité s'appuie sur plusieurs gestionnaires d'événement, concept que nous avons utilisé par deux fois déjà.

Grâce à plusieurs exercices de difficulté croissante, vous allez très vite comprendre comment mettre en œuvre le glisser-déposer.

Examinons quel est le principe d'un glisser-déposer et listons les éléments qui le rendent possible.

Dans une fonctionnalité de glisser-déposer, nous trouvons deux éléments :

  • d'abord l'élément sur lequel on clique et qui va faire l'objet d'un déplacement : ce peut être par exemple une image, ou de façon plus large, un élément de la page web ;

  • ensuite, la cible qui va être survolée par l'élément que l'on déplace et qui va capturer l'objet dès que la souris sera relâchée ; cette cible peut être visible ou non, et plus ou moins grande.

Elément vers cible
Principe du glisser-déposer

Du point de vue du code HTML de la page, le code de l'élément déplacé — ici une image — est tout simplement copié à l'intérieur du conteneur cible, ici un élément div.

Plusieurs choses doivent être installées pour rendre possible une fonctionnalité de glisser-déposer.

Il faut d'abord d'abord rendre possible le fait qu'un élément soit déplaçable : ceci se traduit par un attribut  draggable  à "true" (certaines versions récentes des navigateurs — Safari, Firefox, Chrome — autorisent par défaut un comportement de glisser-déposer) comme ci-dessous :

<img id=”image1” draggable=”true”>

Ensuite, on s'appuie sur plusieurs événements, quatre au total.

On se rappelle que la détection d'un événement implique trois choses :

  • un élément auquel cet événement est attaché ;

  • un gestionnaire ;

  • une fonction qui sera exécutée par ce gestionnaire.

Ainsi, concernant l'élément à déplacer, il faut détecter :

  • quand il commence à être déplacé : ceci se fait grâce à un gestionnaire d'événement qui va détecter et réagir à l'événement  ondragstar ;

  • quand l'élément a fini d'être déplacé : ceci se fait grâce à un gestionnaire d'événement qui va détecter et réagir à l'événement  ondragend  (cet événement est optionnel).

Concernant la cible, il faut détecter :

  • quand elle est survolée par l'élément : ceci se fait grâce à un gestionnaire d'événement ondragover ;

  • quand un élément est relâché sur cette cible : ceci se fait grâce à un gestionnaire d'événement ondrop.

Prenons-les dans l'ordre :

  • la fonction associée à l'événement  ondragstart  est l'occasion de mémoriser, à l'intérieur de l'objet JavaScript événement qui vient d'être créé, l'identifiant de l'élément déplacé.

Ceci se fait à travers la méthode dataTransfert.setData().

Pour une explication sur l'argument "Text" de cette fonction, on pourra consulter la ressource suivante.

De manière optionnelle, la fonction associée à l'événement  ondragstart  peut être aussi l'occasion de changer l'apparence de l'élément déplacé : un effet de transparence, par exemple ;

  • la fonction associée à l'événement ondragend sera pour nous l'occasion de changer à nouveau cette apparence. Ainsi, à la fin du glisser-déposer, l'élément retrouvera par exemple l'aspect qu'il avait avant d'être déplacé, ou encore un autre aspect ;

  • la fonction associée à l'événement ondragover permet de spécifier où l'élément est autorisé à être déposé. Ici, la cible. Dans HTML, le fonctionnement par défaut ne permet pas à un élément d’être déposé dans un autre. Ainsi, la tâche essentielle de la fonction associée à l'événement ondragover est d'empêcher ce fonctionnement par défaut, on va le voir à travers la méthode event.preventDefault() ;

  • enfin, la fonction associée à l'événement ondrop réalise plusieurs choses :

    • elle récupère l'identifiant de l'élément déplacé, 

    • et grâce à cet identifiant, transfère le code HTML de l'élément déplacé à l'intérieur du conteneur de la cible.

 Voici un schéma résumant ce qui vient d’être vu :

Schéma récapitulatif
Schéma récapitulatif

La fonctionnalité de glisser-déposer doit être testée sur plusieurs navigateurs, notamment IE. À cet égard, une solution robuste multiplateforme peut être mise en place grâce à des frameworks multiplateformes comme jQuery ou jQuery UI, dont nous dirons un mot plus loin.

Les exemples qui vont suivre ont été testés sur Chrome 73, Safari 12 et Firefox 66.

Voyons sur plusieurs exemples progressifs comment installer le glisser-déposer.

Mise en œuvre

Dans la partie précédente, nous avons vu le principe général d'un glisser-déposer et listé les éléments qui le rendent possible.

Voyons sur plusieurs exemples progressifs comment installer le glisser-déposer. On cherche à déplacer une image à laquelle est attaché un gestionnaire pour le seul événement  ondragstart.

Ici, le gestionnaire est écrit à l'intérieur de la balise. Il lance une fonction ondragstart qui va ici tout simplement écrire une information dans le conteneur appelé info, et qui est vide au départ.

On note qu'aucun événement n'est pour l'instant associé au relâché de la souris ; l'élément revient donc à sa place.

<p>Détection du déplacement. (Déplacer l'image)</p>
<img id="image1" src="images/HTML5-logo.png" draggable="true"
ondragstart="fonction_ondragstart(event)" width="100" height="100">
<p id="info"></p>
<script>
function fonction_ondragstart(ev){
document.getElementById('info').innerHTML = "déplacement de "+ev.target.id;
}
</script>

Deuxième exemple ci-dessous : on cherche toujours à détecter le seul événement  dragstart,  mais le gestionnaire de cet événement est à présent défini grâce à JavaScript.

<p>Détection du déplacement. (Déplacer l'image)</p>
<p id="info"></p>
<img id="image1" src="images/HTML5-logo.png" draggable="true" width="100" height="100">
<script>
document.getElementById('image1').addEventListener('dragstart',fonction_dragstart,false);
function fonction_dragstart(ev){
document.getElementById('info').innerHTML = "déplacement de "+ev.target.id;
}
</script>

Je vous propose de tester cet exemple.

Voici un troisième exemple :

<p id="info"></p>
<img id="image1" src="images/HTML5-logo.png" draggable="true" width="100" height="100">
<script>
var image = document.getElementById('image1')
image.addEventListener('dragstart',fonction_dragstart,false);
image.addEventListener('dragend',fonction_dragend,false);
function fonction_dragstart(ev){
document.getElementById('info').innerHTML = "déplacement de "+ev.target.id;
/* on change la transparence de l'élément et on ajoute un cadre */
this.style.opacity=0.2;
this.style.borderStyle='dashed';
}
function fonction_dragend(ev){
ev.preventDefault();
document.getElementById('info').innerHTML = "fin de déplacement de "+ev.target.id;
/* on remet les valeurs initiales de transparence et on supprime le cadre */
this.style.opacity=1;
this.style.borderStyle='none';
}
</script>

Ici, on détecte toujours l'événement  dragstart  qui nous permet par exemple de changer la transparence de l'objet déplacé, et de lui ajouter un cadre.

function fonction_dragstart(ev){
document.getElementById('info').innerHTML = "déplacement de "+ev.target.id;
/* on change la transparence de l'élément et on ajoute un cadre */
this.style.opacity=0.2;
this.style.borderStyle='dashed';
}

On détecte également l'événement  dragend  qui provoque ici un retour à la normale de la transparence de l'élément, et la disparition du cadre.

function fonction_dragend(ev){
ev.preventDefault();
document.getElementById('info').innerHTML = "fin de déplacement de "+ev.target.id;
/* on remet les valeurs initiales de transparence et on supprime le cadre */
this.style.opacity=1;
this.style.borderStyle='none';
}

Aucun événement n'ayant été pour l'instant associé au relâcher de la souris, l'élément revient à sa place.

Je vous propose de tester cet exemple.

Voyons maintenant comment mettre en place la partie "déposer" du glisser-déposer.

La première chose à faire est de modifier la fonction associée à l'événement  dragstart  pour sauvegarder au sein de l'objet JavaScript événement l'identité de l'élément qui est déplacé. Ceci se fait grâce à la méthode  dataTransfer

<p>Détection du déplacement et de sa fin. (Déplacer l'image)</p>
<p id="info"></p>
<img id="image1" src="images/HTML5-logo.png" draggable="true" width="100" height="100">
<div id="cible">Ce conteneur acceptera l'élément déplacé.</div>
<style type="text/css">
#cible {width:200px;height:200px;border:1px solid #aaaaaa;}
</style>
<script>
var image = document.getElementById('image1')
image.addEventListener('dragstart',fonction_dragstart,false);
image.addEventListener('dragend',fonction_dragend,false);
function fonction_dragend(ev){
ev.preventDefault();
var id = ev.target.id;
document.getElementById('info').innerHTML="fin de déplacement de "+id;
/* remettre les valeurs initiales de transparence, supprimer le cadre */
this.style.opacity=1;
this.style.borderStyle='none';
}
function fonction_dragstart(ev){
var id = ev.target.id;
document.getElementById('info').innerHTML="déplacement de "+id;
/* changer transparence de l'élément, ajouter un cadre */
this.style.opacity=0.2;
this.style.borderStyle='dashed';
ev.dataTransfer.setData("Text",ev.target.id);
}
var cible = document.getElementById('cible')
cible.addEventListener('dragover',fonction_dragover,false);
cible.addEventListener('drop',fonction_drop,false);
function fonction_dragover(ev){
/* empêcher le fonctionnement par défaut */
ev.preventDefault();
}
function fonction_drop(ev){
ev.preventDefault();
/* récupération de l'identifiant de l'élément déplacé */
var data=ev.dataTransfer.getData("Text");
ev.target.appendChild(document.getElementById(data));
}
</script>

Deuxièmement, il faut associer à la cible (l'endroit où l'image sera déposée) un gestionnaire pour les événements  ondragover  et  ondrop.

La fonction associée à l'événement  ondragover  permet donc de :

  • spécifier où l'élément est autorisé à être déposé : ici, la cible ;

  • empêcher le fonctionnement par défaut.

D’autre part, la fonction associée à l'événement  ondrop  permet :

  • de récupérer l'identifiant de l'élément déplacé ;

  • grâce à cet identifiant, de transférer l'élément déplacé à l'intérieur du conteneur de la cible.

Je vous propose de tester cet exemple désormais complet.

Voici pour ces quelques exemples de mise en œuvre progressive. Dans la prochaine section, nous allons voir comment mettre en oeuvre le glisser-déposer avec jQuery.

jQuery et le glisser-déposer

Voyons à présent comment l'exercice précédent pourrait être réalisé avec jQuery.

Le nouveau code avec jQuery est le suivant :

<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Le drag and drop avec HTML5 et jQuery</title>
<script type="text/javascript" src="jquery.js"></script>
<script type="text/javascript">
/* ajouter propriété pour drop et transfert de données */
$.event.props.push('dataTransfer');
$(document).ready(function() {
$('#image1').on({
dragstart: function(e) {
var id = $(this).attr('id');
$('#info').html("déplacement de "+id);
$(this).css('opacity', '0.5');
/* on garde l'identifiant en mémoire */
e.dataTransfer.setData('text/html', id);
},
dragend: function() {
var id = $(this).attr('id');
$('#info').html("fin du déplacement de "+id);
$(this).css('opacity', '1');
}
});
$('#cible').on({
dragover: function(e) {
e.preventDefault();
},
drop: function(e) {
e.preventDefault();
var data = e.dataTransfer.getData('text/html');
$(this).append($("#"+data));
}
});
});
</script>
</head>
<body>
<p>Détection du déplacement et de sa fin avec jQuery. (Déplacer l'image)</p>
<p id="info"></p>
<div id="image1"><img src="images/HTML5-logo.png" draggable="true" width="100" height="100"></div>
<div id="cible">Ce conteneur acceptera l'élément déplacé.</div>
<style type="text/css">
#cible {width:200px;height:200px;border:1px solid #aaaaaa;}
</style>
</body>
</html>

Dans ce code, avec la façon plus condensée que permet jQuery, on va retrouver les différentes fonctionnalités mises en évidence précédemment.

Que fait cette fonction ?

En premier lieu, elle permet d'associer à l'élément dont l'identifiant est image1 un gestionnaire d'événements :

$('#image1').on({
dragstart: function(e) {
var id = $(this).attr('id');
$('#info').html("déplacement de "+id);
$(this).css('opacity', '0.5');
/* on garde l'identifiant en mémoire */
e.dataTransfer.setData('text/plain', id);
},
dragend: function() {
var id = $(this).attr('id');
$('#info').html("fin du déplacement de "+id);
$(this).css('opacity', '1');
}
});

À chacun des deux événements  dragstart  et  dragend, on associe une fonction $(this) faisant référence à l'élément sur lequel se produit l'événement.

On voit que l'on exécute les mêmes choses que dans le code précédent.

La seconde chose que fait cette fonction, c'est d'associer un gestionnaire d'événement à l'élément dont l'identifiant est cible :

$('#cible').on({
dragover: function(e) {
e.preventDefault();
},
drop: function(e) {
e.preventDefault();
var data = e.dataTransfer.getData('text/plain);
e.preventDefault();
$(this).append($("#"+data));
}
});

À chacun des deux événements  dragover  et  drop,  on associe une fonction. À nouveau, on voit que l'on exécute les mêmes choses que dans le code précédent.

Une petite remarque  : pour des versions récentes de jQuery (> 3), l’instruction  $.event.props.push('dataTransfer');  ne fonctionne plus. Il faut passer par la méthode  originalEvent : e.originalEvent.dataTransfer.setData('text/html', id);

Je vous propose de tester cet exemple. Bien entendu, il vous faudra importer jQuery au sein de la page HTML.

Exercice

Voici l'exercice que je vous propose. Il s'agit de créer un petit jeu qui va reproduire le mécanisme ci-dessous :

Il s’agit de ranger une série de billes dans deux boîtes. Par exemple les paires dans l'une et les impaires dans l'autre. Avec un test quand toutes les billes sont rangées.

Le code correspondant est le suivant :

<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Le drag and drop avec HTML5 et jQuery</title>
<script type="text/javascript" src="jquery3.js"></script>
<script type="text/javascript">
$(document).ready(function() {
// ajouter propriété pour drop et transfert de données
//$.event.props.push('dataTransfer');// Attention : ne fonctionne plus si jQuery > 3 : passer par originalEvent (cf ci-dessous)
// dessin des billes
for(var i=1; i<=5; i++){
var monCanvas = $('#bille'+i)[0];
var context = monCanvas.getContext('2d');
context.clearRect(0,0, monCanvas.width,monCanvas.height);
dessineBalle(context,i);
}
// gestionnaires des billes
for(var i=1; i<=5; i++){
// attention : var canvas = $('#myCanvas') renvoie un objet jQuery par un élément du DOM
$('#bille'+i).on({
dragstart: function(e) {
var id = $(this).attr('id');
$('#info').html("déplacement de "+id);
$(this).css('opacity', '0.5');
// on garde l'identifiant en mémoire
//e.dataTransfer.setData('text/html', id);
e.originalEvent.dataTransfer.setData('text/html', id);
},
dragend: function() {
var id = $(this).attr('id');
$('#info').html("fin du déplacement de "+id);
$(this).css('opacity', '1');
}
});
}
// gestionnaires des boîtes
for(var i=1; i<=2; i++){
$('#boite'+i).on({
dragover: function(e) {
e.preventDefault();
},
drop: function(e) {
//var data = e.dataTransfer.getData('text/html');
var data = e.originalEvent.dataTransfer.getData('text/html');
$(this).append($("#"+data));
}
});
}
// dessin d'une balle : x départ, vitesse / y, rayon
function dessineBalle(ctx,numero){
// dessin
ctx.translate(20,20);
ctx.beginPath();
ctx.arc(0, 0, 18, 0, 2 * Math.PI, false);
ctx.fillStyle = 'green';
ctx.fill();
ctx.lineWidth = 1;
ctx.strokeStyle = '#003300';
ctx.stroke();
ctx.fillStyle = '#fff';
ctx.font = 'Italic 18px Sans-Serif';
ctx.fillText (i, -7, 4);
}
});
</script>
<style>
body {
background-color:#BFE1D8;
}
#boite1, #boite2 {
background-color:white;
vertical-align:top;
}
</style>
</head>
<body>
<h2>Les billes dans les boîtes</h2>
<!-- information -->
<p id="info"></p>
<!-- les boîtes -->
<div id="boite1"></div>
<div id="boite2"></div>
<!-- les billes -->
<canvas id="bille1" width="40" height="40" draggable="true" >
Texte pour les navigateurs qui ne supportent pas canvas
</canvas>
<canvas id="bille2" width="40" height="40" draggable="true" >
Texte pour les navigateurs qui ne supportent pas canvas
</canvas>
<canvas id="bille3" width="40" height="40" draggable="true" >
Texte pour les navigateurs qui ne supportent pas canvas
</canvas>
<canvas id="bille4" width="40" height="40" draggable="true" >
Texte pour les navigateurs qui ne supportent pas canvas
</canvas>
<canvas id="bille5" width="40" height="40" draggable="true" >
Texte pour les navigateurs qui ne supportent pas canvas
</canvas>
<style type="text/css">
#boite1, #boite2 {
display:inline-block;
width:200px;height:200px;border:1px solid #aaaaaa;
}
</style>
</body>
</html>

Comme indiqué sur ce code HTML, je vous propose d'utiliser un canvas pour chaque bille.

À vous de jouer ! Bon travail !

En résumé

Nous arrivons à la fin de la troisième partie de ce cours. Vous avez découvert certaines fonctionnalités d'HTML5 (dessin, animation, interactivité, glisser-déposer) qui vous permettent déjà — j'en suis sûr — d'imaginer de nombreux animations, jeux ou interfaces. À présent, je vous donne rendez-vous dans la quatrième partie de ce cours qui vous propose une méthodologie de conception et de développement d'interfaces ou d’animations en HTML5.

Exemple de certificat de réussite
Exemple de certificat de réussite