Sur le web, j'ai trouvé beaucoup de mauvaises pistes, la plupart s'acharnant sur les méthodes "out", "over" et "drop" de l'élément droppable.

Ce n'est pas trivial, j'ai eu pas mal de soucis pour mettre au point une solution qui fonctionne correctement pour plusieurs éléments draggable dans plusieurs éléments droppable, mais un seul à la fois.

Code : Sélectionner tout - Visualiser dans une fenêtre à part
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
<!DOCTYPE html>
<html lang="fr" dir="ltr">
<head>
    <meta charset="utf-8">
    <meta name="viewport" content="initial-scale=1.0">
    <meta name="author" content="Daniel Hagnoul">
    <title>Forum jQuery</title>
    <script src="http://cdnjs.cloudflare.com/ajax/libs/headjs/1.0.3/head.min.js"></script>
    <link rel="stylesheet" href='http://fonts.googleapis.com/css?family=Sofia|Ubuntu:400|Kreon'>
    <script>
        "use strict";
 
        var debugBool = true;
 
         /*
          * J'utilise head.js pour charger CSS et JS de manière asynchrone 
          * et parallèle, mais les fichiers sont exécute dans l'ordre.
          * Voir la documentation et l'API : http://headjs.com/
          * Les polices de caractères et le fichier head.js doivent être 
          * inclus manuellement.
          */
        head.load(
            "http://danielhagnoul.developpez.com/styles/dvjhRemBase.css",
            "http://code.jquery.com/ui/1.10.4/themes/sunny/jquery-ui.css",
            "http://code.jquery.com/qunit/qunit-1.13.0.css",
            { "d3" : "http://d3js.org/d3.v3.min.js" },
            { "d3Hello" : "http://danielhagnoul.developpez.com/lib/dvjh/d3Hello.js" },
            { "jquery" : "http://code.jquery.com/jquery-2.1.0.min.js" },
            { "i18n" : "http://ajax.googleapis.com/ajax/libs/jqueryui/1.10.4/i18n/jquery-ui-i18n.js" },
            { "jqueryui" : "http://code.jquery.com/ui/1.10.4/jquery-ui.min.js" },
            { "datefr" : "http://danielhagnoul.developpez.com/lib/dvjh/datefr.js" },
            { "qunit" : "http://code.jquery.com/qunit/qunit-1.13.0.js" },
            { "testsQUnit" : "http://danielhagnoul.developpez.com/lib/dvjh/testsQUnit.js" }
        );      
    </script>
    <style>
        /* Nota bene : ici 1 rem est égal à 10 px, voir dvjhRemBase.css */
 
/*-- Début code du test --*/
 
.droppable { float: left; width: 15rem; height: 15rem; margin: 1.2rem; 
    border: 0.1rem solid grey; background-color: grey; }
.draggable { float: left; width: 10rem; height: 10rem; margin: 1.2rem; 
    border: 0.1rem solid red; background-color: yellow; }
 
/*-- Fin code du test --*/
 
    </style>
</head>
<body>
    <header>
        <h1>Forum jQuery</h1>
        <h2>
            <a href="">Lien</a>
        </h2>
    </header>
    <section class="conteneur">
        <nav>
 
<!-- Début code du test -->
 
 
<!-- Fin code du test -->
 
        </nav>
        <article>
 
<!-- Début code du test -->
 
<div class="droppable" data-drop="vide">
    <p>Drop me here</p>
</div>
 
<div class="droppable" data-drop="vide">
    <p>Drop me here</p>
</div>
 
<div class="droppable" data-drop="vide">
    <p>Drop me here</p>
</div>
 
<div class="droppable" data-drop="vide">
    <p>Drop me here</p>
</div>
 
<div class="droppable" data-drop="vide">
    <p>Drop me here</p>
</div>
 
<p style="clear: both;">&nbsp;</p>
 
<p id="results"></p>
 
<div class="draggable" data-drag="Objet 1">
    <p>I revert when I'm not dropped</p>
</div>
 
<div class="draggable"  data-drag="Objet 2">
    <p>I revert when I'm not dropped</p>
</div>
 
<p style="clear: both;">&nbsp;</p>
 
<!-- Fin code du test -->
 
        </article>
        <article class="qunit">
            <div id="qunit"></div>
            <div id="qunit-fixture"></div>
        </article>
    </section>
    <footer class="h-entry">
        <time class="dt-published" datetime="2014-01-22T10:36:43.443+0100">2014-01-22T10:36:43.443+0100</time>
        <a class="p-author h-card" href="http://www.developpez.net/forums/u285162/danielhagnoul/">Daniel Hagnoul</a>
        <a class="u-url" href="http://danielhagnoul.developpez.com/">Mon cahier d’exercices</a>
        <a class="u-url" href="http://javascript.developpez.com/faq/jquery/">FAQ</a>
        <a class="u-url" href="http://javascript.developpez.com/cours/?page=frameworks#jquery">Tutoriels</a>
    </footer>
    <script>
        "use strict";
 
        /*
         * Chargeur de code head.js, document ready et fichiers chargés.
         */
        head.ready( [ 
                "d3", "d3Hello", 
                "jquery", "jqueryui", 
                "qunit", "testsQUnit"
            ], function(){
 
/* Début code du test */
 
/*
 * On détermine la valeur (true ou false) de l'option revert
 * afin q'un élément droppable ne recoive qu'un seul élément 
 * draggable.
 * 
 * L'attribut data-drop doit contenir soit "vide" soit le nom d'un
 * objet draggable (contenu dans l'attribut data-darg).
 * 
 * On affiche dans l'élément d'ID "results" la valeur des attributs
 * data-drop des éléments ayant la classe "droppable".
 * 
 * Lorsqu'un élément draggable quitte un élément droppable, la
 * valeur de son attribut data-drop soit être remis à "vide".
 */
 
$( ".draggable" ).draggable({
    "revert" : function( jObj ){
 
        // si jObj existe et c'est un élément droppable'
        if ( jObj && jObj.hasClass( "droppable" ) ){
            var strDrop = jObj.data( "drop" ),
                strDrag = $( this ).data( "drag");
 
            // on n'accepte qu'un seul draggable
            if ( strDrop != "vide" ){
                return true;
            }
 
            // on tient compte de l'élément draggable
            jObj.data( "drop", strDrag );
 
            // array contenant les informations
            var tab = [];
 
            // on parcours les droppable
            $( ".droppable" ).each( function( i, item ){
 
                // si ce droppable est différent de celui en cours
                if ( item != jObj[ 0 ] ){
 
                    // si son data-drop contient strDrag
                    if ( $( item ).data( "drop" ) == strDrag ){
 
                        // alors il doit contenir "vide"
                        $( item ).data( "drop", "vide" );
                    }
                }
 
                tab[ i ] = $( item ).data( "drop" );
            });
 
            $( "#results" ).html( tab.join( " | " ) );
 
            return false;
        }
 
        // pour un déplacement en dehors d'un élément droppable
        return true;
    }
});
 
$( ".droppable" ).droppable({
    "accept" : ".draggable"
});
 
 
/* Fin code du test */
 
            if ( debugBool ){
               console.log( ISOformat( new Date() ) );
 
                $( ".qunit" ).show();
 
                testQUnitSelector( "App", [ 
                    ".conteneur"
                ] );
 
                testQUnitID( "App", [ 
                    "qunit", "qunit-fixture"
                ] );
            }
 
        });
    </script>
</body>
</html>