Tutorial JQuery : trier une liste en drag’n'drop avec JQueryUI Sortable

Il est toujours compliqué de proposer au internautes de trier des éléments et faire ça de manière simple et ergonomique. On peut, par exemple, proposer de saisir la position pour chaque élément. Mais ça reste contraignant.

C’est dans cette optique qu’apparaît l’option du drag’n'drop (glisser-déposer en français). Je pense que c’est ce qu’il y a de plus simple et intuitif pour l’internaute. Il faut néanmoins le prévenir que l’on attend un glisser-déposer de sa part car ce n’est pas encore répandu absolument partout !

Voir la démo du tutorial

Pour réaliser cela, JQueryUI nous propose le widget nommé Sortable. Il permet de créer et de configurer le drag’n'drop pour une liste d’éléments. Pour commencer, il faut donc inclure : JQuery et JQueryUI (javascript et CSS).

<script type="text/javascript" src="http://ajax.googleapis.com/ajax/libs/jquery/1.4.2/jquery.min.js"></script>
<link rel="stylesheet" href="jquery-ui-1.8.5.custom.css" type="text/css" />
<script type="text/javascript" src="jquery-ui-1.8.5.custom.min.js"></script>

Pour sauvegarder le tri fait par l’internaute, il faut enregistrer le tout dans la base de données. Je te fournis donc celle utilisée dans la démo. Où chauqe élément à un id unique, name contient le nom du fichier de la photo et position pour enregistrer l’ordre des photos.

-- 
-- Structure de la table `demo_photo`
-- 
CREATE TABLE `demo_photo` (
  `id` int(11) NOT NULL auto_increment,
  `name` varchar(255) collate latin1_german2_ci NOT NULL,
  `position` int(11) NOT NULL,
  PRIMARY KEY  (`id`),
  KEY `order` (`position`)
) AUTO_INCREMENT=6 ;
 
-- 
-- Contenu de la table `demo_photo`
-- 
INSERT INTO `demo_photo` VALUES (1, '1.jpg', 2);
INSERT INTO `demo_photo` VALUES (2, '2.jpg', 3);
INSERT INTO `demo_photo` VALUES (3, '3.jpg', 4);
INSERT INTO `demo_photo` VALUES (4, '4.jpg', 1);
INSERT INTO `demo_photo` VALUES (5, '5.jpg', 0);

Ensuite il faut préparer la liste d’éléments sur lequel se portera le tri. Les attributs « id » des éléments de la liste devront respecter une syntaxe particulière : « nomGenerique_id ». Ou nomGenerique est le même pour tout les éléments (par exemple « photo ») et id l’identifiant unique (dans la base de données) de l’élément à trier.

Pour afficher la liste, on récupère bien sûr les éléments dans la base de données, en respectant l’ordre enregistré :

<ul id="list-photos">
	<?php
	//connexion à la base de données 
        define('DB_NAME', 'nom_de_la_base');
        define('DB_USER', 'nom_utilisateur');
        define('DB_PASSWORD', 'mot_de_passe');
        define('DB_HOST', 'localhost');
	$link   =   mysql_connect( DB_HOST , DB_USER , DB_PASSWORD );
	mysql_select_db( DB_NAME , $link );
 
	// récupération des photos dans le bon ordre
	$result =   mysql_query( 'SELECT demo_photo.id, demo_photo.name FROM demo_photo ORDER BY demo_photo.position ASC' , $link );
	while( $photo = mysql_fetch_assoc( $result ))
	{
	?>
	<li id="photo_<?php echo $photo['id'] ?>">
		<img src="photos_NY/<?php echo $photo['name'] ?/>" alt="Photos NY - Arnaud-k" />
		<p>par Arnaud-k</p>
	</li>
	<?php
	}
	?>
</ul>

Il faut ensuite mettre en forme cette liste avec du CSS. Je ne pense pas qu’il faille beaucoup d’explications pour cette partie là.

/* style global de la liste non ordonné */
ul#list-photos{
  list-style:none;
  height:140px;
}
/* style des éléments de la liste */
ul#list-photos li{
  border:1px solid #ddd;
  padding:10px;
  cursor:move;
  height:100px;
  width:75px;
  float:left;
  margin-right:10px;
  background:#fff;
  color:#212326;
  font-size:12px;
  -moz-box-shadow:2px 2px 5px #ccc;
}
/* style de l'élément fantome, qui apparait losque que l'on bouge un élément */
ul#list-photos li.highlight{
  background:#f2f2f2;
  border:1px dashed #212326;
}

Ensuite vient la partie qui nous intéresse vraiment, le code JQuery. On initialise Sortable avec l’id de la liste et on met en Callback l’appel Ajax au fichier PHP qui va procéder à l’enregistrement dans la base données :

