Tutoriel: Drag and Drop JQuery, exemple avec une liste des tâches

Publié par Gui dans jQuery, Tutoriels

Facile

jQuery, css

Après un tutoriel pour créer un diaporama simple avec JQuery, qui d’après vos commentaires vous a assez intéressé, voici un nouveau tutoriel basé sur le Drag and Drop.

L’exemple que nous allons voir est une liste des tâche simple, que je vous présente sous la forme d’une liste de courses.

Le principe est simple, on ajoute des éléments, on les renomme, on modifie leur ordre et on les supprime.

On va voir dans cet article comment utiliser de manière très simple la bibliothèque d’animations et d’interactions JQuery UI.

Celle ci permet d’avoir « nativement » des interfaces pour pouvoir déplacer des éléments et contrôler où on les dépose (drag & drop) ainsi que pour les trier.

Ces interfaces sont les suivantes :

DEMO DOWNLOAD

Mise en page et intégration

Partie HTML

Nous allons créer une page xHTML simple, composée des éléments suivants :

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="fr" lang="fr">
	<head>
		<title>Shopping List JQuery</title>
		<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />

		<!-- Cascading Style Sheets -->
		<link href="page.css" rel="stylesheet" type="text/css" />
		<link href="style.css" rel="stylesheet" type="text/css" />

		<!-- Javascript -->
		<script type="text/javascript" src="js/jquery-1.4.2.min.js"></script>
		<script type="text/javascript" src="js/jquery-ui-1.8.custom.min.js"></script>
		<script type="text/javascript" src="js/jquery.shoppingList.js"></script>
		<script type="text/javascript" src="js/script.js"></script>
	</head>

	<body>

		<h1>My Shopping List</h1>

		<div class="shoppingList">
			<ul>
				<li>Milk</li>
				<li>Red wine</li>
				<li>Sugar</li>
				<li>Ice Cream</li>
			</ul>
		</div>

	</body>

</html>

Nous avons besoin dans ce tutoriel de deux fichiers CSS :

  1. Pour la mise en page de la page elle même
  2. Pour la mise en page de la liste des courses (shopping list)

Ainsi que de quatre fichiers Javascripts :

  1. La librairie JQuery 1.4.2
  2. Notre librairie JQuery UI personnalisée (avec nos trois composants)
  3. Notre plugin shoppingList
  4. Un fichier de script d’initialisation

Partie CSS

En ce qui concerne la partie CSS :

.shoppingList{
	margin:0;
	background:url(img/notebook.gif) no-repeat;
	font-style:normal;
	padding:60px 0 0 40px;
	width:460px;
	height:448px;
	position:relative;
}

.shoppingList ul{
	margin:0;
	padding:0;
	list-style:none;
	position:relative;
	padding-top:17px;
}

.shoppingList li{
	font-size:1.3em;
	position:relative;
	text-shadow:none;
	clear:both;
}

.shoppingList li .count{
	float:right;
	padding-right:20px;
}

.shoppingList li .item{
	cursor:pointer;
}

.shoppingList li .check{
	width:16px;
	height:25px;
	line-height:25px;
	float:left;
	margin-right:50px;
	cursor:pointer;
}

.shoppingList li .unchecked{
	background:url(img/item_unchecked.png) center center no-repeat;
}

.shoppingList li .checked{
	background:url(img/tick_circle.png) center center no-repeat;
}

.shoppingList li.bought .item{
	text-decoration:line-through;
	color:#888;
	font-style:italic
}

.shoppingList .trash{
	position:absolute;
	bottom:70px;
	height:60px;
	background:#ddd;
	-moz-box-shadow:0 0 0.5em #666;
	width:330px;
	opacity:0.6;
	filter : alpha(opacity=60);
    -moz-opacity : 0.6;
	line-height:60px;
	font-style:italic;
	text-shadow:0 1px 0 #ccc;
	font-size:1.5em;
	background:#eee url(img/trash.png) 2.5em center no-repeat;
	padding-left:100px
}

.shoppingList .hover{
	opacity:0.7;
	filter : alpha(opacity=70);
    -moz-opacity : 0.7;
	color:#fff;
	background-color:#cd6a76
}

.shoppingList .deleted{
	background-color:#70cc57;
	opacity:0.7;
	filter : alpha(opacity=70);
    -moz-opacity : 0.7;
	color:#fff;
}

.shoppingList .add{
	position:absolute;
	bottom:20px;
}

