14 KiB
Terrarium-projekti Osa 3: DOM-manipulointi ja sulkeuma
Sketchnote: Tomomi Imura
Ennakkokysely
Johdanto
DOM-manipulointi eli "Document Object Model" on keskeinen osa verkkosivujen kehittämistä. MDN:n mukaan "Document Object Model (DOM) on verkkosivun rakenteen ja sisällön muodostavien objektien tietoesitys." DOM-manipuloinnin haasteet ovat usein johtaneet siihen, että JavaScript-kehyksiä käytetään sen hallintaan sen sijaan, että käytettäisiin pelkkää JavaScriptiä. Tässä projektissa pärjäämme kuitenkin ilman kehyksiä!
Lisäksi tässä oppitunnissa esitellään JavaScript-sulkeuma, jonka voi ajatella olevan funktio, joka on suljettu toisen funktion sisään, jolloin sisempi funktio pääsee käsiksi ulomman funktion laajuuteen.
JavaScript-sulkeumat ovat laaja ja monimutkainen aihe. Tässä oppitunnissa käsitellään perusidea, joka liittyy terrariumin koodiin: sulkeuma, jossa sisempi funktio ja ulompi funktio on rakennettu siten, että sisempi funktio pääsee käsiksi ulomman funktion laajuuteen. Lisätietoja aiheesta löytyy laajasta dokumentaatiosta.
Käytämme sulkeumaa DOM-manipulointiin.
Ajattele DOM:ia puuna, joka edustaa kaikkia tapoja, joilla verkkosivun dokumenttia voidaan manipuloida. Erilaisia API:ita (Application Program Interfaces) on kirjoitettu, jotta ohjelmoijat voivat käyttää DOM:ia ja muokata, järjestellä ja hallita sitä haluamallaan tavalla.
DOM:n ja siihen viittaavan HTML-merkinnän esitys. Lähde: Olfa Nasraoui
Tässä oppitunnissa viimeistelemme interaktiivisen terrarium-projektimme luomalla JavaScriptin, joka mahdollistaa kasvien siirtämisen sivulla.
Esitietovaatimukset
Sinulla tulisi olla terrariumin HTML ja CSS valmiina. Oppitunnin lopussa pystyt siirtämään kasveja terrariumiin ja sieltä pois vetämällä niitä.
Tehtävä
Luo terrarium-kansioon uusi tiedosto nimeltä script.js
. Tuo tämä tiedosto <head>
-osioon:
<script src="./script.js" defer></script>
Huom: käytä
defer
-attribuuttia tuodessasi ulkoisen JavaScript-tiedoston HTML-tiedostoon, jotta JavaScript suoritetaan vasta, kun HTML-tiedosto on ladattu kokonaan. Voisit myös käyttääasync
-attribuuttia, joka sallii skriptin suorittamisen HTML-tiedoston jäsentämisen aikana, mutta tässä tapauksessa on tärkeää, että HTML-elementit ovat täysin saatavilla ennen kuin vetämistoiminto suoritetaan.
DOM-elementit
Ensimmäinen tehtäväsi on luoda viittaukset DOM:ssa oleviin elementteihin, joita haluat manipuloida. Meidän tapauksessamme nämä ovat 14 kasvia, jotka odottavat sivupalkissa.
Tehtävä
dragElement(document.getElementById('plant1'));
dragElement(document.getElementById('plant2'));
dragElement(document.getElementById('plant3'));
dragElement(document.getElementById('plant4'));
dragElement(document.getElementById('plant5'));
dragElement(document.getElementById('plant6'));
dragElement(document.getElementById('plant7'));
dragElement(document.getElementById('plant8'));
dragElement(document.getElementById('plant9'));
dragElement(document.getElementById('plant10'));
dragElement(document.getElementById('plant11'));
dragElement(document.getElementById('plant12'));
dragElement(document.getElementById('plant13'));
dragElement(document.getElementById('plant14'));
Mitä tässä tapahtuu? Viittaat dokumenttiin ja etsit sen DOM:sta elementin, jolla on tietty Id. Muistatko ensimmäisestä HTML-oppitunnista, että annoit yksilölliset Id:t jokaiselle kasvikuvalle (id="plant1"
)? Nyt hyödynnät tätä työtä. Kun olet tunnistanut jokaisen elementin, välität sen funktiolle nimeltä dragElement
, jonka rakennat hetken kuluttua. Näin HTML-elementti on nyt vedettävissä, tai tulee olemaan pian.
✅ Miksi viittaamme elementteihin Id:n avulla? Miksi emme CSS-luokan avulla? Voit palata edelliseen CSS-oppituntiin vastataksesi tähän kysymykseen.
Sulkeuma
Nyt olet valmis luomaan dragElement
-sulkeuman, joka on ulompi funktio, joka sulkee sisemmän funktion tai funktiot (meidän tapauksessamme niitä on kolme).
Sulkeumat ovat hyödyllisiä, kun yksi tai useampi funktio tarvitsee pääsyn ulomman funktion laajuuteen. Tässä esimerkki:
function displayCandy(){
let candy = ['jellybeans'];
function addCandy(candyType) {
candy.push(candyType)
}
addCandy('gumdrops');
}
displayCandy();
console.log(candy)
Tässä esimerkissä displayCandy
-funktio ympäröi funktion, joka lisää uuden karkkityypin jo olemassa olevaan taulukkoon. Jos suorittaisit tämän koodin, candy
-taulukko olisi määrittelemätön, koska se on paikallinen muuttuja (paikallinen sulkeumalle).
✅ Kuinka voit tehdä candy
-taulukon saatavilla? Kokeile siirtää se sulkeuman ulkopuolelle. Näin taulukosta tulee globaali, eikä se jää vain sulkeuman paikalliseen laajuuteen.
Tehtävä
Luo script.js
-tiedoston elementtien määrittelyjen alle funktio:
function dragElement(terrariumElement) {
//set 4 positions for positioning on the screen
let pos1 = 0,
pos2 = 0,
pos3 = 0,
pos4 = 0;
terrariumElement.onpointerdown = pointerDrag;
}
dragElement
saa terrariumElement
-objektinsa skriptin yläosassa tehdyistä määrittelyistä. Sitten asetat joitakin paikallisia sijainteja 0
:ksi funktiolle välitetylle objektille. Nämä ovat paikallisia muuttujia, joita manipuloidaan jokaiselle elementille, kun lisäät vetämis- ja pudotustoiminnallisuuden sulkeuman sisällä. Terrarium täytetään näillä vedettävillä elementeillä, joten sovelluksen täytyy pitää kirjaa siitä, mihin ne sijoitetaan.
Lisäksi funktiolle välitetty terrariumElement
-objekti saa pointerdown
-tapahtuman, joka on osa web-API:ita, jotka on suunniteltu auttamaan DOM:n hallinnassa. onpointerdown
laukeaa, kun painiketta painetaan, tai meidän tapauksessamme, kun vedettävää elementtiä kosketetaan. Tämä tapahtumankäsittelijä toimii sekä web- että mobiiliselaimissa, muutamia poikkeuksia lukuun ottamatta.
✅ Tapahtumankäsittelijä onclick
on paljon laajemmin tuettu eri selaimissa; miksi et käyttäisi sitä tässä? Mieti tarkasti, millaista ruudun vuorovaikutusta yrität luoda tässä.
Pointerdrag-funktio
terrariumElement
on valmis vedettäväksi; kun onpointerdown
-tapahtuma laukeaa, funktio pointerDrag
kutsutaan. Lisää tämä funktio heti tämän rivin alle: terrariumElement.onpointerdown = pointerDrag;
:
Tehtävä
function pointerDrag(e) {
e.preventDefault();
console.log(e);
pos3 = e.clientX;
pos4 = e.clientY;
}
Useita asioita tapahtuu. Ensinnäkin estät oletustapahtumat, jotka normaalisti tapahtuvat pointerdown-tapahtumassa, käyttämällä e.preventDefault();
. Näin sinulla on enemmän hallintaa käyttöliittymän käyttäytymisestä.
Palaa tähän kohtaan, kun olet rakentanut skriptitiedoston kokonaan, ja kokeile ilman
e.preventDefault()
- mitä tapahtuu?
Toiseksi avaa index.html
selaimessa ja tarkastele käyttöliittymää. Kun napsautat kasvia, näet kuinka 'e'-tapahtuma tallennetaan. Tutki tapahtumaa nähdäksesi, kuinka paljon tietoa kerätään yhdestä pointerdown-tapahtumasta!
Seuraavaksi huomaa, kuinka paikalliset muuttujat pos3
ja pos4
asetetaan arvoon e.clientX. Voit löytää e
-arvot tarkastelupaneelista. Nämä arvot tallentavat kasvin x- ja y-koordinaatit sillä hetkellä, kun napsautat tai kosketat sitä. Tarvitset tarkkaa hallintaa kasvien käyttäytymisestä, kun napsautat ja vedät niitä, joten pidät kirjaa niiden koordinaateista.
✅ Alkaako olla selvempää, miksi koko sovellus rakennetaan yhdellä suurella sulkeumalla? Jos ei olisi, kuinka ylläpitäisit laajuutta jokaiselle 14 vedettävälle kasville?
Täydennä alkuperäinen funktio lisäämällä kaksi muuta pointer-tapahtuman käsittelyä pos4 = e.clientY
-rivin alle:
document.onpointermove = elementDrag;
document.onpointerup = stopElementDrag;
Nyt ilmoitat, että haluat kasvin liikkuvan osoittimen mukana, kun siirrät sitä, ja että vetämisliike pysähtyy, kun lopetat kasvin valinnan. onpointermove
ja onpointerup
ovat kaikki osa samaa API:ta kuin onpointerdown
. Käyttöliittymä heittää nyt virheitä, koska et ole vielä määritellyt elementDrag
- ja stopElementDrag
-funktioita, joten rakenna ne seuraavaksi.
elementDrag- ja stopElementDrag-funktiot
Viimeistelet sulkeumasi lisäämällä kaksi sisäistä funktiota, jotka käsittelevät, mitä tapahtuu, kun vedät kasvia ja lopetat sen vetämisen. Haluttu käyttäytyminen on, että voit vetää mitä tahansa kasvia milloin tahansa ja sijoittaa sen mihin tahansa ruudulla. Tämä käyttöliittymä on melko joustava (esimerkiksi pudotusaluetta ei ole), jotta voit suunnitella terrariumin juuri haluamallasi tavalla lisäämällä, poistamalla ja siirtämällä kasveja.
Tehtävä
Lisää elementDrag
-funktio heti pointerDrag
-funktion sulkevan aaltosulkeen jälkeen:
function elementDrag(e) {
pos1 = pos3 - e.clientX;
pos2 = pos4 - e.clientY;
pos3 = e.clientX;
pos4 = e.clientY;
console.log(pos1, pos2, pos3, pos4);
terrariumElement.style.top = terrariumElement.offsetTop - pos2 + 'px';
terrariumElement.style.left = terrariumElement.offsetLeft - pos1 + 'px';
}
Tässä funktiossa muokkaat paljon alkuperäisiä sijainteja 1-4, jotka asetettiin paikallisiksi muuttujiksi ulommassa funktiossa. Mitä tässä tapahtuu?
Kun vedät, määrität pos1
:n uudelleen tekemällä siitä yhtä suuri kuin pos3
(jonka asetit aiemmin arvoksi e.clientX
) miinus nykyinen e.clientX
-arvo. Teet samanlaisen operaation pos2
:lle. Sitten asetat pos3
:n ja pos4
:n uudelleen elementin uusiin x- ja y-koordinaatteihin. Voit seurata näitä muutoksia konsolissa vetämisen aikana. Sitten muokkaat kasvin css-tyyliä asettaaksesi sen uuden sijainnin perustuen uusiin pos1
- ja pos2
-sijainteihin, laskemalla kasvin ylä- ja vasemman x- ja y-koordinaatin sen offsetin perusteella.
offsetTop
jaoffsetLeft
ovat CSS-ominaisuuksia, jotka asettavat elementin sijainnin sen vanhemman elementin perusteella; vanhempi elementti voi olla mikä tahansa, joka ei ole asetettustatic
-sijaintiin.
Kaikki tämä sijainnin uudelleenlaskenta mahdollistaa terrariumin ja sen kasvien käyttäytymisen hienosäädön.
Tehtävä
Viimeinen tehtävä käyttöliittymän viimeistelemiseksi on lisätä stopElementDrag
-funktio elementDrag
-funktion sulkevan aaltosulkeen jälkeen:
function stopElementDrag() {
document.onpointerup = null;
document.onpointermove = null;
}
Tämä pieni funktio nollaa onpointerup
- ja onpointermove
-tapahtumat, jotta voit joko aloittaa kasvin siirtämisen uudelleen tai aloittaa uuden kasvin vetämisen.
✅ Mitä tapahtuu, jos et aseta näitä tapahtumia nulliksi?
Nyt olet valmis projektiisi!
🥇Onnittelut! Olet viimeistellyt kauniin terrariumisi.
🚀Haaste
Lisää uusi tapahtumankäsittelijä sulkeumaasi, jotta kasveille tapahtuisi jotain muuta; esimerkiksi kaksoisnapsauta kasvia tuodaksesi sen etualalle. Ole luova!
Jälkikysely
Kertaus ja itseopiskelu
Vaikka elementtien vetäminen ruudulla vaikuttaa yksinkertaiselta, on olemassa monia tapoja tehdä tämä ja monia sudenkuoppia riippuen halutusta efektistä. Itse asiassa on olemassa kokonainen drag and drop API, jota voit kokeilla. Emme käyttäneet sitä tässä moduulissa, koska haluamamme efekti oli hieman erilainen, mutta kokeile tätä API:a omassa projektissasi ja katso, mitä saat aikaan.
Lisätietoja pointer-tapahtumista löytyy W3C-dokumenteista ja MDN-web-dokumenteista.
Tarkista aina selainten yhteensopivuus CanIUse.com-sivustolla.
Tehtävä
Työskentele hieman lisää DOM:n kanssa
Vastuuvapauslauseke:
Tämä asiakirja on käännetty käyttämällä tekoälypohjaista käännöspalvelua Co-op Translator. Pyrimme tarkkuuteen, mutta huomioithan, että automaattiset käännökset voivat sisältää virheitä tai epätarkkuuksia. Alkuperäistä asiakirjaa sen alkuperäisellä kielellä tulee pitää ensisijaisena lähteenä. Kriittisen tiedon osalta suositellaan ammattimaista ihmiskääntämistä. Emme ole vastuussa tämän käännöksen käytöstä aiheutuvista väärinkäsityksistä tai virhetulkinnoista.