$(document).ready( function(){ // quand la page a fini de se charger
  $("#list-photos").sortable({ // initialisation de Sortable sur #list-photos
	placeholder: 'highlight', // classe à ajouter à l'élément fantome
	update: function() {  // callback quand l'ordre de la liste est changé
		var order = $('#list-photos').sortable('serialize'); // récupération des données à envoyer
		$.post('ajax.php',order); // appel ajax au fichier ajax.php avec l'ordre des photos
	}
  });
  $("#list-photos").disableSelection(); // on désactive la possibilité au navigateur de faire des sélections
});

Et voici enfin le contenu du fichier ajax.php qui enregistre les positions des éléments. On récupère un $_POST de la forme : photo[] = id_photo. Il suffit de boucler sur ce tableau ($_POST['photo']) et on récupère les id des photos dans l’ordre :

<?php
//connexion à la base de données 
define('DB_NAME', 'nom_de_la_base');
define('DB_USER', 'nom_utilisateur');
define('DB_PASSWORD', 'mot_de_passe');
define('DB_HOST', 'localhost');
$link   =   mysql_connect( DB_HOST , DB_USER , DB_PASSWORD );
mysql_select_db( DB_NAME , $link );
 
// changement de l'ordre des photos dans la base de données, photo par photo
foreach( $_POST['photo'] as $order => $id_photo )
{
    mysql_query( 'UPDATE demo_photo SET position = \'' . safe( $order ) . '\' WHERE id = \'' . safe( $id_photo ) . '\'' , $link ) or die( mysql_error() );
}
 
/*****
fonctions
*****/
function safe($var)
{
	$var = mysql_real_escape_string($var);
	$var = addcslashes($var, '%_');
	$var = trim($var);
	$var = htmlspecialchars($var);
	return $var;
}
?>

J’espère que tutoriel est clair et compréhensible. Il faut, certes, un minimum de connaissances en html, javascript et php mais je pense que ça vaut quand même le coup !

Pas encore de billet sur le même sujet !

Cette entrée a été publiée dans JQuery, avec comme mot(s)-clef(s) , , . Vous pouvez la mettre en favoris avec ce permalien.