.shoppingList .add .addValue{
	width:350px;
	margin-right:5px;
	background-color:#f5f2d5;
	-moz-box-shadow:0 0 0.5em #666;
	padding:5px;
	border:1px solid #ccc;
	font-family:Georgia,"Times New Roman","Bitstream Charter",Times,serif;
	color:#093E56;
	font-size:1.3em;
}

.shoppingList .add .addBtn{
	background-color:#093E56;
	-moz-box-shadow:0 0 0.5em #666;
	padding:2px;
	border:1px solid #ccc;
	color:#fff;
	-moz-border-radius:0.2em;
	padding:5px 8px;
	*padding:0;
	cursor:pointer;
	font-size:1.3em;
	font-family:Georgia,"Times New Roman","Bitstream Charter",Times,serif;
}

/*** DRAG ***/

.shoppingList li.ui-sortable-helper{
	cursor:-moz-grabbing;
}

.shoppingList li.ui-sortable-helper .check, .shoppingList li.ui-sortable-helper .count{
	visibility:hidden
}

A ce stade, nous devrions avoir une page qui ressemble à ça :

Rentrons maintenant dans le coeur du sujet, la partie Javascript.

Développement du plugin

Pour plus d’informations sur comment créer un plugin en JQuery, je vous invite à vous référer au tutoriel de création de diaporama.

Voici donc notre script de base pour notre shopping list :

(function($){
	$.fn.shoppingList = function(options) {

		// Options par defaut
		var defaults = {};

		var options = $.extend(defaults, options);

		this.each(function(){

			var obj = $(this);

			// Traitement

		});
		// On continue le chainage JQuery
		return this;
	};
})(jQuery);

Ordonner les éléments

Nous allons maintenant rendre notre liste d’éléments sortable en utilisant la librairie JQuery UI :

// Initialisation du composant "sortable"
$(obj).sortable({
	axis: "y", // Le sortable ne s'applique que sur l'axe vertical
	containment: ".shoppingList", // Le drag ne peut sortir de l'élément qui contient la liste
	handle: ".item", // Le drag ne peut se faire que sur l'élément .item (le texte)
	distance: 10, // Le drag ne commence qu'à partir de 10px de distance de l'élément
	// Evenement appelé lorsque l'élément est relaché
	stop: function(event, ui){
		// Pour chaque item de liste
		$(obj).find("li").each(function(){
			// On actualise sa position
			index = parseInt($(this).index()+1);
			// On la met à jour dans la page
			$(this).find(".count").text(index);
		});
	}
});

Explications :

On applique la méthode Sortable à notre liste <ul> (obj), ce qui rendra chacun de ses éléments fils (<li>) déplaçables (draggable).

Nous pourrons donc les trier selon les paramètres que nous avons définis, à savoir :

  1. Déplacement vertical seulement (axis: « y »)
  2. Déplacement autorisé uniquement au sein de l’élément parent (containment: « .shoppingList »)
  3. Déplacement en cliquant sur le texte (handle: « .item »)

Ensuite, la méthode « stop » va définir les actions à effectuer lorsque l’on arrêtera de déplacer un élément, à savoir simplement actualiser sa position au sein de la liste :

index = parseInt($(this).index()+1);

On récupère sa nouvelle position (méthode index()) que l’on incrémente car celle ci commence de 0 (comme un tableau) (La position est expliquée plus bas dans l’article).

Ajouter / Supprimer des éléments

Nous allons à présent ajouter de quoi rajouter ou supprimer des éléments de notre liste :

// On ajoute l'élément Poubelle à notre liste
$(obj).after("<div class='trash'>Trash</div>");
// On ajoute un petit formulaire pour ajouter des items
$(obj).after("<div class='add'><input class='addValue' /> <input type='button' value='Add' class='addBtn' /></div>");

Nous ajoutons donc simplement deux <div> après notre liste d’éléments.

Ensuite nous allons définir des actions/évènements pour ces <div>.

Action d’ajout

// Bouton ajouter
$(".addBtn").click(function(){
	// Si le texte n'est pas vide
	if($(".addValue").val() != "")
	{
		// On ajoute un nouvel item à notre liste
		$(obj).append('<li>'+$(".addValue").val()+'</li>');
		// On réinitialise le champ de texte pour l'ajout
		$(".addValue").val("");
		// On ajoute les contrôles à notre nouvel item
		addControls($(obj).find("li:last-child"));
	}
})
// On autorise également la validation de la saisie d'un nouvel item par pression de la touche entrée
$(".addValue").live("keyup", function(e) {
	if(e.keyCode == 13) {
		// On lance l'évènement click associé au bouton d'ajout d'item
		$(".addBtn").trigger("click");
	}
});

