A Drag’n’Drop nagyszerű interfész megoldás. Valaminek a felvétele és az elhúzása egyértelmű és egyszerű módja sok dolognak, a dokumentumok másolásától és áthelyezésétől (mint a fájlkezelőknél) a megrendelésig (elemek kosárba dobása).
A modern HTML-ben alapértelmezés szerint van egy szakasz a Húzás és a speciális eseményekről, például dragstart
, dragend
és így tovább.
Ezek az események lehetővé teszik számunkra a különféle drag’n’drop támogatását, például egy fájl áthúzását az OS fájlkezelőből és a böngésző ablakába való dobást. Ekkor a JavaScript hozzáférhet az ilyen fájlok tartalmához.
De a natív Drag Eseményeknek is vannak korlátai. Például nem akadályozhatjuk meg az elhúzást egy bizonyos területről. Nem tehetjük meg, hogy a húzás csak “vízszintes” vagy “függőleges”. És még sok más drag’n’drop feladat van, amelyeket nem lehet felhasználni. Ezenkívül a mobileszközök támogatása az ilyen eseményekhez nagyon gyenge.
Tehát itt megtudjuk, hogyan lehet megvalósítani a Drag’n’Drop egéreseményeket.
Drag’n’Drop algoritmus
Az alap Drag’n’Drop algoritmus a következőképpen néz ki:
-
mousedown
– előkészíti az elemet mozgatásra, ha szükséges (esetleg hozzon létre belőle egy klónt, adjon hozzá osztályt vagy bármi mást). - Ezután
mousemove
tovább helyezheti a a következővel:position:absolute
. -
mouseup
– a drag’n befejezésével kapcsolatos összes művelet végrehajtása ‘drop.
Ezek az alapok. Később megtudhatjuk, hogyan lehet más funkciókat, például kiemelni az aktuális mögöttes elemeket, miközben áthúzzuk őket.
Itt van a labda húzásának megvalósítása:
Ha futtatjuk a kódot, valami furcsát észlelhetünk. A drag’n’drop elején a labda “elágazik”: elkezdjük húzni a “klónját”.
Íme egy példa a műveletben:
Próbálja meg húzni az egérrel az egeret, és ilyen viselkedés lesz látható.
Ez azért van, mert a böngésző saját drag’n’drop támogatással rendelkezik a képekhez és néhány más elem. Automatikusan fut, és ütközik a miénkkel.
Kikapcsolásához:
Most minden rendben lesz.
Műveletben:
Egy másik fontos szempont – mousemove
-t a document
-en követjük, nem pedig a ball
-en. Első látásra úgy tűnhet, hogy az egér mindig a labda felett van, és rátehetjük az mousemove
-t.
De mint emlékszünk, mousemove
gyakran vált ki, de nem minden pixelre. Tehát gyors mozgás után a mutató ugrani tud a labdáról a dokumentum közepén (vagy akár az ablakon kívül).
Tehát hallgatnunk kell a document
hogy elkapja.
Helyes pozícionálás
A fenti példákban a labdát mindig úgy mozgatjuk, hogy középpontja a mutató alatt legyen:
Nem rossz, de van mellékhatása. A drag’n’drop megindításához mousedown
bárhol a labdán elhelyezhetjük. De ha “kiveszi” az éléből, akkor a labda hirtelen “ugrik”, hogy az egérmutató alatt középre kerüljön.
Jobb lenne, ha megtartanánk az elem kezdeti elmozdulását a mutatóhoz képest.
Ha például a labda szélénél fogva kezdünk el húzni, akkor a mutatónak az élen kell maradnia húzás közben.
Frissítsük algoritmusunkat:
A végső kód jobb elhelyezéssel:
Működésben (belül <iframe>
) :
A különbség különösen akkor érzékelhető, ha a labdát annak jobb alsó sarkánál fogva húzzuk. Az előző példában a labda “ugrik” a mutató alá. Most folyékonyan követi a mutatót az aktuális helyzetből.
Potenciális esési célok (leejthetők)
Az előző példákban a labda csak “bárhová” dobják le, hogy maradjanak. A való életben általában az egyik elemet vesszük, és a másikra dobjuk. Például egy “fájlt” egy “mappába” vagy valami mást.
Absztrakt módon egy “húzható” elemet veszünk, és a “dobható” elemre dobjuk.
Mi tudnia kell:
- hová esett az elem a Drag’n’Drop végén – a megfelelő művelet végrehajtásához,
- és lehetőleg ismerje a áthúzódik, hogy kiemelje.
A megoldás egyfajta érdekes és csak egy kicsit trükkös, szóval itt térjünk ki rá.
Mi lehet az első ötlet? Valószínűleg a mouseover/mouseup
kezelőket állítja be potenciális dobhatókra?
De ez nem működik.
A probléma az, hogy amíg mi húzáskor a húzható elem mindig a többi elem felett áll.Az egéresemények csak a legfelső elemen történnek, az alattuk levőkön nem.
Például az alábbiakban két <div>
elem látható, piros a kék tetején az egyik (teljesen lefedi). A kék eseményen nem lehet eseményt elkapni, mert a piros van a tetején:
Ugyanez egy húzható elemmel. A labda mindig fent van a többi elem felett, ezért események történnek rajta. Bármilyen kezelőt állítunk is alacsonyabb elemekre, azok nem fognak működni.
Éppen ezért a gyakorlatban nem működik az az eredeti elképzelés, hogy a kezelőket potenciális dobhatókra helyezzük. Nem fognak futni.
Szóval, mit kell tenni?
Van egy módszer, amelynek neve: document.elementFromPoint(clientX, clientY)
. Visszaadja a legtöbb beágyazott elemet az adott ablak-relatív koordinátákon (vagy null
, ha az adott koordináták nincsenek az ablakon).
Bármelyikben felhasználhatjuk egéresemény-kezelőink felismerik a mutató alatt a potenciálisan leejthetőt, így:
Felhívjuk figyelmét: el kell rejtenünk a labdát a (*)
hívás előtt. Ellenkező esetben általában ezeken a koordinátákon van egy gömbünk, mivel ez a mutató alatti legfelső elem: elemBelow=ball
. Tehát elrejtjük, és azonnal újra megjelenítjük.
Ezzel a kóddal bármikor ellenőrizhetjük, hogy melyik elem felett “repülünk” át. És kezeljük a dobást, amikor ez megtörténik. > onMouseMove
kiterjesztett kódja az “eldobható” elemek megtalálásához:
Az alábbi példában a labda a futballkapu fölé húzva a cél kiemelésre kerül.
Mostantól megvan a jelenlegi “drop target”, amelyen átrepülünk, a currentDroppable
változóban az egész folyamat során, és ezzel kiemelhetjük vagy egyéb dolgok.
Összegzés
Alapvető Drag’n’Drop algoritmust vettünk figyelembe.
A legfontosabb összetevők:
- Események folyamata:
ball.mousedown
→document.mousemove
→ball.mouseup
(ne felejtsd el lemondani a natívatondragstart
). - A húzás kezdetén – emlékezzen a mutató kezdeti elmozdulására az elemhez képest:
shiftX/shiftY
és tartsa a húzás alatt. - Az észrevehető elemeket észlelheti und tegye a mutatót a
document.elementFromPoint
használatával.
Sokat fektethetünk ezen az alapon.
- On
mouseup
intellektuálisan véglegesíthetjük a cseppet: megváltoztathatjuk az adatokat, elemeket mozgathatunk. - Kiemelhetjük azokat az elemeket, amelyeken átrepülünk.
- Mi korlátozhatja a húzást egy bizonyos terület vagy irány szerint.
- Használhatjuk az események delegálását a
mousedown/up
számára. Egy nagy területű eseménykezelő, amely ellenőrzi aevent.target
elemeket, több száz elem kezelésével képes kezelni a Drag’n’Drop elemet. - És így tovább.
Vannak olyan keretrendszerek, amelyek építenek rá architektúrát: DragZone
, Droppable
, Draggable
és más osztályok. Legtöbben a fent leírtakhoz hasonló dolgokat csinálnak, ezért most már könnyen érthetőnek kell lenniük. Vagy tekerje meg a sajátját, amint láthatja, hogy ez elég egyszerű, néha könnyebb, mint adaptálni egy harmadik féltől származó megoldást.