25 reponses a Tutorial JQuery : trier une liste en drag’n'drop avec JQueryUI Sortable

  1. groGeek dit :

    Hello,

    Je découvre tes tutos et ce n’est que du bonheur.
    Je bosse normalement en html+CSS+php mais il est temps pour moi de passer a quelque chose de plus. (suis ancien codeur AS2 en fait).

    Je suis sur un projet open source http.//openardilla.org qui est à la base juste un ptit gestionnaire perso de bookmarks. Comme on m’en a souvent demander les sources, je l’ai passé sous GPL2 et ai ajouté un gestion simpliste de flux et de notes (anciennement cave à vin aussi)

    Je cherchais exactement ce genre de tutos pour faire évoluer mon projet.
    ;) .

  2. okiSeb dit :

    Salut !
    Très bon tuto, simple clair et efficace !
    Merci pour le partage, ce tuto m’a été bien utile pour un de mes projets.
    Je reviendrai voir le reste qui me semble plutôt bien fourni :)

  3. Maxence dit :

    Bonjour Arnaud,

    Tout d’abord merci beaucoup pour le temps passé à faire ce tuto… c’est grâce à des gens comme toi que des personnes comme moi, autodidacte, peuvent apprendre jour après jour.
    Jusqu’ici je bossais sur de l’AS2 et depuis quelques semaines je me mets peu à peu à l’HTML, PHP, Jquery etc… pour faire le site internet d’un ami photographe.
    J’ai essayé tant que possible d’appliquer ton tuto… celui-ci fonctionne parfaitement bien… mais il y a un problème que je n’arrive pas à résoudre malgré les recherches multiples que j’ai pu faire…
    Voilà lorsque je drag and drop mes fichiers, ceux-ci changent bien de place par contre dès que je recharge ma page, malheureusement ils reprennent leur place initiale.
    Comment puis-je faire (où qu’ai-je fais de travers) pour que mes modifications d’ordre s’enregistre sur ma base de donnée Mysql?

    Voilà le code que j’ai pour le moment sur ma page html : (j’ai juste enlevé mes identifiants)

    ————————————————

    Untitled Document

    /************
    STYLE DE LA LISTE A TRIER
    ************/
    ul#list-photos{
    list-style:none;
    height:140px;
    }
    ul#list-photos li{
    border:1px solid #ddd;
    padding:10px;
    cursor:move;
    height:100px;
    width:75px;
    float:left;
    margin-right:10px;
    background:#fff;
    color:#212326;
    font-size:12px;
    -moz-box-shadow:2px 2px 5px #ccc;
    }
    ul#list-photos li.highlight{
    background:#f2f2f2;
    border:1px dashed #212326;
    }

    /************
    DEBUT DU SCRIPT POUR LE DRAG’N'DROP
    ************/
    $(document).ready( function(){ // quand la page a fini de se charger
    $(« #list-photos »).sortable({ // initialisation de Sortable sur #list-photos
    placeholder: ‘highlight’, // classe à ajouter à l’élément fantome
    update: function() { // callback quand l’ordre de la liste est changé
    var order = $(‘#list-photos’).sortable(‘serialize’); // récupération des données à envoyer
    $.post(‘ajax.php’,order); // appel ajax au fichier ajax.php avec l’ordre des photos
    }
    });
    $(« #list-photos »).disableSelection(); // on désactive la possibilité au navigateur de faire des sélections
    });

    <li id="photo_ »>

    ———————————————————————————————–
    Voilà celui de ma page ajax.php

    $id_photo )
    {
    mysql_query( ‘UPDATE Dynamic SET position = \ » . safe( $order ) . ‘\’ WHERE id = \ » . safe( $id_photo ) . ‘\ » , $link ) or die( mysql_error() );
    }

    /*****
    fonctions
    *****/
    function safe($var)
    {
    $var = mysql_real_escape_string($var);
    $var = addcslashes($var, ‘%_’);
    $var = trim($var);
    $var = htmlspecialchars($var);
    return $var;
    }
    ?>

    ———————————–

    Ma base de donnée se présente ainsi :

    id / TitreAlbum/ OrdreAlbum

    ——————————–

    J’espère vraiment que tu pourras m’aider, car j’ai longtemps cherché toute seule avant de te contacter.

    Merci encore pour tout

    Maxence

  4. Maxence dit :

    Résolu : ma classe order n’était pas en clé « index » et c’est pour cela que ça ne fonctionnait pas !

  5. matthieu dit :

    Bonjour ,

    Un très grand merci pour ce module drag and drop.
    Est- il adapatble sur un tableau j’ai essayé sans succes

    merci de votre aide

  6. Jean dit :

    Très bonne idée, dommage que ça fonctionne pas sur IE9 l’exemple .
    Quelqu’un a une soluce?

  7. maxence dit :

    Et bien je suis étonnée que ça ne fonctionne pas sur IE9 car ça fonctionne sur IE8 sans soucis…

  8. arnaud-k dit :

    Pour la compatibilité IE9, il faudrait essayer en mettant à jour JQuery UI vers la dernière version. Dans l’exemple c’est la version 1.8.5 et on en est à la 1.8.10.
    Ça peut être une bonne piste !

  9. syndrael dit :

    Sympa ce tuto. Perso ça ne me servira pas mais je peux comprendre que ça plaise à pas mal de monde.
    En tout cas je t’encourage à poursuivre.
    Bonne journée

  10. JC dit :

    Super pour ce tuto, simple et bien expliqué, ça marche nickel ! Dans la meme idée, y a t’il moyen de pouvoir intervertir deux photos par exemple la une et la trois sans que les autres ne bougent.
    La trois viendrait prendre la place de la une et vice versa, les autres photos conservent leur position.

  11. Elokaze dit :

    Bonjour,

    Super tuto très clair.
    Ici il y a échange de code html. Comment faire pour qu’entre un composant Jquery UI et le code php ne s’échange que des données formatées par exemple en XML ? Ceci pour isoler la couche de présentation de la logique de code.
    Merci

  12. Miraksunny dit :

    Bonjour,
    oui un très bon tuto.
    Par contre quand on y couple une pagination, là l’ordre des photos devient complètement chaotique. Et en cas de galerie photos, la pagination est inévitable.
    Si quelqu’un a une solution.
    Merci

  13. Tatouine dit :

    c’est sympa comme tuto,
    par contre j’ai un petit souci quand je veux passer une autre variable en POST.
    En fait quand on passe order en POST on obtient une array pour les positions avec les valeurs 0,1,2,3 etc…

    Mais si vous y incluez une autre variable :
    var order = $(‘#list-photos’).sortable(‘serialize’);
    var toto = ‘toto’;
    var donnees = « order= »+order+ »&toto= »+toto;
    $.post(‘ajax.php’,donnees); // appel ajax au fichier ajax.php avec l’ordre des photos

    l’array de position donne 0,0,1,2,3,4,etc….
    Donc 2 positions à 0.
    L’auteur ou un internaute saurait il pourquoi ?

    Merci à tous

  14. laurent dit :

    Super merci pour ce tutorial.

  15. Sylvain dit :

    Moi qui suis un quiche en ajax et qui cherchai justement à trier des photos après upload, je te dis un immense merci pour ton script qui est aussi clair qu’efficasse!
    Chapeau l’artiste!

  16. simo dit :

    Un grand merci Arnaud, vous m’avez sauvez la vie. Respect MAN

  17. Atow dit :

    Bonjour, je me permet d’ajouter un petit complément pour optimiser la requête dans ajax.php
    En effet tel qu’il est écrit dans le tuto il effectue autant de requête qu’il y a de photos à trier.
    Je vous propose une solution qui permet de faire l’opération en une seule requête afin de moins solliciter le serveur dans le cas où il y aurait beaucoup de photo à trier:

    $id_photo){
    $requete .= 'WHEN '.safe($id_photo).' THEN '.safe($order).' ';
    }
    $requete .= 'ELSE position END';

    mysql_query($requete , $link ) or die( mysql_error() );

    /*****
    fonctions
    *****/
    function safe($var)
    {
    $var = mysql_real_escape_string($var);
    $var = addcslashes($var, '%_');
    $var = trim($var);
    $var = htmlspecialchars($var);
    return $var;
    }
    ?>

  18. Atow dit :

    petite erreur d’affichage dans la balise code que j’ai mis dans le com et qui ma supprimer le début… revoila le code :

    //connexion à la base de données
    define(‘DB_NAME’, ‘nom_de_la_base’);
    define(‘DB_USER’, ‘nom_utilisateur’);
    define(‘DB_PASSWORD’, ‘mot_de_passe’);
    define(‘DB_HOST’, ‘localhost’);
    $link = mysql_connect( DB_HOST , DB_USER , DB_PASSWORD );
    mysql_select_db( DB_NAME , $link );

    // changement de l’ordre des photos dans la base de données, photo par photo
    $requete = ‘ UPDATE demo_photo SET position = CASE demo_photo.id ‘;
    foreach ($_POST['photo'] as $order =>$id_photo){
    $requete .= ‘WHEN ‘.safe($id_photo).’ THEN ‘.safe($order).’ ‘;
    }
    $requete .= ‘ELSE position END’;

    mysql_query($requete , $link ) or die( mysql_error() );
    /*****
    fonctions
    *****/
    function safe($var)
    {
    $var = mysql_real_escape_string($var);
    $var = addcslashes($var, ‘%_’);
    $var = trim($var);
    $var = htmlspecialchars($var);
    return $var;
    }

  19. julkes dit :

    Ta fonction ‘safe’ ici est inutile.
    Tu récupère 2 entiers ‘id_photo’ et ‘order’, tu as juste besoin de mettre un (int) devant tes variables.
    Tu fais une grosse économie de performance.

  20. Anthony dit :

    Bonjour,

    Tout d’abord merci pour ce tuto ( c’est exactement ce que je cherchais pour créer un gestionnaire de tâches.)

    Je suis en pleine implémentation et j’ai remarqué une toute petite coquille dans le code.
    <li id="photo_ »>
    <img src="photos_NY/ » alt= »Photos NY – Arnaud-k » />
    par Arnaud-k

    Cette ligne :

    doit être :
    />

    Il manque juste le chevron pour fermer le code PHP.

    Encore merci pour ce tuto comme pour le site !
    Cordialement Anthony

  21. Anthony dit :

    Bonjour,

    Tout d’abord merci pour ce tuto ( c’est exactement ce que je cherchais pour créer un gestionnaire de tâches.)

    Je suis en pleine implémentation et j’ai remarqué une toute petite coquille dans le code.
    <li id="photo_">
    <img src="photos_NY//>" alt="Photos NY - Arnaud-k" />
    par Arnaud-k

    Cette ligne :

    doit être :
    />

    Il manque juste le chevron pour fermer le code PHP.

    Encore merci pour ce tuto comme pour le site !
    (désolé pour le double poste, je n’avais pas mis les balises pour le code)
    Cordialement Anthony

  22. Je découvre les nombreuses ressources pour JQuery de ton blog.. C’est génial ! Bravo !

  23. Magister dit :

    Ma question est : comment ajouter de nouveaux éléments a cette liste, en utilisant le dragNdrop ??
    Merci de votre aide

  24. ezrtfetgzerte dit :

    Ne fonctionne pas sous IE 9

  25. jordane dit :

    Merci pour ce tuto.
    Il m’a été bien utile.
    Petite précision toutefois, les ID des éléments « sortable » doivent être du style :
    « QuelqueChose_ID » comme vous l’avez écrit (photo_1…) pour que le Serialize fonctionne….(ben ouais.. je n’y pensais plus lorsque j’ai écrit mon code et je cherchais bêtement pourquoi mon serialize me retournait une chaine vide ^^ …. )

Laisser un commentaire

Votre adresse de messagerie ne sera pas publiée. Les champs obligatoires sont indiqués avec *

Vous pouvez utiliser ces balises et attributs HTML : <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong> <pre lang="" line="" escaped="" highlight="">