Au clic sur l’élément « .addBtn » (notre bouton), si le champ de saisie est rempli, nous récupérons sa valeur que nous ajoutons à la liste d’éléments (méthode append()). Nous ajoutons au dernier élément de la liste (donc celui que l’on vient d’ajouter) des contrôles via la méthode addControls(), que je vais détailler plus loin.

Nous ajoutons aussi un évènement « keyup« , qui nous permet d’ajouter un nouvel élément en validant par la touche <entrée>.

Note :

Pour réitérer la même action que lors du clic, nous pouvons déclencher un évènement (en l’occurence un « click ») via la méthode « trigger()« , et ça c’est bien pratique !

Action de suppression

Pour la partie suppression, nous allons être un peu original ! Pour changer de la très classique croix que l’on clique pour supprimer un élément, nous allons ici profiter de notre sortable pour dragger (déplacer) un élément vers la poubelle.

Pour ce faire nous allons donc appliquer à notre <div> Poubelle la méthode droppable().

Le principe étant de déclencher une action (la suppression de l’élément) lorsque l’on relâche un élément au dessus de la poubelle.

$(".trash").droppable({
	// Lorsque l'on passe un élément au dessus de la poubelle
	over: function(event, ui){
		// On ajoute la classe "hover" au div .trash
		$(this).addClass("hover");
		// On cache l'élément déplacé
		ui.draggable.hide();
		// On indique via un petit message si l'on veut bien supprimer cet élément
		$(this).text("Remove "+ui.draggable.find(".item").text());
		// On change le curseur
		$(this).css("cursor", "pointer");
	},
	// Lorsque l'on quitte la poubelle
	out: function(event, ui){
		// On retire la classe "hover" au div .trash
		$(this).removeClass("hover");
		// On réaffiche l'élément déplacé
		ui.draggable.show();
		// On remet le texte par défaut
		$(this).text("Trash");
		// Ainsi que le curseur par défaut
		$(this).css("cursor", "normal");
	},
	// Lorsque l'on relache un élément sur la poubelle
	drop: function(event, ui){
		// On retire la classe "hover" associée au div .trash
		$(this).removeClass("hover");
		// On ajoute la classe "deleted" au div .trash pour signifier que l'élément a bien été supprimé
		$(this).addClass("deleted");
		// On affiche un petit message "Cet élément a été supprimé" en récupérant la valeur textuelle de l'élément relaché
		$(this).text(ui.draggable.find(".item").text()+" removed !");
		// On supprimer l'élément de la page, le setTimeout est un fix pour IE (http://dev.jqueryui.com/ticket/4088)
		setTimeout(function() { ui.draggable.remove(); }, 1);

		// On retourne à l'état originel de la poubelle après 2000 ms soit 2 secondes
		elt = $(this);
		setTimeout(function(){ elt.removeClass("deleted"); elt.text("Trash"); }, 2000);
	}
})

Nous utilisons trois évènements dans cette interface :

  1. over: au survol de la zone de la poubelle pour indiquer que l’on va supprimer cet élément
  2. out: pour revenir à l’état original (quand on quitte la zone de la poubelle)
  3. drop: la méthode qui va nous permettre de définir une action au moment de relâcher un élément

Méthode Over

Hormis le fait de cacher le texte de l’élément et de modifier l’apparence de l’élément poubelle, la principale subtilité de cette méthode est ce bout de code :

$(this).text("Remove "+ui.draggable.find(".item").text());

On récupère ici le texte de l’élément déplacé via le paramètre ui, qui est un object JQuery que l’on peut donc exploiter via la méthode text() et on l’affiche dans le <div> Poubelle.

Méthode Out

Cette méthode permet de revenir à l’état original (enlever les classes, réafficher l’élément en cours de déplacement et réinitialiser le texte du <div> Poubelle)

Méthode Drop

Enfin la méthode Drop est celle qui va nous permettre de déclencher la suppression de notre élément. On va changer la classe du <div> Poubelle pour indiquer que l’élément a été supprimé l’espace de 2 secondes avant de revenir à la normale.

Nous devrions maintenant avoir une interface assez fonctionnelle qui ressemble à cela :

Confirmation visuelle pour la suppression de l'élément Ice Cream

L'élément a bien été supprimé

Ajout de fonctionnalités

Comme dit plus haut, nous allons maintenant enrichir notre liste avec quelques fonctionnalités bien pratiques comme :

  1. La possibilité de renommer un élément
  2. Afficher sa position dans la liste
  3. Ajouter un bouton pour dire que la tâche est accomplie

Voici donc la fonction :

