Kahjuks pole jõudnud kirjutada oma blogis vastavat puhastusfunktsiooni soovimatute blogitäienduste vältimiseks, mistõttu HTML näidistes on </body> ja </html> lõpetamata ">" märgiga.
HTML-st, mida enam ei ole
Nagu näete - HTML -st ei ole alles peaaegu mitte midagi.
Kõik, mis vaja, joonestab JavaScript ise valmis. Ajutiselt lülitasin välja
viite "css" failile, millega praegu ei tegele. Lisaks märkate "mõttetut" div
märgist. See on kasulik mitmel põhjusel: selle div-le on antud id "TTTII" ja nii
on seal sees tekkivaid-kaduvaid asju võimalik kergesti kätte saada.
Selle HTML teksti võib tõenäoliselt tõsta mõne teise html sisse, ilma, et mäng lakkaks töötamast.
Täpsustav märkus
Päris nii kahjuks ei ole (isikliku näite varal), minu enda kirjutatud endise TTTI mängu objektid segavad siin asja.
Märkused JavaScript aadressil
Nagu nägite - HTML -st ei jäänud suurt midagi alles, aga JavaScript koodi
saime pea 50 rida juurde.
See ei ole isegi mitte patt. Koodiridadega, kui need tunduvad vajalikud, ei maksaks koonerdada.
Ka funktsioone on lisandunud juurde. Ka see ei ole patt, kuigi mitte alati ka voorus.
Kui funktsioon on vajalik ja täidab kindlat funktsiooni ja mitte midagi muud, siis tuleb see luua.
Funktsioonide deklareerimisest
Mulle meeldib mõelda ülevalt alla stiilis ja nii tundub mugavam.
See tähendab, et kõigepealt kirjutame valmis mallina, mis ehk midagi ei teegi,
kõige üldisemad, suuremad operatsioonid. Näiteks looManguLaudTTT() võiks olla selline operatsioon. Funktsioonide deklareerimise stiilis kirjutamisel ei ole sellest lugu, kui pudiludi funktsioonid tulevad suure funktsiooni järel, aga funktsiooniavaldiste puhul var funkts = function() {...}; tekivad probleemid, saame teateid, et mingi funktsioon on "undefined". Seetõttu ma praegu eelistan seda stiili lihtsamate programmide kirjutamisel. Keerukamatega nii ei saa, sest seal tuleb funktsioone kirjutada üksteise sisse ja deklaratiivne stiil (vist enam) sellisel juhul ei toimi. Kuidas sellisel juhul loetavat JavaScripti kirjutada, kasutades funktsiooniavaldisi, üritan välja uurida ja siis soovitusi jagada.
Muutuvaim asi ilmas - KONSTANDID
// KONSTANDID:
var KANVAA = "kanvaa";
var VOIDUKOMBINATSIOONID = [[0,1,2],[3,4,5],[6,7,8],[0,3,6],[1,4,7],
[2,5,8],[0,4,8],[2,4,6]];
Siin on muutuvad suurused kirjutatud üles suurte tähtedega. See on iidne konventsioon tähistamaks asju,
mille kohta on kujunenud veendumus - need asjad iial ei muutu.
Kahjuks on see veendumus osutunud peaaegu alati valeks
Kõige muutuvamad suurused ilmas on "KONSTANDID" ja kõige püsivamad nähtused need,
mille kohta armastatakse mõelda "AJUTINE".
Ettenägelikud programmeerijad seetõttu mitte kordagi ei jäta programmi sisse peaaegu mitte
ühtegi arvu ja asendavad nad suurte tähtedega kirjutatud muutujatega päris programmi alguses,
et kui läheb jälle suureks muutmiseks ja ümbertegemiseks, oleks neid võimalik muuta.
Lisaks on inimkeeles ära seletatud konstantidel see hea omadus, et nad on teisele programmeerijale
arusaadavad. Kui kirjutada mingi asja kohta 3.141592, siis vähesed siin maailmas oskavad kohe öelda,
et see võiks olla PI. Aga kui selle koha peal ongi Math.PI, siis on kõigile selge, kes koolis käinud,
et see on see kuulus PII.
Sissejuhatus objektide maailma
// Objekt mang (mäng)
var mang = {
sisu :[-1,-1,-1,-1,-1,-1,-1,-1,-1],
kaik : 0
};
Kommenteerin siis nüüd ridu, mis tekitavad objekti mang:
Kuigi siin oleks olemas juba võimalus märku anda oma
teadmiste pakatamisest OO (ObjektOrienteerituse) vallas, jätan selle targu
tegemata. Praegu kasutan objekte lihtsalt selleks, et oma koodilasu
grupeerida.
Mis on objekt ?
Objekt on muutuja, mis võib endas sisaldada mitmesuguseid
omadusi.
"Omadus" omakorda on tegelikult igas muus suhtes täieõiguslik muutuja, v.a. see asjaolu, et
ta on seotud oma objektiga (ülemusega). Minu koodis TTT I
sisu[] oli täieõiguslik massiiv.
Praeguses koodis tekitasin ma objekti mang ja selle alla paigutasin selle massiivi.
Nüüd sisu enam päris omaette ei ole, tema väärtusi ilma viitamata ülemusobjektile kätte ei saa.
sisu[0] muutub nüüd viiteks mang.sisu[0].
sisu ise on muuseas ka objekt, sisaldades oma elemente, nii et objekt võib sisaldada alamobjekte,
need omakorda alamobjekte ja nii edasi...
Samasuguse degradatsiooni tegime ära muutujaga
kaik.
kaik ja
sisu on nüüd objektmuutuja
mang OMADUSED.
Nagu arvata võib, saab objekti
mang omadustega
manipuleerida samamoodi, nagu tavaliste muutujatega,
näiteks saab
mang omadusele
kaik ühte juurde liita:
(
mang.kaik++ teeb seda )
Märkus
Kui päris täpne olla, on kõik muutujad sirvikus algusest peale ühe suure globaalobjekti
"window" alluvuses, nii et eelmises variandis oli
sisu window alam, nüüd sai siis ülemuseks
mang
Selles mõttes on analoogia DOM puuga, kõik asjad JavaScripti interpretaatori jaoks moodustavad ühe
suure objektipuu ja nn. mitteobjektid on siiski vaid konventsioon tavaprogrammeerijale.
Need mitteobjektid (string, number, loogiline andmetüüp ning eraldi "undefined" ja "null") lihtsalt on teatud piiratud võimalustega objektid.
Näiteks string jaoks on olemas meetod
.length, aga pole võimalusi igasuguseid omadusi juurde teha.
Objektide loomine
Objektide loomiseks ja täiendamiseks on mitu võimalust.
Ühte nägite selles koodis:
Sama võib aga saavutada ka nn. punktnotatsiooni kasutades.
Kõigepealt loome tühja objekti
ja seejärel lisame
omadusi ja nende väärtusi, või ka juba muudame neid.
sellist notatasiooni kasutades:
OBJEKT . OMADUSE NIMI = OMADUSE VÄÄRTUS
Objekt mang omistused võiksid välja näha nii:
Kolmas võimalus oleks
omadus:väärtus
paare lisada, kasutades massiividega sarnanevat süntaksit, ainult et massiivi
indeksi asemel on objekti omadus, kirjutatuna jutumärkides.
Edaspidi tahaksin objekt mang alla sokutada ka kõik selles mängus vajaminevad funktsioonid või vähemalt enamik neist.
Sündmuste ettearvamatu maailm: window.onload-st ja teistest juhtumustest
veebilehekülgedel
See rida koodis -
window.onload = function() ...
tundub esimesel pilgul üsna krüptiline.
Selle lause sisu aga midagi üleloomulikku ei ole - käsk on SEE tegevus edasi lükata
hetkeni, kui lehekülg laaditud.
Nii näetegi selle kirjutise laadimisel kummalisi asju juhtuvat ekraanil - algul tekib pealkiri
ja isegi selgitav tekst selle juurde ja ALLES SIIS tekitab "nähtamatu käsi" ekraanile 9 kanvaad ja saate hakata
oma lemmikmängu mängima.
Selle kohta ütlevad netigurud, et see sündmus toimub
ASÜNKROONSELT. Ja otse vastupidist juhtumit,
kus kõik ilmub veebileheküljele nii, nagu veebilooja on kirjutanud,
ülevalt alla, algul "head-s" sisalduv, seejärel body sisu,
nimetatakse SÜNKROONSEKS.
See terminoloogia kahjuks on väga eksitav, sest
pigem just asünkroonselt toimuvad sündmused on sujuvamad, kasutaja saab samal ajal,
kui server ajab kusagilt netist reklaami taga, juba tegelda lehekülje sisuga.
NN. sünkroonselt toimuvate sündmuste puhul tuleks netilehekülje laadimise
ajaks minna kohvi keetma, täpselt samamoodi, nagu teleka reklaamipauside puhul.
Ja ärge imestage, et teie veebileheküljed muutuvad selles mõttes järjest "sünkroonsemaks":
kes maksab, see tellib ka muusika. Kui reklaami tellija on ähmaselt taibanud, et
vaid "sünkroonne" mudel tagab reklaami 100%-lise jõudmise kasutaja ajudesse,
saavad kõik programmeerijad delfis ja mujal kiiresti kinga, kui nad ainult julgevad iitsatada
mõttest käitada autoreklaam TAGAPLAANIL (Väga hea näide on jutjuubis toimunud
saastumine).
Nii et ärge laske ennast eksiteele viia ebaõnnestunud terminitest -
arvutimaailmas kahjuks tänase
päevani ei ole olemas sünkroonsust, sekundilise täpsusega käivitatavaid sündmusi
kindlas, ettenähtud järjestuses.
Termin Asünkroonne aga vastab enam-vähem ettekujutletavale - võite selle kohta
mõelda: nii nagu jumal juhatab...
Netilehekülje laadimisel toimub veel tänini asi enam-vähem sünkroonselt: sirvik loeb rida rea
haaval leheküljelt elemente ja käske ja täidab neid nii, nagu nad ette tulevad. Seetõttu võib
lehekülje laadimine viibida: näiteks on päises viide javascripti failile ja selle laadimine võtab aega.
Sellise olukorra vältimiseks võikski programmeerija vahel lasta toimida asünkroonselt, kui tema
koodi toimimine ei sõltu sellest, kas ülejäänud lehekülg on üles laetud või mitte.
Ja otse vastpidi on siis, kui kood tegeleb selle lehekülje alles üleslaetava elemendiga, siis tuleb see osa koodist
edasi lükata, kasutades sündmust windows.onload - selle sündmuse külge poogitud funktsioon käivitub alles siis, kui lehekülg on laaditud.
Blogis tajute seda imeliku viitena, mängulaud ilmub alles kõikide teiste elementide järel.
Kui aga lehekülg on laaditud, ei ole mingist sünkronismist mõtet isegi mitte mõelda. Kõik, mis toimub,
toimub juba asünkroonselt. Enamasti on sündmuste tekitajaks kasutaja, aga on võimalikud ka välised sündmused,
mis mõnda tegevust käivitavad - näiteks programmides ikka esinevad vead.
Nii ongi ülejäänud kood sõltuv kasutaja suvast, sellest millal ja mismoodi ta klõpsib 9 kanvaa ruutudel.
Näiteid võimalikest sündmustest
Sündmusi veebilehe elus võib olla väga palju, allpool ma natuke loetlen neid asj,
mida üsna tõenäoliselt tuleb kasutada.
Entsüklopedistid võtku ette juba mõni "REFERENCE MANUAL", kahjuks olete jõudmas sinnamaale,
kus neist enam pääseda ei ole võimalik. Isegi Flanagani tuleks väga ettevaatlikult hakata veerima.
Wiki, "Dom events" märksõna alt leiab ka päris korraliku tabeli.
Aga loodame pääseda kõigist sündmustest ja tegeleme kõige tähtsamatega.
Enne loetelu juurde asumist märgime, et kui sündmuse tüüp on näiteks click, siis selle sündmuse halduri
külgevõtmiseks tuleks kasutada onclick nimetust (button.onclick = ... ja siin tuleb funktsioon, mis midagi teeb).
Veel moodsamal moel jälle peab kasutama sündmuse tüübi nimetust click umbes nii:
el.addEventListener("click", hallo_maailm, false);
(hallo maailm on funktsioon, mis hõigub ilmakuulsat programmilauset - "Hello World", seda iga programmeerimisega alustanu on kohanud ad nauseam)
Selle selgituseni, mida see krüptika teeb, tahaks jõuda veidi hiljem, mängulaua loomise juures, siin lihtsalt
memoriseerimise mõttes jätke meelde, et kaks lihtsamat viisi sündmuse halduri paikapanekuks kasutavad eesliidet "on" sündmuse tüübile lisaks, ja kõigetäiuslikum meetod jälle viitab sündmusele selle tüüpi kasutades.
Ja IE-st parem ei räägi, see sirvik teeb seda kõike omamoodi. Versioon 11 väidetavalt enam ei tee omamoodi ja
äkki saab sellest ajastust lahti, kus pool manuaali läheb alati selle peale ära, kus selgitatakse, kuidas teevad teised sirvikud ja teise poole peal selgitatakse, mida sellest arvab IE.
Kui aga sellest lahti ei saa, aitab JavaScripti abipakett nimega JQUERY, sellest tn. ei pääse keegi isegi siis,
kui IE korralikuks hakkab. Kuid ma praegu lükkan JQUERY-ga tegelemise edasi. Sisulise mõistmise mõttes oleks vaja ka allolevast masinavärgist aru saada. Aga IE veidruste peale aega ei raiska. (Seda peate aga tn. hakkama kohe tegema, kui reaalsete rakenduste tegemiseks läheb.)
Tähtsamad võimalikud sündmused veebilehe elus:
- click / onclick - käivitub, kui elemendi peal on klikatud
- dblclick / ondblclick - topeltklõps...
- mousedown /onmousedown - hiire nupp vajutatud alla (peab eraldi uurima veel, mis nupp)
- mouseup / onmouseup - nupp lastud üles tagasi
- mouseover / onmouseover - hiirega libisetud elemendile peale
- igasugu drag ja drop sündmusi ei viitsi loetleda
- keydown / onkeydown klaveri klahv vajutatud alla
keyup / onkeyup klaveri klahv vabas positsioonis tagasi
keypress / onkeypress - klahv vajutatud alla.
- unload / onunload - sündmus ilmselt leheküljelt lahkumisel.
- ja nii edasi ja edasi, küllap kodeerimispraktika edenedes jõuab need asjad kõik sõrmede sisse tampida,
ja kui ei jõua, ei ole neid asju järelikult vaja.
Mängulaua loomisest
Vaatleksime siis, kuidas JavaScriptis luua HTML elemente ja neile omadusi
külge pookida.
Mängulaua joonistamise võinuks siin muidugi jätta ka HTML teksti hooleks, sest
see ei muutu mängus, v.a. ristide ja nullide kustutamine, mis kindlalt oleks JavaScripti töö. Kuid võiks olla ka nii, et iga mäng tuleb joonistada erinev laud - ütleme siis, kui tahetakse mängida vahel ka 4x4 laual...
Päris hakatuseks on näha, et põhifunktsioon midagi sisulist ei teegi, ainult jagab korraldusi.
Kui inimühiskonnas sooviksime, et ka ülemused vahel midagi sisulist teeksid, siis programmide sootsiumis on selline nähtus AINULT TERVITATAV.
Kood tuleb niimoodi pulkadeni lahti kirjutada, et reaalsed töötegijad tõesti täidaksid vaid äärmuseni lihtsustatud ülesandeid, ei midagi enamat. Umbes nagu Ford 20. sajandi alguses leiutas konveiermeetodi.
looManguLaudTTT() tött öelda siiski ühte teist teeb, tõlkides ülesande arvutikeelde.
(Ka inimühiskonnas sageli ülemused siiski teevad kasulikku tööd, tõlkides alamuste jaoks arusaadavasse keelde asju, millest arusaamiseks ei ole meile aru antud.)
Esialgne ülesanne:
Joonista ruudustik 3x3, iga ruut kujutagu omaette kanvaad.
Iga kanvaad ümbritsegu punane äärejoon.
Esimene tõlge:
Joonista 3 rida kanvaasid, igas reas 3 kanvaad ja sellejärel reavahetus.
Iga kanvaad ümbritsegu punane äärejoon
Panek arvutikeelde (lühendatud, täistekst on juba kood ise):
Tee 3x for tsükli abiga kanvaarea joonistamist,
kus kanvaarea joonistamine tähendab 3x kanvaa joonistamist,
pluss reavahetus,
ning kanvaa numbriks saab reanumbri (0-2) ja veerunumbri (1-3)
summa.
Ilma programmeerimise aluseid oskamata oleks väga raske taibata, et selleks,
et midagi teha n korda, tuleks luua muutuja i ning iga i väärtuse jaoks
0...,(n-1) sedasama asja teha.
Aga selline on arvuti arusaamine ülesandest "tee midagi n korda".
Ja see on alles "kannatuste raja" algus, sest sama ülesande saaks kirja panna rekursiivselt
või koguni kõrgemat järku funktsioone kasutades....
Funktsioonist looKanvaa
Elemendi tekitamine arvutiekraanile TEGELIKULT toimub kahes järgus.
- Kõigepealt element tuleb luua:
Seda teeb käsk kanvaa = createElement('canvas')
Sulgudes tuleb ära märkida, mis tüüpi elementi tahetakse tekitada.
Pärast loomisakti tasuks loodule kohe kõik vajalikud atribuudid juurde pookida, kuigi seda võib igal ajal teha.
Atribuutide nimed HTML-s ja loodavad elemendi omadused JavaScriptis enam-vähem on üks ühele ümber pandavad.
("." järel tuleb loodava HTML elemendi atribuudi nimi).
Aga!:JavaScriptis on need ALATI väikeste tähtedega, kui ühesõnalised ja järgmine
sõnaalustus suurega, kui atribuudi nimi koosneb mitmest sõnast (vt. all näidet)
Aga! style="border:..." asemel on siin .style.border =...
Võinuks ka vanaviisi, s.t. .style=....
Aga! Mitmeosalised atribuudid HTML-s kaotavad JavaScriptis ära "-" kahe sõna vahelt
ning vormindatakse ära JavaScripti stiilselt, s.t. esimene sõna väikse tähega,
järgmine algab alati SUURE tähega.
text-align muundub nii textAlign-ks.
Aga! 2 atribuuti HTML-s ja üks CSS-s on JavaScriptis reserveeritud sõnad:
HTML atribuut class muundub className-ks, for muundub
htmlFor-ks ja float CSS-st muundub cssFloat-ks.
- KUI nüüd element on loodud, tuleb ta DOM puu külge pookida.
DOM - Document Object Model on viis HTML lehekülgi arvutile arusaadavaks teha. Kogu loodud HTML lehekülje elemendistik paigutatakse puusse, v.t. minu sissejuhatava jutu lõpp HTML-st.
Lühimeenutusena: kõige alustus, puu juur on html, html-l on lapsed "head" ja "body". "head" järglased võiksid olla näiteks "meta" - märgised, aga ka "title", "script" märgised ehk programmid lehekülje jaoks, ja nii edasi...
"body" lasteks on juba lehe sisu, näiteks võiks tulla kohe "p" (paragrahv", "img" ja nii edasi. Ja peaaegu igal elemendil võib olla "järglasi" ...
DOM puusse istutamiseks tuleb otsida element, kellele loodud laps sokutada.
Praegu oleme isarolli täitjaks määranud elemendi "div" id-ga "TTTII".
Lõplikuks elemendi sünnimomendiks saabki siis selle elemendi puusseistutamise käsk:
kanvaaIsa.appendChild(kanvaa);
appendChild() asemel on veel võimalus panna loodud laps mõnele teisele kohale isaelemendi laste järjestuses. Seda toimetab käsk insertBefore(viide elemendile).
Ka juba loodud elemente saab nii ümber paigaldada.
Kes tunneb hästi Piiblit, teab, et kõik autoriõigused sünnijärjestuse muutmise mõtte osas kuuluvad Jaakobile, kes Eesavilt esisünniõiguse välja kauples läätseleeme eest.
Kliki käitlemise 3 viisi JavaScriptis
Iga sündmuse lehekülje elus (hiireklikk, nupuvajutus etc...) saab programselt seostada mingi tegevuse / tegevustega. Seondamiseks on 3 erinevat võimalust.
- Ajalooliselt vanim on meetod, mida kasutatakse TTT I variandis - "canvas" elemendi juurde
kirjutatud onclick="kanvaaKlikk(x)".
x asemel on siis kanvaa number 1-9.
Kanvaa klikkamisel tõepoolest käivitub see funktsioon ja ka parameeter x
on edastatud iga kord õieti. Võite aga proovida, ühe funktsiooni asemele võib kirjutada ka mitu käsku, kusjuures need isegi ei pea olema kirjas mingis skriptifailis. Võtke näiteks lihtsaim HTML ilma "js", "css" lisadeta, lisage paragrahv tekstiga "hallo, maailm" ja kirjutage üheks paragrahvi atribuudiks onclick="alert('hallo');alert('maailm');". Kõik toimib.
Tegelikult teeb Javascript onclick juurde kirjutatud stringist FUNKTSIOONI,
lisades stringi algusesse sõna function() ning ümbritsedes stringi loogeliste sulgudega. "hallo, maailm" paragrahvi näites saadakse paragrahvi klikkamise puhul käivitavaks funktsiooniks function() {alert('hallo');alert('maailm');}
TTT I variandis ei kasutata mitte ühte ja sama funktsiooni 9 erineva parameetri väärtusega 1-9, vaid luuaksea iga kanvaa puhul klikkamiseks eraldi funktsioonid ,
kanvaa 1 puhul siis function() {kanvaaKlikk(1);}
...
kanvaa 9 puhul function() {kanvaaKlikk(9);}.
-
TTT II, hetkel kehtiva mängu variandi korral loome elemendid
kanvaa1...kanvaa9 JavaScriptis ning nende elementide sündmuse klikihalduri
peab määrama JavaScriptis.
kanvaa.onclick =function()
{ kanvaaKlikk(kanvaaNumber);};
teebki seda, "kanvaa.onclick" omaduseks on funktsiooni kood.
Märkus - katsed siia külge riputada I meetodis kasutatud stringe, näiteks
"kanvaaKlikk(1)" , ei toimi. Vastava omaduse väärtuseks saab null
Kui funktsioon on süntaktiliselt vigane, saame sama tulemuse.
Võrreldes TTT I variandiga antakse kanvaaKlikk funktsioonile kaasa
muutuja kanvaaNumber väärtus. JavaScriptis saab seda teha, muutuja
kanvaaNumber on nähtav kogu funktsiooni looKanvaa ulatuses (ja mittenähtav naabruses
asuvatele funktsioonidele). Nähtavus laieneb ka kõigile selle funktsiooni all loodud funktsioonidele, siin siis funktsioonile kanvaa.onclik.
kanvaaNumber on tõsi, parameeter, aga niipea, kui parameeter on jõudnud funktsiooni, on ta oma staatuselt täpselt samaväärne muutuja.
Kuulge, siin on midagi mäda, võib lugeja hüüatada, eriti mõni selline, kes peab
iseenesestmõistetavaks, et kui funktsioon lõpetab tegevuse, kaovad areenilt ka kõik selle funktsiooni sees defineeritud muutujad.
Ja koht, kus funktsioon välja kutsutakse, määrab selle, mida see funktsioon näeb.
Talle peaks nähtavad olema kõik väljakutse hetkel kättesaadavad muutujad.
Sellist asja nimetatakse veel DÜNAAMILISEKS SKOOBIKS ja kes sellega harjunud,
sellel on ümberõppimine päris valulik (k.a. mina).
Aga JavScriptis nii ei ole. Kõigepealt ei tea funktsioon tema väljakutsuja
skoobis kehtivatest muutujatest midagi, v.a. globaalne skoop...
Selle asemel peab funktsioon meeles mingeid noaaegseid
muutujaid sellest kohast ja ajastust, millal ta defineeriti.
Ja ta ei näe mitte midagi sellest ümbrusest, kus ta välja kutsuti!
PS! Veel kord: näeb siiski globaalskoopi, mistõttu see kanapimedus ei paista kohe välja!
Sellest võib mõelda umbes nii.
Olgu meil mingi funktsioon F, mille sees luuakse uus
funktsioon f (Praegu looKanvaa()).
Praegu on funktsiooniks f sündmuse "click" haldur loodava kanvaa jaoks.
Loodud funktsioon f jätab meelde kõikide muutujate väärtused, mis tema looja F-le on kättesaadavad. Neist globaalsed muutujad j ä t k a v a d oma muutumist funktsiooni f jaoks,
kui tal peaks neid tarvis minema, aga kõik F-i jaoks lokaalsete muutujate väärtused jäetakse mällu selle ajahetke väärtustega.
Nii jääbki "kanvaa1" klikihaldurile igaveseks tempel mällu muutujast
kanvaaNumber väärtusega 1. Hiljem kutsutakse looKanvaa() funktsiooni veel 8 korda välja, luues
elemendid kanvaa2 ... kanvaa9.
Muutuja kanvaaNumber jõuab lõpuks omadega väärtuseni 9. See kõik
kanvaa1 klikihaldurit ei häiri, tema jaoks jääb kanvaaNumber väärtuseks 1.
Üks üsna vastik nüanss on veel selle kõige juures: ülesvõtte hetk. Klõps käib siis,
kui funktsioon lõpetab oma töö. Kui meil oleks tulnud pähe mõte parameetrit
kanvaaNumber muuta, näiteks enne töö lõppu teha käsk kanvaaNumber++;,
saaksime paraja segaduse - 1. ruudul klikkides joonistatakse 0 või x teise ruutu.
Veel pöörasemaks läheksid asjad, kui sündmuse haldur ise otsustab kanvaaNumber väärtust muuta ütleme kanvaaNumber++ käsuga, siis teisel ja rohkematel klõpsudel sellel kanvaal hakkaksid klõpsude järgsed ristide ja 0-de joonistused toimuma järjest suurema järjenumbriga ruutudel (kuni 9-ni)...
Hea oleks, kui te praegu lepiksite tõsiasjaga - mingi imevalemiga jäävad kanvaade
klikihalduritele meelde muutuja kanvaaNumber väärtused 1-9. Ühel ilusal päeval aga
põrkute nende nähtustega jälle kokku.
Nende teemade ümber jahumist tähistatakse sõnadega
"Leksikaalne skoop" ehk "Lexical scope" (funktsioon näeb seda ümbrust, kus ta on defineeritud) ning "Sulund" elik "Closure". Funktsioonidest on saanud "sulundid", kes tassivad kaasa tema loomise ajal eksisteerinud temast ülalpool olevate muutujate väärtusi. Tõeline "sulund" kasutab neid väärtusi, tavapärane ei kasuta, kuigi võiks seda iga kell teha. Nii võib kohata vaheldumisi väiteid, et iga funktsioon on sulund ja väiteid, et näe, see funktsoon ikka tõeline sulund ei ole...
Nii et leksikaalses skoobi korral funktsioonid sarnanevad inimestega, kes ühest firmast teise minnes põhilises säilitavad need hoiakud, mis kaasa antud koolist ja ülikoolist.
Erinevalt inimestest sellised funktsioonid lausa põhimõtteliselt ei taha midagi teada
oma "uutest väljakutsetest" ja asjaoludest, miks seda väljakutset tehti...
Iga tervemõistuslik firmajuht annaks sellisele tegelasele üsna pea kinga.
Funktsioonidega nii ei ole, "sulund" on tänapäeval väga populaarne nähtus,
hoolimata püsivast peavalust, mida professionaalse slängiga mittekohanenud progejale sellega on võimalik tekitada.
- Meie praegusteks vajadusteks piisab täiesti sündmuste halduri programmeerimisest
kujul x.onclick = function() {kood siin};. Sellest hoolimata võivad ette tulla
olukorrad, kus see enam nii ei ole. Eelkõige puudutab see vajadust siduda ühe ja sama
sündmusega mitu haldusprogrammi.
Näiteks window.onload-ga sidumine võiks olla selline sündmus. Kui kirjutada TTT II mängu
omaette, käivitades seda oma arvutis, siis selline vajadus puudub. Aga näiteks minu Trips Trapsi versioonid
pidanuks juba sellega arvestama (ja tõtt öelda eriti ei arvestanud, ma lihtsalt lootsin, et blogikeskkond ehk oskab selliste amatööride eest ennast piisavalt hästi kaitsta).
Minu blogis võinuks blogikeskkond vajada omi window.onload haldureid ning mina oleksin
võinud selle kergesti ära rikkuda. Tõsi, ainuke tõsisem kaotaja oleks olnud minu enese blogi,
mis poleks käivitunud normaalselt ja minema peletanud viimasedki lugejad, kes veel siin käivad.
Vanasti tehti selleks alati nii - funktsioon windows.onload haldur salvestati muutujasse
old_onload ning uus haldur pidi ära tegema kõigepealt oma töö ja seejärel käivitas selle
"old_onload"-i uue funktsiooni sees.
Nagu ma nüüdseks olen õppinud, nii enam minu (ja teiste amatööride õnneks) ei saa juhtuda, sest rohkem JavaScripti õppinud programmeerijad enam iial ei kasuta
minu poolt kasutatud sündmuste halduri sättimise viisi kujul window.onload = function() {};
Tõtt öelda on see meetod lausa taunitav (deprecated), viitav kas õppimisvõimetuse sündroomile, s.t. taunprogrammeerijale või algajale.
Praeguseks, isegi IE-s (vähemalt versioonis 11 peaks olema kõik OK) on nii, et sündmuste haldurile lisatöö lisamine toimub nii:
el.addEventListener(sündmuse tüüp, meil "click" või "load",
,funktsiooni NIMETUS või kood kujul function() {kood},
false - sellest hiljem, mida see tähendab)
Nüüd see sündmuse haldurprogramm vaid lisatakse olemasolevatele ja teiste töötlejate koodi ei eemaldata.
Kliki käitlemise 2 faasi
Sündmuse käitluses on veel üks nüanss - saladuslik 3. parameeter
addEventListener() meetodis, mis millegipärast on false.
See on seotud elementide paiknemisega üksteise sees. Igal elemendile on võimalik sättida
sündmuste haldurit ja kohe kerkib probleem - millist eelistada.
Ka TTT mängus on mängulaud div märgisega elemendi vahel, id TTTII.
Ka selle elemendi külge on võimalik lisada sündmuse käitlejat.
I faasis püütakse sündmus kõige ülemise elemendi halduri poolt kinni ning see annab
ülesande, kui see ei tundu küllalt tähtis, üle DOM puus alamatele elementidele.
Bürokraatia lõpeb kõige madalamas positsioonis paikneva elemendil.
Seda faasi nimetatakse püüdmisfaasiks (capture phase).
Selles faasis lisatavatele püüduritele või halduritele tuleb see kolmas parameeter sättida
true-ks.
Järgnevat faasi nimetatakse mullitamise faasiks (bubble phase).
Nüüd annavad sündmuste haldurid tulist kartulit edasi alt üles, järjest tähtsamatele elementidele
DOM hierarhias. Kõikidel selles faasis lisatavatel haldurprogrammidel peab see kolmas parameeter olema
false.
Ja kogu see muinasjutuline töötlus toimub vaid ühe hiirekliki jooksul!
Õnneks ei ole tegelikus elus neid mullistamisi ja püüdurfaase vist väga palju kasutatud, muidu ei õnnestuks
vist netis enam ühte hiireklikki ka teha, ilma et arvuti lootusetult kinni ei jookseks.
Ja IE vist veel ei ole hakanud püüdurfaasi kasutama, nii et peab piirduma sündmusemulli veeretamisega alt
üles. Ma usun, et selletagi on võimalik tekitada halle juukseid nii kasutajale kui ka programmeerijale.
Globaalsetest muutujatest, mida enam ei ole
Mõni probleem võib osutuda üllatavalt lihtsaks. Kirjutasin TTT II variandi
alguses, et "peab uurima, kuidas globaalsetest muutujatest lahti saada".
Minu blogirullis osutus see suureks probleemiks, kuna TTT I variandi kood suurel
määral on suguluses TTT II variandi koodiga ja seetõttu vähemalt blogirulli sees
pidin mõne funktsiooni ümber ristima, sest sirvik näeb korraga kõiki skripte, nii
TTT I koodi skripti, kui TTT II skripti. Nii et TEGELIKULT eesriide taga mängisite
natukene teistsugust TTT skripti, kui ma avalikult välja tõin - mitmed funktsioonid,
mis TTT I variandiga ühise nimega (aga veidi erineva koodiga), olid ümber ristitud.
Selgub muidugi, et see oli lahendatav KAHE (jah, KAHE!!) reaga. Kogu TTT II kood tuleks ümbritsega selliste ridadega:
ALGUSESSE KIRJUTAGE:
(function() {
// siia tuleb kõik see kood TTT II-st.
// Kõige lõpus aga on:
}());
Sellist funktsiooni nimetatakse
Kohe Käivituvaks FunktsiooniAvaldiseks,
originaalis Immediately Invoked Function Expression - IIFE
Algselt on selliste asjade mõte olnud vist pigem mingite kohe kohe käivitatavate
asjade loomine, mille jaoks nime ei tasugi raisata. Praegu aga on tegemist väga
mugava vahendiga, sest funktsiooni kesta sisse saab tegelikult kõike, mida
soovitakse
lõppkasutaja eest varjata, ära peita. Kõik muutujad on lokaalsed selle anonüümse,
nimeta objekti sees ja ometi üksteisele nähtavad, sest muutuja skoop või nähtavus
ongi funktsiooni sisene, kõik funktsioonid, mis selle näota ja nimeta funktsioni
sees, on ka üksteisele nähtavad ja kõige tähtsam - hiirekliklikile reageerivad
funktsioonid function() {kanvaaKlikk(xx);} samuti näevad kõike selle skoobi sees,
kuigi näotu funktsioon käivitus ju kohe ja lõpetas oma tegevuse.
See on nüüd üks kohene näide sellest, et algselt hirmsegasest asjast nimega SULUND
võib kohe tulu tõusta...
Mis veel rõõmustavam - JavaScripti sees joonistamine ja klikihaldurite paigaldamine
andis kohe praktilise väljundi - TTT I versioonis peab onclik atribuudis viidatud
kanvaaKlikk() olema avalikkusele nähtav funktsioon, sest sellele viidatakse HTML
teksti seest ja sinna anonüümse funktsiooni sisse peidetud seltskond välja ei paista.
1 Comments:
Väga huvitav ;)
Postita kommentaar
<< Home