Drag’n’Drop on loistava käyttöliittymäratkaisu. Jotain ottaminen ja vetäminen ja pudottaminen on selkeä ja yksinkertainen tapa tehdä monia asioita asiakirjojen kopioimisesta ja siirtämisestä (kuten tiedostohallinnoissa) tilaamiseen (tavaroiden pudottaminen ostoskoriin).
Nykyaikaisessa HTML-muodossa vakiona on vedä ja pudota -osiossa erityistapahtumia, kuten dragstart
, dragend
ja niin edelleen.
Näiden tapahtumien avulla voimme tukea erityisiä drag’n’drop -tyyppejä, kuten tiedoston vetämisen käsitteleminen OS-tiedostonhallinnasta ja pudottaminen selainikkunaan. Sitten JavaScript voi käyttää tällaisten tiedostojen sisältöä.
Mutta alkuperäisillä vetotapahtumilla on myös rajoituksia. Emme voi esimerkiksi estää vetämistä tietyltä alueelta. Emme myöskään voi tehdä vetämistä vain ”vaaka” tai ”pystysuora”. Ja on monia muita vedä-tehtäviä, joita ei voi tehdä niiden avulla. Myös mobiililaitteiden tuki tällaisille tapahtumille on hyvin heikkoa.
Joten tässä kerrotaan, miten Drag’n’Drop toteutetaan hiirtapahtumien avulla.
Drag’n’Drop -algoritmi
Drag’n’Drop-perusalgoritmi näyttää tältä:
- Päällä
mousedown
– valmistele elementti siirrettäväksi, jos tarvitaan (ehkä luo siitä klooni, lisää siihen luokka tai mikä tahansa). - Sitten
mousemove
siirrä sitä muuttamallaleft/top
japosition:absolute
. - Käytössä
mouseup
– Suorita kaikki vetämisen lopettamiseen liittyvät toiminnot pudota.
Nämä ovat perusasiat. Myöhemmin näemme, miten muita ominaisuuksia, kuten nykyisten taustalla olevien elementtien korostaminen vetämällä niitä.
Tässä on pallon vetämisen toteutus:
Jos suoritamme koodin, voimme huomata jotain outoa. Vedä pudotuksen alussa pallo ”haarautuu”: alamme vetää sen ”kloonia”.
Tässä esimerkki toiminnassa:
Yritä vetää hiirtä hiirellä ja näet tällaisen käyttäytymisen.
Tämä johtuu siitä, että selaimella on oma drag’n’drop-tuki kuville ja joitain muita elementtejä. Se toimii automaattisesti ja on ristiriidassa meidän.
Voit poistaa sen käytöstä:
Nyt kaikki on kunnossa.
Toiminnassa:
Toinen tärkeä näkökohta – seuraamme mousemove
-kohtaa document
, ei ball
. Ensi silmäyksestä voi tuntua siltä, että hiiri on aina pallon päällä, ja voimme laittaa siihen mousemove
.
Mutta kuten muistan, mousemove
laukaisee usein, mutta ei jokaiselle pikselille. Joten nopean liikkeen jälkeen osoitin voi hypätä pallosta jonnekin asiakirjan keskelle (tai jopa ikkunan ulkopuolelle).
Joten meidän pitäisi kuunnella document
saadaksesi sen kiinni.
Oikea sijoittelu
Yllä olevissa esimerkeissä pallo siirretään aina niin, että sen keskikohta on osoittimen alla:
Ei paha, mutta sillä on sivuvaikutus. Aloita vedä pudotus valitsemalla mousedown
missä tahansa pallossa. Mutta jos ”otat” sen reunaltaan, pallo yhtäkkiä ”hyppää” keskittyäkseen hiiren osoittimen alle.
Olisi parempi, jos pidämme elementin alkuperäisen siirtymän osoittimeen nähden.
Jos esimerkiksi aloitamme vetämisen pallon reunasta, osoittimen tulisi pysyä reunan yli vedettäessä.
Päivitetään algoritmiamme:
Viimeinen koodi paremmalla sijainnilla:
Toiminnassa (sisällä <iframe>
) :
Ero on erityisen havaittavissa, jos vedämme palloa sen oikeasta alakulmasta. Edellisessä esimerkissä pallo ”hyppää” osoittimen alle. Nyt se seuraa sujuvasti osoittinta nykyisestä sijainnista.
Potentiaaliset pudotuskohteet (pudotettavat)
Aiemmissa esimerkeissä pallo voisi pudotetaan vain ”minne tahansa” pysyäksesi. Tosielämässä otamme yleensä yhden elementin ja pudotamme sen toiseen. Esimerkiksi ”tiedosto” kansioon tai jokin muu.
Abstraktia puhetta varten otamme ”vedettävän” elementin ja pudotamme sen ”pudotettavaan” elementtiin.
Me täytyy tietää:
- mihin elementti pudotettiin Drag’n’Dropin lopussa – tehdä vastaava toiminto,
- ja mieluiten tuntea pudotettava vedämme sen päälle korostaaksesi sitä.
Ratkaisu on tavallaan mielenkiintoinen ja vain vähän hankala, joten käsitellään sitä tässä.
Mikä voi olla ensimmäinen idea? Luultavasti asetat mouseover/mouseup
-käsittelijät potentiaalisille pudotettaville?
Mutta se ei toimi.
Ongelmana on, että vaikka olemme vetämällä elementti on aina muiden elementtien yläpuolella.Ja hiirtapahtumat tapahtuvat vain ylimmällä elementillä, ei sen alapuolella olevilla.
Esimerkiksi alla on kaksi <div>
-elementtiä, punainen yksi sinisen päällä. yksi (peittää kokonaan). Sinisellä tapahtumaa ei ole mahdollista saada kiinni, koska punainen on päällä:
Sama vedettävällä elementillä. Pallo on aina muiden elementtien päällä, joten siellä tapahtuu tapahtumia. Riippumatta siitä, mitä käsittelijöitä asetamme alemmille elementeille, ne eivät toimi.
Siksi alkuperäinen ajatus laittaa käsittelijät potentiaalisiin pudotettaviin ei toimi käytännössä. Ne eivät toimi.
Joten, mitä tehdä?
On olemassa menetelmä nimeltä document.elementFromPoint(clientX, clientY)
. Se palauttaa kaikkein sisäkkäisimmän elementin annetuilla ikkuna-suhteellisilla koordinaateilla (tai null
, jos annetut koordinaatit eivät ole ikkunassa).
Voimme käyttää sitä missä tahansa hiirtapahtumankäsittelijämme havaitsevat potentiaalisen pudotettavan osoittimen alla, näin:
Huomaa: Meidän on piilotettava pallo ennen puhelua (*)
. Muuten meillä on yleensä pallo näillä koordinaateilla, koska se on osoittimen alla oleva ylin elementti: elemBelow=ball
. Joten piilotamme sen ja näytämme heti uudelleen.
Voimme käyttää koodia milloin tahansa tarkistamaan, minkä elementin yli lennämme. Ja käsittelemme pudotusta, kun se tapahtuu.
Laajennettu koodi onMouseMove
”pudotettavien” elementtien löytämiseksi:
Alla olevassa esimerkissä, kun pallo vedetään jalkapallomaalin yli, tavoite korostetaan.
Nyt meillä on nykyinen ”pudotuskohde”, jonka yli lennämme, muuttujassa currentDroppable
koko prosessin ajan ja voimme käyttää sitä korostamaan tai kaikki muut asiat.
Yhteenveto
Pidimme Drag’n’Drop-perusalgoritmia.
Avainkomponentit:
- Tapahtumien kulku:
ball.mousedown
→document.mousemove
→ball.mouseup
(älä unohda peruuttaa natiiviaondragstart
). - Vedä aloitettaessa – muista osoittimen alkusiirto suhteessa elementtiin:
shiftX/shiftY
ja pidä se vetämisen aikana. - Tunnista pudotettavat elementit erota osoitin käyttämällä
document.elementFromPoint
.
Voimme laittaa paljon tälle perustalle.
- Päällä
mouseup
voimme viimeistellä pudotuksen älyllisesti: muuttaa tietoja, siirtää elementtejä ympäri. - Voimme korostaa elementit, joiden yli lennämme.
- Me voi rajoittaa vetämistä tietyllä alueella tai suunnassa.
- Voimme käyttää tapahtuman delegointia
mousedown/up
. Suuren alueen tapahtumankäsittelijä, joka tarkistaaevent.target
, voi hallita Drag’n’Dropia satojen elementtien osalta. - Ja niin edelleen.
On olemassa kehyksiä, jotka rakentavat sen päälle arkkitehtuurin: DragZone
, Droppable
, Draggable
ja muut luokat. Suurin osa heistä tekee samanlaisia asioita kuin yllä on kuvattu, joten heidän pitäisi olla helppo ymmärtää niitä nyt. Tai rullaa omasi, sillä huomaat, että se on tarpeeksi helppoa, joskus helpompaa kuin kolmannen osapuolen ratkaisun mukauttaminen.