function addControls(elt)
{
	// 1. On ajoute en premier l'élément textuel
	$(elt).html("<span class='item'>"+$(elt).text()+"</item>");
	// Puis l'élément de position
	$(elt).prepend('<span class="count">'+parseInt($(elt).index()+1)+'</span>');
	// Puis l'élément d'action (élément acheté)
	$(elt).prepend('<span class="check unchecked"></span>');

	// Au clic sur cet élément
	$(elt).find(".check").click(function(){
		// On alterne la classe de l'item (le <li>), le CSS associé fera que l'élément sera barré
		$(this).parent().toggleClass("bought");

		// Si cet élément est acheté
		if($(this).parent().hasClass("bought"))
			// On modifie la classe en ajoutant la classe "checked"
			$(this).removeClass("unchecked").addClass("checked");
		// Le cas contraire
		else
			// On modifie la classe en retirant la classe "checked"
			$(this).removeClass("checked").addClass("unchecked");
	})

	// Au double clic sur le texte
	$(elt).find(".item").dblclick(function(){
		// On récupère sa valeur
		txt = $(this).text();
		// On ajoute un champ de saisie avec la valeur
		$(this).html("<input value='"+txt+"' />");
		// On la sélectionne par défaut
		$(this).find("input").select();
	})

	// Lorsque l'on quitte la zone de saisie du texte
	$(elt).find(".item input").live("blur", function(){
		// On récupère la valeur du champ de saisie
		txt = $(this).val();
		// On insère dans le <li> la nouvelle valeur textuelle
		$(this).parent().html(txt);
	})

	// On autorise la même action lorsque l'on valide par la touche entrée
	$(elt).find(".item input").live("keyup", function(e) {
		if(e.keyCode == 13) {
			$(this).trigger("blur");
		}
	});
}

Explications :

Ajout d’éléments HTML

On ajoute le « markup » nécessaire pour nos actions :

  1. On englobe le texte de l’élément avec un <span>
  2. On ajoute juste après un <span> pour afficher la position
  3. Et enfin un dernier <span> pour notre bouton d’action « check« 

Accomplissement d’une tâche

Au clic sur le <span> « check« , on alterne la classe du <li> grâce à la méthode toggleClass().

Ensuite, en fonction de la tâche est accomplie (i.e si la classe « bought » est présente), on définit la bonne classe pour l’élément (« checked » correspond à une tâche finie (barrée), et « unchecked » l’inverse).

Achèvement d'une tâche

Renommer l’élément

Lorsque l’on double clique sur le texte de l’élément, on récupère sa valeur, on la remplace par un champ de saisie avec comme par valeur par défaut le texte de l’élément, sélectionné automatiquement.

Renommer un élément

Ensuite, lorsque l’on quitte la zone du champ de saisie (méthode « blur()« ),  ou lorsque l’on valide par la touche <entrée>, on récupère la nouvelle valeur que l’on attribue à notre élément.

Interopérabilité

L’application a été testée sous Firefox 3.6, Chrome (PC), Safari, IE8.

A noter sous IE7, un léger problème de disparition des deux <span> Position et Bouton lors du déplacement d’un élément. Si par hasard vous arrivez à le corriger, n’hésitez pas à laisser un message 😉

Interaction avec une base de données

Pour aller plus loin et sauvegarder les données triées dans une base de données, vous pouvez suivre le très bon tutoriel de Sébastien Angot, qui se base sur cet article pour ajouter cette fonctionnalité de sauvegarde via Ajax. C’est par ici: Tutoriel: Drag and drop jQuery / AJAX.

Conclusion

Et voilà notre liste de course est terminée ! Facile non ?

Pour toute question, laissez un commentaire juste en dessous là et sinon ça se passe ici pour la Demo et là pour les Sources.

65 commentaires

