Dra ”n” Släpp med mushändelser

Drag’n’Drop är en bra gränssnittslösning. Att ta något och dra och släppa det är ett tydligt och enkelt sätt att göra många saker, från att kopiera och flytta dokument (som i filhanterare) till att beställa (släppa artiklar i en kundvagn).

I den moderna HTML standard finns det ett avsnitt om dra och släpp med speciella händelser som dragstart, dragend och så vidare.

Dessa händelser tillåter oss att stödja speciella typer av drag’n’rop, som att hantera att dra en fil från OS-filhanteraren och släppa den i webbläsarfönstret. Då kan JavaScript komma åt innehållet i sådana filer.

Men infödda draghändelser har också begränsningar. Vi kan till exempel inte förhindra att vi drar från ett visst område. Vi kan inte heller bara dra ”horisontellt” eller ”vertikalt”. Och det finns många andra drag’n’drop-uppgifter som inte kan göras med dem. Stöd för mobila enheter för sådana händelser är också mycket svagt.

Så här ser vi hur man implementerar Drag’n’Drop med mushändelser.

Drag’n’Drop-algoritm

Den grundläggande Drag’n’Drop-algoritmen ser ut så här:

  1. mousedown – förbered elementet för rörelse, om behövs (kanske skapa en klon av den, lägg till en klass i den eller vad som helst).
  2. Sedan flyttar den på mousemove genom att ändra left/top med position:absolute.
  3. mouseup – utför alla åtgärder relaterade till att avsluta drag’n ’drop.

Det här är grunderna. Senare kommer vi att se hur andra funktioner, som att markera aktuella underliggande element medan vi drar över dem.

Här är implementeringen av att dra en boll:

Om vi kör koden, vi kan märka något konstigt. I början av drag’n’rop, ”gafflar” bollen: vi börjar dra dess ”klon”.

Här är ett exempel i handling:

Försök att dra och dra med musen så ser du ett sådant beteende.

Det beror på att webbläsaren har sitt eget stöd för drag och dropp för bilder och några andra element. Det körs automatiskt och strider mot vårt.

För att inaktivera det:

Nu går allt bra.

I aktion:

En annan viktig aspekt – vi spårar mousemovedocument, inte på ball. Från första ögonkastet kan det verka som att musen alltid är över bollen, och vi kan sätta mousemove på den.

Men som vi kommer ihåg, mousemove utlöses ofta, men inte för varje pixel. Så efter snabb rörelse kan pekaren hoppa från bollen någonstans mitt i dokumentet (eller till och med utanför fönstret).

Så vi borde lyssna på document för att fånga den.

Korrekt positionering

I exemplen ovan flyttas bollen alltid så att den är mitt under pekaren:

Inte illa, men det finns en bieffekt. För att initiera drag’n’drop kan vi mousedown var som helst på bollen. Men om ”ta” den från kanten så ”hoppar” bollen plötsligt för att bli centrerad under muspekaren.

Det vore bättre om vi behåller elementets inledande skift i förhållande till pekaren.

Om vi till exempel börjar dra i bollkanten bör pekaren förbli över kanten medan vi drar.

Låt oss uppdatera vår algoritm:

Den slutliga koden med bättre positionering:

I aktion (inuti <iframe>) :

Skillnaden märks särskilt om vi drar bollen i dess nedre högra hörn. I det föregående exemplet ”hoppar” bollen under pekaren. Nu följer den pekaren flytande från den aktuella positionen.

Potentiella droppmål (droppbar)

I tidigare exempel kunde bollen tappas bara ”var som helst” för att stanna. I verkligheten tar vi vanligtvis ett element och släpper det på ett annat. Till exempel en ”fil” i en ”mapp” eller något annat.

När vi talar abstrakt tar vi ett ”dragbart” element och släpper det på ”droppbart” element.

Vi behöver veta:

  • var elementet släpptes i slutet av Drag’n’Drop – för att utföra motsvarande åtgärd,
  • och helst känna till den droppbara vi drar över för att markera det.

Lösningen är ganska intressant och bara lite knepig, så låt oss täcka den här.

Vad kan vara den första idén? Förmodligen att ställa in mouseover/mouseup hanterare på potentiella droppables?

Men det fungerar inte.

Problemet är att medan vi är att dra, är det dragbara elementet alltid ovanför andra element.Och mushändelser inträffar bara på det övre elementet, inte på dem under det.

Till exempel är nedan två <div> -element, röd ovanpå det blå en (helt täcker). Det finns inget sätt att fånga en händelse på den blå, eftersom den röda är överst:

Detsamma med ett dragbart element. Bollen är alltid ovanpå andra element, så händelser händer på den. Oavsett vilka hanterare vi sätter på lägre element fungerar de inte.

Det är därför som den ursprungliga idén att sätta hanterare på potentiella droppables inte fungerar i praktiken. De körs inte.

Så vad ska man göra?

Det finns en metod som heter document.elementFromPoint(clientX, clientY). Det returnerar det mest kapslade elementet på givna fönsterrelaterade koordinater (eller null om givna koordinater är utanför fönstret).

Vi kan använda det i någon av våra mushändelsehanterare för att upptäcka potentialen som kan släppas under pekaren, så här:

Obs! Vi måste dölja bollen innan samtalet (*). Annars har vi vanligtvis en boll på dessa koordinater, eftersom det är det översta elementet under pekaren: elemBelow=ball. Så vi döljer det och visar omedelbart igen.

Vi kan använda den koden för att kontrollera vilket element vi ”flyger över” när som helst. Och hantera droppen när det händer.

En utökad kod för onMouseMove för att hitta ”droppbara” element:

I exemplet nedan när bollen dras över fotbollsmålet markeras målet.

Nu har vi nuvarande ”släppmål”, som vi flyger över, i variabeln currentDroppable under hela processen och kan använda den för att markera eller andra saker.

Sammanfattning

Vi betraktade en grundläggande Drag’n’Drop-algoritm.

Nyckelkomponenterna:

  1. Händelser flöde: ball.mousedowndocument.mousemoveball.mouseup (glöm inte att avbryta native ondragstart).
  2. Vid dragstart – kom ihåg den första skiftningen av pekaren i förhållande till elementet: shiftX/shiftY och behåll den under släpningen.
  3. Upptäck droppbara element und är pekaren med document.elementFromPoint.

Vi kan lägga mycket på den här grunden.

  • mouseup vi kan intellektuellt slutföra droppen: ändra data, flytta element runt.
  • Vi kan markera elementen vi flyger över.
  • Vi kan begränsa dragning efter ett visst område eller riktning.
  • Vi kan använda händelsedelegering för mousedown/up. En storhändelsehanterare som kontrollerar event.target kan hantera Drag’n’Drop för hundratals element.
  • Och så vidare.

Det finns ramar som bygger arkitektur över den: DragZone, Droppable, Draggable och andra klasser. De flesta av dem gör liknande saker som beskrivs ovan, så det bör vara lätt att förstå dem nu. Eller rulla själv, eftersom du kan se att det är enkelt att göra, ibland lättare än att anpassa en tredjepartslösning.

Write a Comment

Din e-postadress kommer inte publiceras. Obligatoriska fält är märkta *