Javascript ja DOM

Harjoitellaan Javascriptin ja DOM-rajapinnan perusteita.

Mallivastaus (malli.zip)

Alkuvalmistelut

Hello World

  1. Tallenna käyttöösi valmis dokumenttipohja johonkin väliaikaishakemistoon.
    • Valmiiseen pohjaan on linkitetty myös htmlinspector, joka ilmoittelee konsolissa, jos kirjoitat html-syntaksia väärin
    • Voit tämän kurssin kaikissa tehtävissä hyödyntää htmlinspectoria. Voit sillä automaattisesti tarkistaa DOM-rajapinnan kautta luomasi html-rakenteen oikeellisuuden. Lisää html-sivusi loppuun rivi:
      <script src="http://appro.mit.jyu.fi/tools/htmlinspector/html-inspector.js"></script>
      

      Tarkistus kannattaa tehdä aina sen jälkeen, kun teet jotain muutoksia sivusi html-rakenteeseen. Tarkistuksen voit aktivoida aina tarvittaessa kutsulla:

      HTMLInspector.inspect();
  2. Pura paketti W:\TIEA2120-hakemiston alihakemistoon ohjaus2.
  3. Luo haluamallasi editorilla nanonano.js-tiedosto samaan kansioon tallentamasi dokumenttipohjan kanssa.
  4. Avaa editoriin ja Firefox-selaimeen Nanonanon kotisivu index.html.
  5. Linkitä nanonano.js HTML-dokumentin head-osaan script-elementillä.
    <script src="nanonano.js"></script>
  6. Kirjoita nanonano.js-tiedostoon ensin yksinkertaisin mahdollinen Javascript-ohjelma:
    "use strict";
    alert("Hello world");
    
  7. Lataa sivu uudelleen selaimessa. Jos kaikki meni ok niin näytölle pitäisi ilmestyä dialogi jossa lukee Hello world. Huom! Sivusi on oltava WWW-palvelimella (users.jyu.fi), jotta kaikki varmasti toimii kuten on tarkoitus.
  8. Dialogi ilmestyy jo ennen sivun sisältöä joten korjataan ohjelmaa sen verran, että sivu ilmestyy ensin ja vasta sitten dialogi. Siirrä alert-funktiokutsu window-luokan onload-tapahtumankäsittelijäfunktioon.
    "use strict"; // estää pahimpia virheitä
    
    // liitetään oma funktio window.onload-tapahtumaan, joka tapahtuu sitten kun koko html-dokumentti ja siihen liittyvät
    // kuvat ja muut tiedostot on ladattu
    window.addEventListener("load", function() {
      alert("Hello world");
    });
    
  9. Kokeile ladata sivu uudelleen (reload). Nyt dialogi ilmestyy vasta dokumentin latauduttua.
  10. Kommentoi (// tai /* */) alert-komento pois ennen seuraavia tehtäviä. Älä koskaan käytä alert-funktiota esim. virheilmoituksiin.

Otsikoiden numerointi

Muutellaan elementtien sisältöä

  1. Lisää uusi funktio numeroi ja kutsu tätä window.onload-tapahtuman käsittelijäfunktiossa.
  2. Hae numeroi-funktiossa kaikki h2-elementit sopivan nimiseen muuttujaan. Tämä onnistuu document.getElementsByTagName-metodilla. getElementsByTagName-funktio palauttaa aina NodeList-tyyppisen listan riippumatta siitä montako alkiota löytyi.

    NodeList toimii käytännössä kuten taulukko, mutta on ns. live eli DOM-puuhun mahdollisesti tehdyt muutokset heijastuvat suoraan kyseisen listan elementteihin.

    document.getElementsByTagName etsii aina koko DOM-puusta. Elementti.getElementsByTagName etsii aina kyseisen elementin alla olevasta dokumenttihaarasta.
  3. Haetaan h2-elementtien tekstit
    • Voit käydä NodeListin läpi perinteisellä for-silmukalla tai myös for..of-silmukalla
    • Yksittäisen h2-elementin NodeListista saat item-metodilla tai []-operaattorilla aivan kuten taulukosta.
    • Kokeile miten h2-elementin textContent-ominaisuus toimii. Sillä saat suoraan jonkun elementin koko tekstisisällön eikä tarvitse välittää erillisistä TextNodeista
      • Kunkin elementin sisällä oleva teksti muodostaa oikeasti oman TextNode-tyypin olionsa. TextNodeja voi olla myös useita
      • Textnoden saat tässä tapauksessa h2-elementin firstChild-attribuutista.
      • Varsinaisen tekstin (String-tyyppiä) saa nodeValue-attribuutista. Huomaa, että nodeValue antaa tekstisisällön vain textNode-tyypeiltä.
      • Yleensä riittää textContent-ominaisuuden käyttäminen eikä erillisistä textnodeista tarvi välittää
  4. Aluksi tekstin voit tulostaa konsoliin ja testata tuleeko sinne tekstiä. Arvoja voit myös tutkia debuggerilla.
  5. textContent-attribuuttiin voi myös asettaa arvoja. Lisää sivulle näkyviin kunkin nykyisen otsikon eteen vielä for-silmukassa käytetyn indeksin arvo. Kokeile. Jos teit silmukan for..of-versiona niin joudut nyt muuttamaan sen perinteiseksi for-silmukaksi.

Elementtien lisäys

Lisätään elementtejä sivulle dynaamisesti DOM-rajapinnan avulla.

Linkit tekstiksi linkkilistaan

Linkit tekstiksi a-elementin jälkeen

  1. Luo uusi funktio linkit ja kutsu sitä window.onload-käsittelijäfunktiossa.
  2. Merkitse id-ominaisuudella dokumentin lopussa oleva ul-elementti.
  3. Hae linkit-funktiossa id:llä varustettu ul johonkin muuttujaan.
  4. Pyydä kaikkia tämän elementin alla olevia a-elementtejä getElementsByTagName-metodilla.
  5. Tee silmukka ja hae kunkin a-elementin http-osoite href-ominaisuudesta ja tulosta se konsoliin. Tämä onnistuu getAttribute-metodilla.
  6. Luo osoitteesta uusi tekstisolmu createTextNode-metodilla. Miksi et voi tässä käyttää textContent-ominaisuutta? Voit kokeilla toteuttaa tätä textContentin avulla niin huomaat miksi se ei onnistu.
  7. Lisää luomasi textnode kyseisen a-elementin sisältävän li-elementin loppuun. Tarvitset parentNode-attribuuttia ja appendChild-metodia. Sinun pitää myös keksiä miten saat a-elementin kautta käsiisi li-elementin. Esim. parentNode.
  8. Kokeile toimiiko skripti. Ulkoasua voi parantaa lisäämällä tekstin alkuun vielä yhden välilyönnin. Koodin uudelleenkäytettävyyttä voi vielä parantaa siten, että funktion parametriksi laitetaan id, tämä id haetaan dokumentista ja tämän jälkeläisinä olevat linkit "tekstitetään".
  9. Lisää koodisi loppuun vielä kutsu htmlinspectoriin:
    HTMLInspector.inspect()

    Inspector ilmoittaa konsolissa, jos luomasi html-koodin rakenteessa on vikaa. Tavallista validaattoria ei voi käyttää, koska muutokset sivun sisältöön tapahtuvat dynaamisesti javascriptilla.

Tapahtumat

Tapahtumankäsittelijän lisääminen

  1. Luo johonkin kohtaan dokumenttia painike button-elementillä. Anna painikkeelle jokin id-arvo ja näkyvä teksti Hello world. Älä laita painiketta form-elementin sisään.
  2. Luo uusi funktio varoitus. Tapahtumankäsittelijänä toimivat funktiot ottavat aina yhden parametrin, jonka nimi on yleensä e.
    function varoitus(e) {
    console.log("Hello world!")
    }
    
  3. Lisää onload-tapahtuman käsittelijään koodi, joka hakee painikkeen väliaikaismuuttujaan. Tämä onnistuu document-olion getElementById-metodilla.
  4. Aseta painikkeelle click-tapahtuman käsittelijäksi funktio varoitus addEventListener-metodilla.
    painike.addEventListener("click", varoitus);

    When to use "use capture" in your event listeners

  5. Testaa painikkeen toimintaa.
  6. Muuta ohjelman toimintaa siten, että Hello world ilmestyy vain tuplaklikkauksesta (kts. HTML:n tapahtumat).
  7. Muuta tuplaklikkauksen käsittely takaisin tavalliseksi click-tapahtumaksi. Siirrä painikkeesi lomakkeen (form-elementti) sisään. Miten ohjelmasi toimii nyt?
    form action="">
      
      </form>
    • Lomakkeen sisällä oleva painike toimii lomakkeen lähetyspainikkeen (submit-painike). Oletuksena lomakkeen sisältö lähetetään www-palvelimelle osoitteeseen, joka on ilmoitettu form-elementin action-ominaisuudessa, jossa on ohjelma, joka käsittelee lomakkeen tiedot. Tällä kurssilla ei käsitellä www-palvelimeen liittyviä ohjelmia. Käytännössä selain vain lataa sivusi uudelleen.
    • Jos halutaan javascriptilla käsitellä lomake, niin täytyy estää lomakkeen lähettäminen www-palvelimelle. Tämä tehdään preventDefault-metodilla tapahtumankäsittelijänä toimivassa funktiossa:
      function varoitus(e) {
          e.preventDefault();
          ...  
        }
        
      Samalla metodilla voidaan estää minkä tahansa tapahtuman oletustoiminta.

Menu

Avautuva menu

Tehdään yksinkertainen menu, jossa alakohtia voidaan piilottaa. Tässä tarvitaan tapahtumankäsittelyä.

  1. Luo dokumentin alkuun HTML:ää kirjoittamalla kaksitasoinen sisäkkäinen lista, jossa on pääkohdat Artikkelit, Harkat ja Linkit ja näiden alle pari listakohtaa, joiden sisällä on linkit. Kts. edellä oleva kuva. Lisää näiden listakohtien eteen img-elementillä minus.jpg-kuva merkitsemään avattua menukohtaa.
  2. Merkitse ulompi lista sopivalla id-attribuutilla.
  3. Luo nanonano.js-tiedostoon uusi funktio muuta_nakyvyys ja aseta funktion ensimmäisen parametrin nimeksi e tai event.
  4. Hae window.onload-tapahtumankäsittelijässä edellä määrittelemälläsi id:llä varustettu ul-elementti johonkin muuttujaan document.getElementById-funktiolla. Tämä funktio palauttaa aina yhden elementin.
  5. Pyydä kaikkia tämän ul-elementin alla olevia img-elementtejä getElementsByTagName-metodilla.
  6. Käy silmukalla img-elementtilista läpi ja lisää kunkin elementin click-tapahtuman käsittelijäksi funktio muuta_nakyvyys.
    elementti.addEventListener("click", muuta_nakyvyys);
  7. Selvitä muuta_nakyvyys-funktion alussa mikä elementti on aiheuttanut tapahtuman. Tämä selviää muuta_nakyvyys-funktiolle parametrina tulevan event-olion target-attribuutista. Voit esim. kokeilla mitä tekee console.log(event.target.nodeName) tai katsoa debuggerilla mitä event-parametri pitää sisällään.
  8. Muuta kuvan (event.target) src-attribuutin arvoksi plus.jpg. Kts. setAttribute. Kokeile selaimella.
  9. Lisää logiikkaa ja aseta src-attribuutiksi plus.jpg jos ennen oli minus.jpg, muutoin toisinpäin. Muuta myös alt-attribuutin tekstiksi Auki/Kiinni tilan mukaan. Testaa toimintaa.
  10. Tutki Inspectorilla millainen puu sisäkkäisestä listasta muodostuu.

    HTML inspector firebugissa - vanha versio

  11. Selvitä, miten pääset käsiksi tapahtuman aiheuttanutta img-elementtiä seuraavaan (sibling) ul-elementtiin. Tapoja on monia, voit joutua käyttämään esim. joitain seuraavista attribuuteista/metodeista: parentNode, childNodes, nextSibling, nodeName ja getElementsByTagName (kts. elementtien hakeminen).
  12. Piilota sisempi ul-elementti samalla kun asetat src-attribuutin arvoksi plus.jpg, muutoin näytä elementti.
    • Lisää CSS-tiedostoon luokka, jossa on ominaisuus display: none;. Käytä tätä luokkaa javascript-koodissasi
      .piilota { 
        display: none; 
      }
    • Elementin näkyvyyttä voi muuttaa esimerkiksi className-attribuuttia muokkaamalla. Voit classNamen avulla ottaa elementissä käyttöön haluamasi css-tyylin.
    • Elementin saa näkyväksi poistamalla class-attribuutin. Tämä onnistuu removeAttribute-metodilla.
  13. Poista vielä navigointipalkin uloimman listan alkioiden pallukat. Tämä onnistuu helpoiten manipuloimalla näiden elementtien css-ominaisuuksia.
    elementti.style.display = "block";
    Vinkki: käy silmukalla läpi navigointipalkin lapsielement (childNodes) ja tee muutoksia niille, joiden nodeName on LI. getElementsByTagName-metodia voit myös käyttää, mutta tässä tulee ongelma. Hoksaatko mikä?
  14. Voit vielä liu'uttaa (float: right;) koko listan sivun oikeaan laitaan.

Yhteenlaskupeli

Rivejä joissa kaksinumeroisia lukuja ja tekstikenttä niiden summaa varten

Tehdään Nanonanon sivuille päässälaskun harjoitteluun pieni yhteenlaskutehtäviä generoiva peli.

  1. Lisää h2-otsikko ja sen alle div-lohko, jonka id-ominaisuuden arvo on laskut. Lisää myös painike Tarkista, jolla on myös oma id-ominaisuuden arvonsa. Huom. id ei saa alkaa numerolla.
  2. Tee funktio luo_lasku, joka ottaa parametrinä rivin numeron ja palauttaa p-elementin, joka sisältää arvotun laskutehtävän tekstinä ja tekstilaatikon vastaukselle. Tuotettu elementti on muotoa:
    <p><span id="ekaluku_riviX">LUKU1</span> + <span id="tokaluku_riviX">LUKU2</span>
     = <input id="summa_riviX" type="text" size="3" /></p>
    • Elementin runko-osan saa luotua helposti DOMTool-työkalulla. Kopioi elementit HTML-osaan ja napauta Create DOM Statements. Kopioi DOM-koodi js-tiedostoosi. Palautettava elementti löytyy Nodes to Append -osasta.
    • Muuta id-attribuuttien asettamista siten, että X:n tilalle tulee parametrina annettu numero.
    • Arvo tekstien LUKU1 ja LUKU2 tilalle jotkin luvut väliltä 0-99. Tämä onnistuu Math.random-metodilla.
  3. Tee funktio luo_laskut, joka luo kymmenen laskua silmukassa ja lisää ne laskut-div-lohkon sisälle. Kutsu luo_lasku-funktiota silmukan kierrosluvun arvolla. Lisää luo_laskut-funktiokutsu window.onload-tapahtuman käsittelijään.
  4. Tee CSS-tiedostoon uusi luokka .virhe, joka muuttaa elementin taustavärin punaiseksi ja tekstin värin mustaksi.
  5. Tee funktio tarkista_laskut. Lisää tämä funktio window.onload-tapahtuman käsittelijässä tarkista-napin click-tapahtuman käsittelijäksi.
  6. Käy tarkistuksessa kaikki rivit läpi silmukassa ja tarkista ovatko kaikki laskut oikein.
    • Summaa luvut yhteen ja vertaile onko luku sama kuin tekstilaatikossa.
      • Luvut sisältävät elementit saat document.getElementById-metodilla. Luo lukuja sisältävien elementtien id-arvoja vastaavat merkkijonot käyttämällä vakiomerkkijonoja "ekaluku_rivi", "tokaluku_rivi" ja "summa_rivi" perään silmukan kierroksen numero. Esim. luot merkkijonon "ekaluku_rivi0", jotta voit pyytää tätä vastaavaa riviä document.getElementById-metodilla.
        let luku1 = document.getElementById("ekaluku_rivi" + nro);
      • Tekstin saa textContent- tai nodeValue-attribuutilla. Tekstikenttään (input-elementti) syötetyn arvon saa value-ominaisuudesta. Huom. ei html-koodin attribuutista vaan DOM-objektin value-ominaisuudesta.
      • Summausta varten luvut on muutettava integer-tyyppisiksi parseInt-funktiolla
    • Jos laskettu luku ei ole oikein, niin laita tekstilaatikon class-attribuuttiin arvo virhe.
    • Jos rivi on kunnossa, niin poista class-attribuutti
    • Jos kaikki rivit ovat ok, niin ilmoita painikkeen alapuolella sopivalla tekstillä pelin läpipääsystä.
  7. Testaa "pelin" toimintaa.

Jos aikaa jäi, niin kokeile vielä toteuttaa uuden pelin arpominen ja vastaamiseen kuluneen ajan laskeminen pelin uudelleenaloituksesta hyväksyttyyn tarkistukseen.

Mallivastaus (malli.zip)

Käyttäjien kommentit

Kommentoi Lisää kommentti
Kurssimateriaalien käyttäminen kaupallisiin tarkoituksiin tai opetusmateriaalina ilman lupaa on ehdottomasti kielletty!
https://appro.mit.jyu.fi/tiea2120/ohjaus/ohjaus2/
© Tommi Lahtonen (tommi.j.lahtonen@jyu.fi) <https://hazor.iki.fi/>
2020-10-08 10:10:25
Informaatioteknologia - Jyväskylän yliopiston informaatioteknologian tiedekunta