1 2 3 4
  1. Zenka

    Nice article !
    Ça va beaucoup m’aider et une pour fois tout est bien expliqué ce qui me permet de bien comprendre .

    Merci Encore

  2. Steph

    Ne fonctionne pas avec IE9 (le drag ne drag pas)

  3. alexiase

    sur IE9 c’est ok essaye les dernières versions de jquery et jquery ui

  4. Kasimodo

    Merci pour ce tuto, mais j’ai un petit souci.
    En effet j’appelle mes fichiers .js depuis ma page maître.

    <script type="text/javascript" src=" » language= »javascript »>
    <script type="text/javascript" src=" » language= »javascript »>
    <script type="text/javascript" src=" »>
    <script type="text/javascript" src=" »>

    $(function() {
    $(« #Divdep »).draggable();
    $(« #DivCorbeille »).droppable({
    drop: function(event, ui) {
    $(this)
    .html(« Welcome ! »);
    // resultat = $(« #form1 »).find(« #textdepart »).val();
    // $(« #textarrive »).value = resultat;
    resultat = $(« #PhotoArticle »).selector;
    alert(resultat);
    }
    }
    );
    });
    }

    Cependant voici un message d’erreur qui me tourmente : $(« #Divdep »).draggable is not a function dan s firebug. Peux tu m’aider à résoudre. J’ai l’impression qu’il ne reconnaît pas mon fichier Jquery UI. Merci et a ¨+

  5. daggoon

    Merci pour ce tuto. Je cherche en vain à faire un drop de noms d’une liste dans un champ input. Je ne trouve pas la solution. Autre donnée: si je droppe un autre nom sur la cellule input, le nouveau nom doit remplacer le précédent. Pouvez vous me guider? merci

  6. daggoon

    Je reviens vers vous avec ce script. Le drag and drop fonctionne. Mais je n’arrive pas à mettre un prénom dans le input. Quand je fais le drag and drop. Le input disparait. J’arrive à mettre un prénom. Si je choisis un autre prénom. En le déposant, il remplace bien le précédent. C’est ce que je souhaite. Mon problème c’est que le prénom entre dans le input. Pouvez vous m’aiguiller ? merci

    h1 { padding: .2em; margin: 0; }
    #planning { float:left; width: 500px; margin-right: 2em; }
    #cart { width: 200px; float: left; }
    #cart ol { margin: 0; padding: 1em 0 1em 3em; }
    li {margin:0; padding:0; list-style-image:none; list-style-position:outside; list-style-type:none; }

    $(function() {

    $(« #catalog li »).draggable({
    appendTo: « body »,
    helper: « clone »
    });
    $(« #cart ol »).droppable({
    activeClass: « ui-state-default »,
    hoverClass: « ui-state-hover »,
    drop: function(event, ui) {
    $(this).find(« .planning »).text(ui.draggable.text())
    .appendTo(this);
    }
    });

    });

    liste

    Eric
    Claire
    Olivier

    cellule

  7. Eric D.

    Code super-commenté et tout et tout…
    Merci !

  8. Fred

    Vraiment bien codé.
    Seul bémol, au double clic, si tu vides l’input, après plus moyen de le rééditer. 🙂

    Merci.

  9. ibanez

    Bonjour, et merci pour ce tuto super bien expliqué et documenter, j’aimerai savoir, si il ya un moyen de restoré un élément du trash. merci encore 🙂

  10. Rin

    Thank u soooo much 😀

  11. younes

    merci c’est un site génial

  12. Arsu

    Merci et bravo pour ce tuto très utile, très bien commenté, très bien expliqué !
    Effectivement, il y a un léger bug sous IE7 : lorsqu’on trie, le bouton et le compteur disparaissent… Personne n’a trouvé de solution ?
    Merci d’avance 🙂

    • Gui
  13. flaydeer

    Surper script,

    et comment enregistrer les données pour que les elements reste à leur place ?
    merci.

  14. flaydeer

    Super, Merci.

  15. diwan-site-web

    Merci bien vraiment un tuto complete pour les personnes qui cherche à commencer dans le drag and drop

  16. Goodtobeme

    Bonjour,

    Tout d’abord, bravo pour ce tutoriel formidable que je découvre avec plaisir et j’ai deux questions à son sujet.

    La première est à quoi sert :
    // Options par defaut
    var defaults = {};
    var options = $.extend(defaults, options);

    La deuxième est si ceci empêche la sélection des éléments à la souris :
    var _preventDefault = function(evt) { evt.preventDefault(); };
    $(« li »).bind(« dragstart », _preventDefault).bind(« selectstart »,
    _preventDefault);
    Pourquoi ne pas ajouter plutôt un disableselection à votre liste sortable comme ceci:
    $(obj).sortable({
    …….
    }).disableSelection();

    Débutant dans le domaine, je demande donc votre compréhension, merci à tous.

    • Gui
  17. jeremy

    excellent ton blog, je suis bien content d’être tombé dessus! a bientot j’espère

  18. Inside

    Salut,

    C’est vraiment un excellent tutoriel, j’ai juste un question :

    J’ai crée deux listes mais je n’arrive pas a déplacer un élément d’une liste à l’autre.
    J’ai débloqué l’axe x,y, et je permet également au drag de sortir de l’élément qui contient la liste mais pas a l’ajouter a une autre liste..

    Quelle partie de code modifier ?

    Merci d’avance.

1 2 3 4

Laisser une réponse


Post shadow