Drag’n’Drop est une excellente solution d’interface. Prendre quelque chose et le glisser-déposer est un moyen clair et simple de faire beaucoup de choses, de la copie et du déplacement de documents (comme dans les gestionnaires de fichiers) à la commande (déposer des éléments dans un panier).
Dans le HTML moderne standard, il existe une section sur le glisser-déposer avec des événements spéciaux tels que dragstart
, dragend
, et ainsi de suite.
Ces événements nous permettent de prendre en charge des types spéciaux de glisser-déposer, tels que la gestion du glisser-déposer d’un fichier depuis le gestionnaire de fichiers du système d’exploitation et le déposer dans la fenêtre du navigateur. Ensuite, JavaScript peut accéder au contenu de ces fichiers.
Mais les événements de glisser natifs ont également des limitations. Par exemple, nous ne pouvons pas empêcher le glissement depuis une certaine zone. De plus, nous ne pouvons pas rendre le glissement « horizontal » ou « vertical » uniquement. Et il existe de nombreuses autres tâches de glisser-déposer qui ne peuvent pas être effectuées en les utilisant. De plus, la prise en charge de ces événements par les appareils mobiles est très faible.
Nous verrons donc ici comment implémenter Drag’n’Drop à l’aide d’événements de souris.
Algorithme Drag’n’Drop
L’algorithme de base Drag’n’Drop ressemble à ceci:
- On
mousedown
– prépare l’élément pour le déplacement, si nécessaire (peut-être en créer un clone, y ajouter une classe ou autre). - Puis sur
mousemove
déplacez-le en changeantleft/top
avecposition:absolute
. - Sur
mouseup
– effectuez toutes les actions liées à la fin du drag’n ‘drop.
Voici les bases. Plus tard, nous verrons comment d’autres fonctionnalités, telles que la mise en évidence des éléments sous-jacents actuels pendant que nous les glissons.
Voici l’implémentation du glissement d’une balle:
Si nous exécutons le code, nous pouvons remarquer quelque chose d’étrange. Au début du drag’n’drop, la balle « fourche »: on commence à faire glisser son « clone ».
Voici un exemple en action:
Essayez de glisser-déposer avec la souris et vous verrez un tel comportement.
C’est parce que le navigateur a son propre support de glisser-déposer pour les images et quelques autres éléments. Il s’exécute automatiquement et entre en conflit avec le nôtre.
Pour le désactiver:
Maintenant, tout ira bien.
En action:
Un autre aspect important – nous suivons mousemove
sur document
, pas sur ball
. À première vue, il peut sembler que la souris est toujours au-dessus de la balle, et nous pouvons y mettre mousemove
.
Mais comme on s’en souvient, mousemove
se déclenche souvent, mais pas pour chaque pixel. Ainsi, après un déplacement rapide, le pointeur peut sauter de la balle quelque part au milieu du document (ou même à l’extérieur de la fenêtre).
Nous devrions donc écouter sur document
pour l’attraper.
Positionnement correct
Dans les exemples ci-dessus, la balle est toujours déplacée de sorte que son centre soit sous le pointeur:
Pas mal, mais il y a un effet secondaire. Pour lancer le glisser-déposer, nous pouvons mousedown
n’importe où sur la balle. Mais si vous la «prenez» à partir de son bord, alors la balle «saute» soudainement pour se centrer sous le pointeur de la souris.
Ce serait mieux si nous gardions le décalage initial de l’élément par rapport au pointeur.
Par exemple, si nous commençons à faire glisser par le bord de la balle, le pointeur doit rester sur le bord pendant le déplacement.
Mettons à jour notre algorithme:
Le code final avec un meilleur positionnement:
En action (à l’intérieur de <iframe>
) :
La différence est particulièrement perceptible si l’on fait glisser la balle par son coin en bas à droite. Dans l’exemple précédent, la balle « saute » sous le pointeur. Maintenant, elle suit couramment le pointeur à partir de la position actuelle.
Cibles de largage potentielles (droppables)
Dans les exemples précédents, la balle pourrait être déposé « n’importe où » pour rester. Dans la vraie vie, nous prenons généralement un élément et le déposons sur un autre. Par exemple, un « fichier » dans un « dossier » ou autre chose.
En parlant de résumé, nous prenons un élément « draggable » et le déposons sur l’élément « dropable ».
Nous besoin de savoir:
- où l’élément a été déposé à la fin de Drag’n’Drop – pour faire l’action correspondante,
- et, de préférence, connaître le droppable nous traîne, pour le mettre en évidence.
La solution est assez intéressante et juste un peu délicate, alors couvrons-la ici.
Ce qui peut être la première idée? Probablement de définir des gestionnaires de mouseover/mouseup
sur des droppables potentiels?
Mais cela ne fonctionne pas.
Le problème est que, alors que nous sommes en faisant glisser, l’élément déplaçable est toujours au-dessus des autres éléments.Et les événements de souris ne se produisent que sur l’élément supérieur, pas sur ceux en dessous.
Par exemple, ci-dessous se trouvent deux éléments <div>
, un rouge au-dessus du bleu un (couvre entièrement). Il n’y a aucun moyen d’attraper un événement sur le bleu, car le rouge est en haut:
La même chose avec un élément déplaçable. La balle est toujours au-dessus des autres éléments, donc les événements se produisent dessus. Quels que soient les gestionnaires que nous définissons sur des éléments inférieurs, ils ne fonctionneront pas.
C’est pourquoi l’idée initiale de placer des gestionnaires sur des droppables potentiels ne fonctionne pas dans la pratique. Ils ne fonctionneront pas.
Alors, que faire?
Il existe une méthode appelée document.elementFromPoint(clientX, clientY)
. Il renvoie l’élément le plus imbriqué sur les coordonnées relatives à la fenêtre (ou null
si les coordonnées données sont hors de la fenêtre).
Nous pouvons l’utiliser dans n’importe lequel des nos gestionnaires d’événements de souris pour détecter le potentiel droppable sous le pointeur, comme ceci:
Veuillez noter: nous devons cacher la balle avant l’appel (*)
. Sinon, nous aurons généralement une boule sur ces coordonnées, car il s’agit de l’élément supérieur sous le pointeur: elemBelow=ball
. Nous le masquons et le réafficherons immédiatement.
Nous pouvons utiliser ce code pour vérifier quel élément nous « survolons » à tout moment. Et gérer la chute lorsque cela se produit.
Un code étendu de onMouseMove
pour trouver les éléments « droppables »:
Dans l’exemple ci-dessous, lorsque le ballon est glissé sur le but de football, le but est mis en surbrillance.
Nous avons maintenant la « cible de dépôt » actuelle, que nous survolons, dans la variable currentDroppable
pendant tout le processus et pouvons l’utiliser pour mettre en évidence ou tout autre truc.
Résumé
Nous avons considéré un algorithme de Drag’n’Drop de base.
Les composants clés:
- Flux d’événements:
ball.mousedown
→document.mousemove
→ball.mouseup
(n’oubliez pas d’annulerondragstart
). - Au début du glissement – rappelez-vous le décalage initial du pointeur par rapport à l’élément:
shiftX/shiftY
et conservez-le pendant le glissement. - Détecter les éléments déposables et er le pointeur en utilisant
document.elementFromPoint
.
Nous pouvons énoncer beaucoup de choses sur cette fondation.
- Sur
mouseup
nous pouvons finaliser intellectuellement le drop: changer les données, déplacer les éléments. - Nous pouvons mettre en évidence les éléments que nous survolons.
- Nous peut limiter le glissement par une certaine zone ou direction.
- Nous pouvons utiliser la délégation d’événements pour
mousedown/up
. Un gestionnaire d’événements de grande surface qui vérifieevent.target
peut gérer Drag’n’Drop pour des centaines d’éléments. - Et ainsi de suite.
Il existe des frameworks qui construisent une architecture dessus: DragZone
, Droppable
, Draggable
et autres classes. La plupart d’entre eux font des choses similaires à celles décrites ci-dessus, il devrait donc être facile de les comprendre maintenant. Ou lancez le vôtre, car vous pouvez voir que c’est assez facile à faire, parfois plus facile que d’adapter une solution tierce.