Træk “n” Drop med musebegivenheder

Drag’n’Drop er en fantastisk grænsefladesløsning. At tage noget og trække og slippe det er en klar og enkel måde at gøre mange ting på, lige fra kopiering og flytning af dokumenter (som i filadministratorer) til bestilling (smidning af varer i en vogn).

I den moderne HTML standard er der et afsnit om træk og slip med specielle begivenheder såsom dragstart, dragend osv.

Disse begivenheder giver os mulighed for at understøtte specielle slags drag’n’drop, såsom håndtering af at trække en fil fra OS-filhåndtering og slippe den i browservinduet. Derefter kan JavaScript få adgang til indholdet af sådanne filer.

Men oprindelige trækbegivenheder har også begrænsninger. For eksempel kan vi ikke forhindre træk fra et bestemt område. Vi kan heller ikke kun trække “vandret” eller “lodret”. Og der er mange andre drag’n’drop-opgaver, der ikke kan udføres ved hjælp af dem. Desuden er understøttelse af mobilenheder til sådanne begivenheder meget svag.

Så her får vi se, hvordan Drag’n’Drop implementeres ved hjælp af musebegivenheder.

Drag’n’Drop-algoritme

Den grundlæggende Drag’n’Drop-algoritme ser sådan ud:

  1. mousedown – forbered elementet til flytning, hvis nødvendigt (måske lav en klon af det, tilføj en klasse til det eller hvad som helst).
  2. Derefter flyttes det på mousemove ved at ændre left/top med position:absolute.
  3. mouseup – udfør alle handlinger relateret til efterbehandling af drag’n ‘drop.

Dette er de grundlæggende. Senere vil vi se, hvordan andre funktioner, såsom at fremhæve aktuelle underliggende elementer, mens vi trækker over dem.

Her er implementeringen af at trække en kugle:

Hvis vi kører koden, vi kan bemærke noget underligt. I begyndelsen af drag’n’drop “kugler” bolden: vi begynder at trække dens “klon”.

Her er et eksempel i aktion:

Prøv at trække og droppe med musen, så ser du en sådan adfærd.

Det er fordi browseren har sin egen drag’n’drop-understøttelse af billeder og nogle andre elementer. Det kører automatisk og er i konflikt med vores.

Sådan deaktiveres det:

Nu vil alt være i orden.

I aktion:

Et andet vigtigt aspekt – vi sporer mousemovedocument, ikke på ball. Fra første øjekast ser det ud til, at musen altid er over bolden, og vi kan sætte mousemove på den.

Men som vi husker, mousemove udløser ofte, men ikke for hver pixel. Så efter hurtig bevægelse kan markøren hoppe fra bolden et eller andet sted midt i dokumentet (eller endda uden for vinduet).

Så vi skal lytte på document for at fange det.

Korrekt positionering

I eksemplerne ovenfor flyttes bolden altid så, at den er midt under markøren:

Ikke dårligt, men der er en bivirkning. For at indlede drag’n’drop kan vi mousedown hvor som helst på bolden. Men hvis “tager” det fra kanten, så springer bolden pludselig for at blive centreret under musemarkøren.

Det ville være bedre, hvis vi holder elementets indledende forskydning i forhold til markøren.

For eksempel, hvis vi begynder at trække ved kanten af bolden, så skal markøren forblive over kanten, mens du trækker.

Lad os opdatere vores algoritme:

Den endelige kode med bedre positionering:

I aktion (inde i <iframe>) :

Forskellen er især synlig, hvis vi trækker bolden i højre nederste hjørne. I det forrige eksempel “springer” bolden under markøren. Nu følger den markøren flydende fra den aktuelle position.

Potentielle faldmål (droppables)

I tidligere eksempler kunne bolden kunne blive droppet bare “hvor som helst” for at blive. I det virkelige liv tager vi normalt et element og smider det på et andet. For eksempel en “fil” i en “mappe” eller noget andet.

Når vi taler abstrakt, tager vi et “trækbart” element og slipper det på “droppable” -element.

Vi har brug for at vide:

  • hvor elementet blev droppet i slutningen af Drag’n’Drop – for at udføre den tilsvarende handling
  • og fortrinsvis kende den droppable vi trækker for at fremhæve det.

Løsningen er slags interessant og bare lidt vanskelig, så lad os dække det her.

Hvad der kan være den første idé? Sandsynligvis for at indstille mouseover/mouseup -håndterere på potentielle droppables?

Men det virker ikke.

Problemet er, at mens vi er trækker, er det trækbare element altid over andre elementer.Og musebegivenheder sker kun på det øverste element, ikke på dem under det.

For eksempel er nedenunder to <div> -elementer, rød oven på det blå en (fuldt dækker). Der er ingen måde at fange en begivenhed på den blå, fordi den røde er øverst:

Det samme med et trækbart element. Bolden er altid på toppen af andre elementer, så begivenheder sker på den. Uanset hvilken håndterer vi sætter på lavere elementer, fungerer de ikke.

Derfor fungerer den første idé om at placere handlere på potentielle droppables ikke i praksis. De kører ikke.

Så hvad skal jeg gøre?

Der er en metode, der hedder document.elementFromPoint(clientX, clientY). Det returnerer det mest indlejrede element på givne vinduesrelaterede koordinater (eller null, hvis givne koordinater er ude af vinduet).

Vi kan bruge det i en hvilken som helst af vores mushændelseshåndterere til at opdage det potentiale, der kan smides under markøren, som denne:

Bemærk: Vi skal skjule bolden før opkaldet (*). Ellers har vi normalt en kugle på disse koordinater, da det er det øverste element under markøren: elemBelow=ball. Så vi skjuler det og viser straks igen.

Vi kan til enhver tid bruge den kode til at kontrollere, hvilket element vi “flyver over”. Og håndtere dråbet, når det sker.

En udvidet kode på onMouseMove for at finde “droppable” -elementer:

I eksemplet nedenfor, når bolden trækkes over fodboldmålet, er målet fremhævet.

Nu har vi det aktuelle “drop target”, som vi flyver over, i variablen currentDroppable under hele processen og kan bruge det til at fremhæve eller andre ting.

Oversigt

Vi betragtede en grundlæggende Drag’n’Drop-algoritme.

Nøglekomponenterne:

  1. Begivenhedsflow: ball.mousedowndocument.mousemoveball.mouseup (glem ikke at annullere native ondragstart).
  2. Ved trækstart – husk markørens indledende skift i forhold til elementet: shiftX/shiftY og hold det under trækningen.
  3. Registrer kasserbare elementer und er markøren ved hjælp af document.elementFromPoint.

Vi kan lægge meget på dette fundament.

  • mouseup vi kan intellektuelt færdiggøre dråben: ændre data, flytte elementer rundt.
  • Vi kan fremhæve de elementer, vi flyver over.
  • Vi kan begrænse trækning efter et bestemt område eller retning.
  • Vi kan bruge begivenhedsdelegering til mousedown/up. En begivenhedshåndterer med stort område, der kontrollerer event.target, kan administrere Drag’n’Drop for hundredvis af elementer.
  • Og så videre.

Der er rammer, der bygger arkitektur over det: DragZone, Droppable, Draggable og andre klasser. De fleste af dem gør lignende ting som beskrevet ovenfor, så det skal være let at forstå dem nu. Eller rul dig selv, da du kan se, at det er let nok at gøre, nogle gange lettere end at tilpasse en tredjepartsløsning.

Write a Comment

Din e-mailadresse vil ikke blive publiceret. Krævede felter er markeret med *