A nyelvi bevezetők után ebben a fejezetben azzal kezdünk el foglalkozni, hogy hogyan lehet JavaScripttel a HTML dokumentum elemeit programozni. Egyelőre az alapkoncepcióval és az ehhez szükséges alapismeretekkel ismerkedünk meg.
Az előző fejezetekben két oldalról közelítettük meg a JavaScript nyelvet. Egyrészt megnéztük, hogy milyen hasonlóságokat találunk a C++ nyelvvel. Tettük ezt azért, hogy a C++ nyelvet ismerőket ismerős úton vezessük be a JavaScript nyelv világába. Láthattuk, hogy mind szintaktikájában, vezérlési szerkezeteiben, operátoraiban, adattípusaiban, magasabb szintű nyelvi elemeiben sok hasonlóság fedezhető fel. A nyelvi bemutatás másik fejezetében pedig inkább arra koncentráltunk, hogy mik azok a nyelvi sajátosságok, amelyek a JavaScript nyelvet jellemzik, és amelyek nagy sokoldalúságot adnak a nyelvnek. Ezeknek nagy része nem igazán ismerős egy C++ nyelvet ismerő programozó számára. Ugyanitt még megismerkedtünk pár hasznos JavaScript objektummal is, amelyek gyakran előforduló problémákra nyújtanak megoldást.
Noha a JavaScript nyelvvel önmagában sok érdekes feladat oldható meg, mégis alapvetően arra találták ki, hogy HTML dokumentumba ágyazva a weboldalakat dinamikussá tegye. Ebben a fejezetben a HTML dokumentum programozásának az alapjait tekintjük át. Egy konkrét példát veszünk alapul, és ennek megoldása során mutatjuk be azokat az ismereteket, amelyek szükségesek a feladat megoldásához. A fejezet végére igyekszünk olyan tudást kialakítani, mely tetszőleges alapfeladat elvégzéséhez kellő iránymutatást nyújt.
Erre a fejezetre olyan szemmel is tekinthetünk, hogy a feladatmegoldás általános lépéseiből, a beolvasás-feldolgozás-kiírás hármasából a JavaScript nyelvi ismereteink a feldolgozáshoz elegendőek. A már adott bemeneti paraméterekből a kimeneti adatokat megfelelő függvényekkel tudjuk előállítani. A felhasználóval történő kommunikáció, az adatok beolvasása és megjelenítése azonban a HTML dokumentumon keresztül történik, űrlapok és tulajdonképpen tetszőleges HTML elemek segítségével.
A fejezetben megismerkedünk azzal, hogy JavaScriptből hogyan tudjuk a HTML elemeket elérni, hogyan tudunk ezek egyes adataihoz hozzáférni, hogyan tudunk a felhasználói cselekményekre, mint például egy gombnyomás, programozottan reagálni. A fejezet végén foglalkozunk egy kicsit az elkészült kódunk szervezésével, valamint megnézzük azt, hogy milyen általános lépésekre bontható a feladatmegoldás.
A feladat egyszerű: kérjünk be egy nevet (pl. Senki bácsi), és üdvözöljük azt (Hello Senki bácsi!)!
Ez a feladat megoldható a böngésző által biztosított beolvasó és kiíró függvényekkel a következőképpen (a kódot <script> elemek között bárhova helyezhetjük a HTML dokumentumon belül):
var nev = prompt('Hogy hívnak?', 'Senki bácsi'); alert('Hello ' + nev + '!');
Mindez valóban megfelel a feladat kiírásának, de merőben eltér attól, amihez hozzászoktunk az interneten böngészve. Manapság szinte sehol nem találunk felugró ablakokat akkor, amikor bekérünk adatot, és egy feladat eredményét ugyancsak nem felugró ablakokkal közöljük. A beolvasást általában űrlapon keresztül szoktuk megtenni, hiszen a HTML nyelv éppen ezért vezette be az űrlapok interaktív elemeit. A kiírás pedig megint csak a dokumentumban jelenik meg.
A felhasználóval való kapcsolattartás tehát tipikusan űrlapok és űrlapelemek segítségével történik. A konkrét feladatban tehát valami ilyesmi felületet szeretnénk használni:
Az ennek megfelelő HTML kód a következő:
<form> Név: <input type="text"> <br> <input type="button" value="Köszönj!"> </form>
A feladat megoldását ezek alapján az alábbi lépésekre bonthatjuk:
Ahogy a fejezet elején is írtuk, a fenti lépések közül egyedül a 3.-hoz vannak meg az ismereteink. A feldolgozó függvény az alábbi lehet (látjuk, hogy most nem a feladat bonyolultsága a lényeg):
function nevbolUdvozles(nev) { return 'Hello ' + nev + '!'; }
A többi pont megoldásához viszont az alábbi kérdéseket tehetjük fel:
Kezdjük a második kérdéssel: hogyan tudjuk a szöveges beviteli mező értékét kiolvasni? Vagy a konkrét feladat helyett más példa is eszünkbe juthat: hogyan tudom egy többsoros beviteli mező (textarea) szövegét kiolvasni, vagy egy jelölőmező (checkbox) bejelöltségét, egy nyomógomb feliratát, egy táblázat cellájába írt szöveget meghatározni?
Ha jól megfigyeljük a kérdéseket, akkor mindig valaminek a valamijére vagyunk kíváncsiak: szöveges beviteli mező értéke, jelölőmező bejelöltsége, nyomógomb felirata, stb. Ezekben először mindig egy HTML elemet nevezünk meg (szöveges beviteli mező, jelölőmező, stb), és utána valamelyik tulajdonságára vagyunk kíváncsiak.
Az eredeti konkrét kérdés helyett tehát igazából két általános kérdést kell megválaszolnunk:
Az első kérdés az elemek elérésére vonatkozik, a második az elemek tulajdonságaira.
Magában a JavaScript nyelvi alapjául szolgáló ECMAScript szabványban nincsen definiálva, hogy a HTML elemek hogyan érhetőek el. Ez érthető is, hiszen a JavaScript nyelv független az azt futtató környezettől. Ha a böngészőben megjelenített HTML dokumentumbeli elemek elérése a cél, akkor azt magának a JavaScriptet futtató környezetnek, azaz a böngészőnek kell biztosítania. A HTML elemek eléréséhez tehát a böngésző tesz lehetővé olyan kapcsolódási pontokat (interfészeket), amelyeken keresztül a HTML dokumentum JavaScripttel manipulálható.
A HTML elemekhez való hozzáférés JavaScriptben egy szabványos interfészen, a Dokumentum Objektum Modellen (röviden DOM) keresztül történik. A DOM megértéséhez nézzük meg egy HTML dokumentum böngészőbe történő betöltésének folyamatát.
A HTML dokumentum egy fastruktúra, ahol HTML elemek egymás mellett vagy egymásba ágyazva jelennek meg. Minden elemnek legfeljebb egy szülője van, aki tartalmazza őt, és minden elemnek lehetnek gyerekei, akiket az ő nyitó- és záróeleme tartalmaz. Az elemmel egy szinten lévő más elemeket pedig testvéreknek nevezzük. (Ugyancsak fastruktúrát alkot például a könyvtárszerkezet egy fájlrendszerben.) Példánkban a teljes HTML oldal forráskódja így néz ki:
<!doctype html> <html lang="hu"> <head> <meta charset="utf-8"> <title>Üdvözlés</title> </head> <body> <form> Név: <input type="text"> <br> <input type="button" value="Köszönj!"> </form> </body> </html>
Itt a head elem szülője a html, gyerekei a meta és a title, testvére pedig a body elem. A fastruktúrában a szöveges elemek is megjelennek.
A szöveges HTML állomány betöltésekor a böngésző minden egyes HTML elemnek (még a szövegeseknek is) létrehoz egy JavaScript objektumot, és ezeket az objektumokat ugyanolyan fastruktúrába rendezi, mint ahogy az az eredeti HTML elemeknél volt. A folyamat eredményeképpen kialakul egy objektumhierarchia, ez a DOM. Példánkban a fenti HTML kód betöltése után az alábbi objektumszerkezet alakul ki (Live DOM Viewer segítségével előállítva):
A böngésző az oldal megjelenítését a DOM alapján végzi el. A DOM és a kirajzolt oldal között nagyon szoros kapcsolat van. Ha változik a DOM, akkor az az oldalon azonnal érvényesül. Ugyanez visszafele is igaz: ha valamit változtatunk az oldalon, akkor a változások rögtön megjelennek a DOM-ban is. A fenti folyamatot az alábbi ábrával foglalhatjuk össze:
Az ábra jól szemlélteti azt, hogy az egyes HTML elemeknek milyen DOM objektumok felelnek meg. Ez a leképezés egyszeri, a betöltés során jön létre. A felület a DOM alapján rajzolódik ki, az oda-vissza nyilak azt szemléltetik, hogy a DOM és a felhasználói felület kölcsönös, élő kapcsolatban vannak egymással.
A HTML forráskód, a belőle létrejövő DOM, a felhasználói felület és a JavaScript viszonyát az alábbi sematikus ábrán szemléltethetjük:
Az ábra a fent említett kapcsolaton túl azt is hangsúlyozza, hogy a DOM az az interfész, amelyen keresztül JavaScript kódból a felületi elemeket elérhetjük, módosíthatjuk. Mivel a DOM JavaScript objektumokból áll, olyanokból, mint amilyeneket a nyelvi részben láthattunk, a felület programozása nem áll másból, mint ezen objektumok adattagjainak lekérdezéséből, módosításából, metódusainak a meghívásából.
Szerencsére az, hogy pontosan hogyan hívják a DOM egyes objektumait, milyen adattagjaik és metódusaik lehetnek, nem az egyes böngészőkre van bízva, hanem szabvány határozza meg. A DOM nem csak HTML elemek modellezésére szolgál, hanem minden fastruktúra alapú hierarchiának kezelésére ír le egy szabványos interfészt. A HTML mellett ilyen például az XML dokumentumok szerkezete is. A DOM szabvány is – a HTML-hez hasonlóan – több lépésben fejlődött: az első verziója (DOM Level 1) 1998-ban született, ezt követte a 2-es verzió 2000-ben (DOM Level 2), jelenleg a 3-as verzió van érvényben, amit 2004-ben véglegesítettek.
Amikor HTML oldalak esetében DOM-ról beszélünk, akkor több dolgot foglalunk össze ezen név alatt. Maga a DOM egy általános fastruktúránál határozza meg annak felépítését, elemeinek típusát, azok adattagjait és működtető metódusait. Ezt egészíti a HTML DOM, amely minden egyes HTML elemnél meghatározza azokat a plusz tulajdonságokat és metódusokat, amelyek a HTML elemek teljes körű kezeléséhez szükségesek. Végül a DOM Events a DOM elemekhez tartozó események működtetését határozza meg (ld. később). A DOM tehát inkább a fastruktúrához tartozó műveleteket és tulajdonságokat határozza meg, a HTML DOM pedig a HTML specifikumokat tartalmazza. A böngészőben használva azonban ezek egyszerre jelennek meg, és válnak elérhetővé.
Hivatkozások:
Összefoglaló oldal a különböző DOM szabványokról és verzióikról
A DOM részletes taglalása nem ennek a fejezetnek a feladata. Későbbi fejezetben bővebben lesz szó a DOM használatáról, de egyelőre elég annyit tudni, hogy a DOM minden egy HTML elemről rengeteg információt tárol egy-egy JavaScript objektum képében, és nagyon sokféle műveletet enged elvégezni velük. Ezekkel gyakorlatilag az oldal tetszőleges megváltoztatása lehetséges. A főbb műveletcsoportok:
Most, hogy már betekintést nyertünk abba a mechanizmusba, hogy a HTML elemek hogyan képződnek le JavaScript objektumokká, vissza is térhetünk ennek a résznek eredeti céljához, amelyben HTML elemek elérését tűztük ki. Hogyan lehetséges a DOM segítségével egy adott elemet elérni? Egész egyszerűen úgy, hogy a megfelelő elemet egyedi azonosítóval látjuk el az id attribútumán keresztül, és a létrejövő objektumhierarchiában megkeressük az adott azonosítójú elemet. A keresést az egész DOM gyökérelemén, a document objektumon kell kezdeményeznünk a getElementById() paranccsal, aminek paraméterének a keresendő elem azonosítóját kell megadni, a rá való hivatkozást pedig a függvény visszatérési értéke adja meg:
var elem = document.getElementById('id');
A fentieket összefoglalva a lényeg a következő: Bármelyik elem elérhető JavaScriptből úgy, hogy HTML-ben azonosítót adunk neki az id attribútumával, majd a JavaScript kódban document.getElementById() metódussal hivatkozunk rá.
Példánkban a szöveges beviteli mező értékének kiolvasásához előbb hivatkozást kell szerezni a szöveges beviteli mezőnek megfelelő DOM objektumra. Ezt úgy is nevezzük, hogy először el kell érni a szöveges beviteli mezőnek megfelelő objektumot. A fentieknek megfelelően tehát először a HTML kódot kell módosítani, hogy a szöveges beviteli mezőnek azonosítót adjunk:
Név: <input type="text" id="nev">
JavaScriptben pedig a következőképpen hivatkozhatunk rá:
document.getElementById('nev') //vagy változóba kimentve var nev = document.getElementById('nev');
Ennek első sorát a JavaScript konzolba másolva láthatjuk, hogy a böngésző felismeri az elemet a hivatkozásán keresztül:
A DOM nem az első interfész volt, amely történetileg elérhető volt HTML elemek manipulálására. A DOM a HTML szabványhoz képest viszonylag későn jött létre, viszont addig is el kellett tudni érni az elemeket. A DOM megszületéséig és szabványossá válásáig a másik alternatíva az elemek elérésére a Böngésző Objektum Modell (BOM) volt.
A BOM egy egyáltalán nem szabványos, de szinte minden böngésző által egyformán támogatott objektumhierarchia, amelyen keresztül a böngésző egyes elemeihez lehet programozottan hozzáférni (erről később részletesen is lesz szó), másrészt a dokumentum elemeihez is korlátozott hozzáférést ad. A korlátozás azt jelenti, hogy nem minden elem érhető el, csak azok, amelyek az interfészen megjelennek.
A BOM minket érintő objektumhierarchiáját az alábbi ábra mutatja:
A hierarchia csúcsán a window objektum áll. Alatta számos egyéb mellett megjelenik a HTML dokumentumnak megfelelő document objektum (ez egyébként maga a DOM). A document-en keresztül számos gyűjtemény érhető el: képek az images, hivatkozások a links, űrlapok a forms tömbön keresztül. Az űrlapelemek pedig a forms megfelelő eleme alatt vannak az elements tömbben összegyűjtve. Az egyes tömböket vagy számokkal indexelhetjük (ha tudjuk, hányadik az oldalon belül), vagy az elemek name attribútumával.
Példánkban tehát a HTML kódot ki kellene egészítenünk megfelelő name attribútumokkal:
<form name="formHello"> Név: <input type="text" name="nev"> <br> <input type="button" value="Köszönj!"> </form>
Ekkor a szöveges beviteli mező elérése a következőképpen történhetne követve az objektumhierarchiát:
//Számokkal indexelve window.document.forms[0].elements[0]; //name attribútum szerint window.document.forms['form1'].elements['nev']; //A window lehagyható document.forms['form1'].elements['nev']; //Működik csak a névre hivatkozva document.form1.nev;
A példából látszik, hogy a window le is hagyható, és elég csupán az elemek neveire hivatkozni. Ez utóbbi forma mind a mai napig gyakran előfordul internetes szkriptekben.
Látni kell azonban, hogy a BOM használata elemek eléréséhez korlátozott és elavult, így a használata egyáltalán nem javasolt. Csupán azért néztük meg, hogy ha olyan szkripttel találkozunk, amelyben ezt használják, akkor is megértsük annak működését. Manapság egyértelműen a szabványos és gyorsabb DOM műveletek használata az ajánlott.
A szöveges beviteli mezőt most már el tudjuk érni, de honnan tudjuk, hogy a hozzá tartozó DOM objektumnak milyen adattagjai vannak, és azok közül melyiken keresztül tudjuk a beírt értéket kiolvasni? A válaszhoz ismerkedjünk meg a DOM elemek tulajdonságaival!
A HTML elemeknek megfelelő DOM objektumok a böngésző által létrehozott JavaScript objektumok. Mint minden JavaScript objektumnak, ezeknek is vannak adattagjai és metódusai, amelyeket a DOM szabvány határoz meg. Ezekről többféleképpen szerezhetünk tudomást.
Ha a szabvány határozza meg az adattagokat, akkor nyilvánvaló, hogy annak leírása szolgáltatja a legrelevánsabb információkat. Mivel minket most elsősorban az elemek HTML mivolta érdekel, és nem az, hogy fastruktúrába vannak rendezve, ezért a DOM szabvány közül is a HTML DOM leírása érdekelhet minket. A Mozilla gondozásában is találhatunk referenciát a HTML elemek DOM interfészéről.
Példánkban a szöveges beviteli mezőnek a leírását a HTMLInputElement alatt találhatjuk a W3C leírásában vagy a Mozilla referenciájában.
Szabványos referenciák használatának egyértelmű előnye az, hogy ezek szolgálnak a legpontosabb információval. Egy referencia azonban nem könnyű olvasmány, néha a számunkra fontos információ elvész a sok egyéb szöveg között, szóval mindenképpen az egyik legidőigényesebb módszer egy DOM elem tulajdonságainak a feltérképezésére.
Szerencsére létezik más lehetőség is bizonyos tulajdonságok megsejtésére. Az egyes adattagok elnevezésekor ugyanis a tervezők azzal az elnevezési konvencióval éltek, hogy a HTML attribútumoknak megfelelő DOM tulajdonságokat vezettek be egy-egy HTML elemnél. Mivel azonban a HTML nem kis- és nagybetűérzékeny, a JavaScript viszont igen, ezért megfelelő átírási szabály szerint dolgoztak. Ennek neve camel case, és lényege, hogy minden adattag és metódus kis betűvel kezdődik, de összetett szavaknál a második szótól kezdve nagy betűvel kezdődnek a szavak.
//példák a "camel case" átírásra egyszerű kicsitBonyolultabb ezEgyÖsszetettSzó //Példa JavaScriptből getElementById
Példaképpen nézzük meg a szöveges beviteli mező esetében, hogy a HTML attribútumai alapján milyen DOM tulajdonságokra számíthatunk:
HTML attribútum | DOM tulajdonság |
id | id |
name | name |
type | type |
value | value |
readonly | readOnly |
maxlength | maxLength |
Az analóg módszer előnye, hogy meglévő HTML tudásunk azonnal hasznosítható a JavaScript tulajdonságok megismerésére, így ez egy nagyon gyors és egyszerű módszer HTML elemek programozására. Hátránya, hogy egy-két esetben nem működik, és nem ad teljes rálátást az elérhető funkciókra. A HTML DOM ugyanis sokkal bővebb programozói interfészt nyújt a HTML attribútumok analóg másolásánál.
A DOM tulajdonságok megismeréséhez végül a JavaScript konzolon keresztül is eljuthatunk. A legtöbb JavaScript konzolban ugyanis van egy olyan rész, ahol a kiválasztott HTML elemnek megfelelő DOM objektum tulajdonságait lehet megtekinteni. Ez gyors rálátást ad a lehetőségekre, és a tulajdonságok beszédes elnevezése segít funkcióik megsejtésében is. Ezzel a módszerrel azonban azért érdemes óvatosan bánni, mert a DOM tulajdonságok között megjelenhetnek böngészőspecifikusak is, amelyeket más böngészők nem támogatnak.
Az alábbi képen a szöveges beviteli mezőnk DOM tulajdonságainak részletei láthatók Chrome böngészőben:
Példánkban a szöveges beviteli mező értékére vagyunk kíváncsiak. A leggyorsabb módszer, az analóg módszer szerint ha tudom, melyik paraméter határozza meg a szöveges beviteli mezőbe beírt szöveget, akkor azt JavaScriptből is el tudom érni. HTML-ben az <input type="text"> típusú beviteli mezőknél a value attribútum határozza meg a mezőbe írt értéket. Így várhatóan a value tulajdonságon keresztül kérdezhető le a beírt érték.
Feltételezésünket ellenőrizhetjük a másik két módszerrel. A felfedező módszernél először érdemes valamilyen szöveget írni a mezőbe, majd megvizsgálni a value tulajdonságot. Az alábbi képen látható, hogy a felületen beírt érték megjelent a felületi elemnek megfelelő DOM elem value tulajdonságában.
Végül a szabvánnyal is ellenőriztethetjük sejtésünket. Az ottani leírás szerint text típusú beviteli mező esetén a value attribútum az űrlapelem aktuális tartalmát reprezentálja, ami éppen az, ami nekünk kell.
A példában található szöveges beviteli mező értékét tehát a következőképpen tudjuk kiolvasni:
//hivatkozást szerzünk az elemre var input = document.getElementById('nev'); //lekérdezzük a value tulajdonságát input.value; //=> a beírt értéket írja ki, alapesetben "" //vagy a két lépés egybevonható document.getElementById('nev').value; //a tartalom változóba kimenthető var nev = document.getElementById('nev').value;
Érdemes megjegyezni – bár a példához ez nem szükséges –, hogy a legtöbb tulajdonság olvasható-írható. Ez azt jelenti, hogy JavaScriptből akár meg is változtathatjuk a szöveges beviteli mezőnkbe írt szöveget:
document.getElementById('nev').value = 'Jézus';
Próbáljuk ki a fenti kódrészleteket a megfelelő HTML oldalon!
Most, hogy már tudjuk, hogyan olvashatjuk ki a szöveges beviteli mező értékét, rátérhetünk a feladatmegoldás elején feltett kérdések közül a következőre: Hogyan tudunk JavaScriptben reagálni egy gomb megnyomására? A részletes válaszhoz itt is érdemes általánosítani a kérdést: Hogyan tudunk reagálni a felhasználói cselekvésekre vagy egyéb történésekre?
Egy weboldalon rengeteg kiváltó ok lehet, amire reagálhatunk. Például a felhasználó kattint, mozgatja az egeret, lenyom egy billentyűt. De nemcsak a felhasználó adhat okot a reakcióra. Maga a böngésző is sok mindent elvégez: betölt egy oldalt, vagy egy képet, megváltozik a böngészőablak mérete, stb. A HTML dokumentum egyes elemei, pontosabban a mögöttük álló JavaScript objektumok érzékelik ezeket a történéseket, és jelzik ezek bekövetkeztét. Ezeket a jelzéseket hívjuk eseményeknek.
Ezekre a bekövetkező eseményekre lehet reagálni JavaScriptből az ún. eseménykezelő függvényekkel. Az elnevezés jól mutatja, hogy ezek a függvények bizonyos események bekövetkeztekor futnak le, és programozottan kezelik az események történéseit. Az egyes eseményekhez hozzá kell rendelni az eseménykezelő függvényeket, amit úgy is nevezünk, hogy az eseményekre feliratkoznak az eseménykezelő függvények, vagy az eseménykezelőket regisztráljuk az eseményekhez.
Ezt másképpen úgy lehet elképzelni, mintha az egyes HTML elemeknek megfelelő DOM objektumok kis rádióadók lennének, és egy-egy esemény bekövetkeztekor jelzést sugároznának. Nem kötelező ezekre a jelzésekre figyelni, de ha szeretnénk, akkor figyelhetjük ezeket a kis jeladásokat, és reagálhatunk rájuk. Megkérjük a böngészőt, hogy írja fel az eseménykezelő függvényünket az adott elem adott eseményének listájára, és ha az esemény bekövetkezik, akkor értesítsen minket, azaz hívja meg a függvényt.
Például egy gombra kattintva a gomb jelzi, hogy rákattintottak: egy kattintás esemény keletkezik a gombon. Egy függvényt felírathatunk a gomb kattintás eseményére, ami azt jelenti, hogy a gombra kattintáskor ez a függvény fog meghívódni.
Az események száma igen nagy lehet. A felhasználó már csak azzal, hogy mozgatja az egeret, rengeteg eseményt vált ki, hiszen minden elem, ami fölött elhalad az egér, jelez, hogy belépett az egér, mozog fölöttem az egér, elhagyott az egér.
Egy weboldalon nagyon sokféle esemény következhet be. Ezek egy részét a felhasználói cselekvések váltják ki, másokat maga a böngésző indukálja. A teljesség igénye nélkül nézzük meg, milyen események lehetnek.
A web fejlődése során több módszer is kialakult az eseménykezelők regisztrálására. Ezeket nézzük végig, mindegyiknél megemlítve az előnyeit és hátrányait, valamint azt, hogy ajánlott-e a használatuk.
A történetileg legkorábbi módszernél HTML attribútumként jelenik meg az eseménykezelő. Annál az elemnél kell felvenni, amelyen az esemény bekövetkezik. Általános formában így néz ki:
<elem ontípus="JavaScript kód">
Az attribútum nevében az esemény típusát megelőzi az on szócska. A JavaScript kód tetszőleges hosszúságú kód lehet, de általában csupán egy függvényhívás szokott ott szerepelni.
Példánkban egy hello függvényre bízhatjuk az esemény bekövetkeztekor lefutó logikát. Ennek regisztrálásához a HTML kódunkat a következőképpen kellene módosítani:
<input type="button" onclick="hello()">
Mivel történetileg az első módszerről van szó, ezért ez a megoldás minden böngészőben működik. Az interneten fellelhető példakódokban még mindig sok helyen használják, de e fejezetben később szólunk arról, hogy miért nem érdemes élni ezzel a regisztrálási lehetőséggel. Az inline módszer egyik fő hátránya, hogy mivel a HTML kódban jelenik meg, ezért nincs lehetőség eseménykezelők programozott beállítására. A következő módszerek ezt orvosolják.
Ugyancsak viszonylag korán jelent meg a web történetében a tradicionális módszer, amellyel programkódból rendelhetünk egy eseménykezelő függvényt egy eseményhez. Az eseménykezelő függvényt a kiválasztott elem megfelelő adattagjához kell rendelni:
elem.ontípus = függvény;
Az adattag neve kötelezően végig kisbetű, és az inline módszernél megismert felépítésű: on+típus. Az értékadás jobb oldalán függvényreferenciának, és nem függvényhívásnak kell szerepelnie. A következő regisztrálás tehát rossz:
//Ez rossz elem.ontípus = függvény();
Itt ugyanis a függvény() meghívásra kerül, és annak visszatérési értékét rendeli az elem eseménykezelőjéhez. (Egyetlen esetben lehet jó a fenti kód, ha a függvény() függvénnyel tér vissza.)
Eseménykezelő függvény törlése úgy történik, hogy a megfelelő adattaghoz null értéket rendelünk:
//Eseménykezelő törlése elem.ontípus = null;
Példánkban a gombra kattintáskor kell meghívnunk a hello függvényt. Ahhoz, hogy a gombot JavaScriptből elérjük, id-t kell adnunk neki:
<input type="button" id="gomb">
Ezt követően a hello függvény regisztrálása a click eseményhez a következőképpen történhet:
document.getElementById('gomb').onclick = hello;
A tradicionális módszer előnye, hogy minden böngésző egyaránt támogatja, nagy hátránya viszont, hogy egy elem egy eseményéhez csak egy függvény regisztrálható. Ez nagy megkötést jelent főleg olyan alkalmazásokban, amelyek más programozók által készített JavaScript kódokat is használnak. Ezeknél nem tudhatjuk, hogy feliratkozott-e már valaki egy adott elem eseményére, így könnyen felülírhatjuk a meglévő hozzárendeléseket. Ezekre természetesen születtek különböző megoldások, de ugyanerre a problémára adott választ az eseménykezelések szabványosítása is.
A szabvány két függvényt vezetett be minden DOM objektumhoz: az addEventListener függvénnyel feliratkozhatunk egy elem adott típusú eseményére, a removeEventListener függvény pedig megszünteti a hozzárendelést. Mindkét függvénynek három paramétere van:
//Feliratkozás elem.addEventListener('típus', függvény, false); //Leiratkozás elem.removeEventListener('típus', függvény, false);
A függvények elnevezése és használata arra utal, hogy egy elemhez hozzáadunk egy adott típusú eseményre figyelő függvényt.
A példánk a szabványos módszerrel így néz ki:
var gomb = document.getElementById('gomb'); gomb.addEventListener('click', hello, false); //vagy egyben, de így nagyon hosszú document.getElementById('gomb').addEventListener('click', hello, false);
A módszer egyértelmű előnye a szabványossága, és hogy megoldotta azt a felíratható függvények számának korlátozását. Ezzel a módszerrel egy elem adott eseményére akárhány függvény feliratkozhat, az esemény bekövetkeztekor a böngésző mindegyik függvényt egymás után meghívja valamilyen sorrendben.
A Microsoft saját eseménykezelő-regisztrálási módszert dolgozott ki, amely a szabványhoz hasonlóan ugyancsak több függvény regisztrálását teszi lehetővé, de nem támogatja az elkapás irányának meghatározását, így valamennyivel kevesebb annál. Itt is egy-egy függvény szolgál a fel- és leiratkozásra, az attachEvent és detachEvent.
//Feliratkozás elem.attachEvent('ontípus', fuggveny); //Leiratkozás elem.detachEvent('ontípus', fuggveny);
A típus megadásánál itt is az on+típus szerepel.
Az újabb Microsoft böngészők már támogatják a szabványos megoldást, így ennek használata csak régebbi Internet Explorerek támogatása esetén jöhet szóba.
A példafeladat megoldásához már csak egyetlen kérdésre kell válaszolunk: Hogyan tudjuk a dokumentumban megjeleníteni az eredményt? Az előző fejezetekben láttunk már néhány függvényt, amely kiírásra szolgált, de ezek egyike sem felel meg az elvárásnak. A console.log() a JavaScript konzolra ír, az átlagos felhasználónak nem is látszik, fejlesztéskor használjuk. Az alert() felugró ablakkal zavarja meg az oldal használatának menetét. A document.writeln() függvény ugyan a dokumentumba ír, de csak oldal betöltődése során szabad használni. Az eseménykezelőkben megadott kód pedig tipikusan már az oldal betöltődése után fog lefutni. Lezárt dokumentumon meghívva a document.writeln() függvényt, a böngésző új dokumentumot nyit (eltűnik az eddigi dokumentumunk, és egy üres oldal lesz), és abba írja bele a megadott szöveget. Ez kerülendő mellékhatás.
Egy másik lehetőség a kiírásra az űrlapmezők használata. Azt már láttuk, hogyan lehet kezelni a szöveges beviteli mezőt. Így ezt nemcsak bekérésre, hanem kiírásra is használhatnánk, az eredményt az eredmény megjelenítésére felvett beviteli mező value tulajdonságának értékül adva (ahogy arra fentebb volt is példa). Ezzel a megközelítéssel azonban az a gond, hogy az űrlapmezők ritkán illenek kiírás szempontjából egy oldal designjába. Bár CSS-sel szabadon formázhatók, keretük eltüntethető, csak olvashatóvá tehetők, de pl. formázott szöveget nem tartalmazhatnak. Használatuk tehát nagyon korlátozott, alapvetően nem kiírásra, hanem beolvasásra való komponensek ezek.
Ha nem programból szeretnénk kiírni az eredményt, hanem statikus HTML-t kellene készítenünk, akkor hogyan oldanánk meg a feladatot? Valószínűleg úgy, hogy felvennénk egy új elemet, pl. egy bekezdést vagy egy span elemet a megfelelő helyre, és abba írnánk az eredményt. Valahogy így:
<form> Név: <input type="text"> <br> <input type="button" value="Köszönj!"> <br> <p>Hello világ!</p> </form>
JavaScriptben nincsen más dolgunk, mint ennek a bekezdésnek a tartalmát megváltoztatni. Ha általánosítani szeretnénk, akkor úgy is mondhatjuk, hogy a kész dokumentumba írni annyit jelent, hogy a dokumentum valamelyik elemének a tartalmát szeretnénk megváltoztatni. Erre az innerHTML nevű DOM adattag ad lehetőséget, amelyen keresztül bármelyik elemnek a belső HTML tartalmát lekérdezni vagy beállítani szöveges formában. Bár ez az adattag csak a HTML5-tel válik szabványossá, már most minden böngésző kivétel nélkül támogatja.
//Írás elem.innerHTML = 'Tetszőleges HTML szöveg'; //Olvasás elem.innerHTML;
A kiíráshoz tehát azonosítót kell adnunk a bekezdésnek, és JavaScriptből elérve őt, az innerHTML tulajdonságának értékül kell adni a kiírandó szöveget. Mivel pedig az elem belső HTML-jét határozzuk meg, ezért ide tetszőleges HTML szöveg kerülhet. A HTML tehát így alakul:
<p id="kimenet"></p>
JavaScriptben pedig a következőképpen jelenítjük meg az eredményt:
document.getElementById('kimenet').innerHTML = 'Hello világ!';
A fentiekben választ kaptunk minden kérdésre, így ezek alapján a példafeladat megoldása elkészíthető. A beolvasáshoz szükségünk lesz szöveges beviteli mezőre, a kiírás egy bekezdés tartalmaként jelenik meg, a feladat megoldása pedig egy gombra kattintással indul el. Mivel a beolvasáshoz a beviteli mező, kiíráshoz pedig a bekezdés elérésére van szükség, ezért ezeknek egyedi azonosítót adunk az id attribútumukon keresztül. A gombra kattintáskor egy hello nevű függvényt szeretnénk meghívni, amely beolvassa a beírt értéket, átadja azt a feldolgozó függvénynek, majd kiírja a bekezdésbe. A hello eseménykezelő regisztrálását inline módszerrel végezzük el, ehhez a gombnál egy onclick attribútum megadása szükséges. A feldolgozó függvényt pedig már a fejezet elején megadtuk (nevbolUdvozles). A logikát jelentő JavaScript kódokat a head-en belül helyezzük el.
<!doctype html> <html lang="hu"> <head> <meta charset="utf-8"> <title>Üdvözlés</title> <script type="text/javascript"> function nevbolUdvozles(nev) { return 'Hello ' + nev + '!'; } function hello() { //Beolvasás var nev = document.getElementById('nev').value; //Feldolgozás var udvozles = nevbolUdvozles(nev); //Kiírás document.getElementById('kimenet').innerHTML = udvozles; } </script> </head> <body> <form> Név: <input type="text" id="nev"> <br> <input type="button" value="Köszönj!" id="gomb" onclick="hello()"> <p id="kimenet"></p> </form> </body> </html>
Kliensoldali szkriptekben gyakran kell különböző dokumentumbeli elemekre hivatkoznunk, így viszonylag gyakran fordul elő a document.getElementById() függvény használata. Azon túl, hogy a hosszúsága miatt olvashatatlanná teszi a kódot, még könnyen el is gépelhető. A gyorsabb fejlesztés, a jobban olvasható kód és a hibamentesség érdekében bevezetünk egy $() nevű függvényt, amely ugyanazt adja vissza, mint a document.getElementById() függvény, gyakorlatilag azt csomagolja be egy rövidebb nevű formába.
function $(id) { return document.getElementById(id); }
Innentől kezdve minden document.getElementById() helyett nyugodtan írhatunk $() függvényt. Példakódunk JavaScript része tehát a következőképpen változik:
function $(id) { return document.getElementById(id); } function nevbolUdvozles(nev) { return 'Hello ' + nev + '!'; } function hello() { var nev = $('nev').value; var udvozles = nevbolUdvozles(nev); $('kimenet').innerHTML = udvozles; }
Jelenlegi megoldásunk – bár az elvárásoknak megfelelően látja el feladatát – annyiban még finomításra szorul, hogy jelenleg egy forráskódban két alapvetően eltérő funkcionalitású kód, a HTML és a JavaScript keveredik, ami hosszú távon áttekinthetetlenné teheti a kódot. Úgy, ahogy a HTML-t és a CSS-t állományszinten is elválasztottuk egymástól, jó lenne a JavaScriptet is állományszinten szétválasztani a HTML-től. Így az egyes állományok csak egyféle funkciójú kódért lennének felelősek:
A szétválasztás első lépéseként a head részben szereplő függvénydefiníciókat kell külön állományba helyezni (hello.js), és helyette egy hivatkozást elhelyezni rá.
<!doctype html> <html lang="hu"> <head> <meta charset="utf-8"> <title>Üdvözlés</title> <script type="text/javascript" src="hello.js"></script> </head> <body> <form> Név: <input type="text" id="nev"> <br> <input type="button" value="Köszönj!" id="gomb" onclick="hello()"> <p id="kimenet"></p> </form> </body> </html>
Látszólag végeztünk is a feladattal, azonban az inline eseménykezelők még tartalmaznak JavaScript kódot. Ezek megszüntetéséhez a HTML-ben minden eseménykezeléshez kapcsolódó attribútumot törölni kell, és az eseménykezelőket programozottan kell az elemekhez rendelni, célszerűen a szabványos módszerrel. A HTML így végleg mentesül minden beleírt JavaScript kódtól:
<!doctype html> <html lang="hu"> <head> <meta charset="utf-8"> <title>Üdvözlés</title> <script type="text/javascript" src="hello.js"></script> </head> <body> <form> Név: <input type="text" id="nev"> <br> <input type="button" value="Köszönj!" id="gomb"> <p id="kimenet"></p> </form> </body> </html>
A JavaScript kódot tartalmazó hello.js állomány pedig tartalmazza a függvénydefiníciókat és az eseménykezelők regisztrálását.
//Függvénydefiníciók function $(id) { return document.getElementById(id); } function nevbolUdvozles(nev) { return 'Hello ' + nev + '!'; } function hello() { var nev = $('nev').value; var udvozles = nevbolUdvozles(nev); $('kimenet').innerHTML = udvozles; } //Eseménykezelők regisztrálása $('gomb').addEventListener('click', hello, false);
A fenti kód azonban nem működik. A gond az, hogy a hello.js állományt a head részben töltjük be, ami le is fut azonnal, mielőtt az oldal tovább töltődne. A függvénydefiníciókkal nincs is baj, de az eseménykezelő regisztrálásakor olyan elemre hivatkozunk (gomb), ami még be sem töltődött, hiszen az <input type="button"> sor a forráskódban később szerepel. A problémára két megoldás is lehetséges. Elsőként letehetnénk a script elemet az oldal aljára a </body> elé, hiszen eddigre már biztosan létrejött az összes elem, amire hivatkozhatunk. Ha viszont szeretnénk a script elemet a fejlécben tartani, akkor az eseménykezelők regisztrálását csak akkor szabad elvégezni, ha már az oldal teljesen betöltődött. Szerencsére ezt egy esemény jelzi, a window objektum load eseménye, amire felírathatunk egy olyan függvényt, mely az oldal betöltődése után a kezdeti beállításokat elvégzi. Nevezzük ezt init függvénynek. Ezzel a JavaScript kód a következőképpen alakul:
//Függvénydefiníciók function $(id) { return document.getElementById(id); } function nevbolUdvozles(nev) { return 'Hello ' + nev + '!'; } function hello() { var nev = $('nev').value; var udvozles = nevbolUdvozles(nev); $('kimenet').innerHTML = udvozles; } function init() { //Eseménykezelők regisztrálása $('gomb').addEventListener('click', hello, false); } window.addEventListener('load', init, false);
Érdemes a JavaScript állományon belüli különböző funkciójú kódrészleteket (egyelőre) megjegyzésekkel elválasztani egymástól. Háromféle függvény található a kódban:
A feldolgozó függvények függetlenek a felülettől, ők a rendelkezésre álló adatokkal dolgoznak, a paraméterül kapott bemeneti adatok alapján visszatérési értékként adják vissza az eredményt (ez később finomítható a függvények objektumokba szervezésével, ahol az objektumok saját adattagjaikra is hivatkozhatnak). Az eseménykezelő függvények közvetlen kapcsolatban állnak a felülettel, hiszen az ottani eseményekre reagálnak. A segédfüggvények pedig a kódolást segítik elő. Hosszú távon ezek a különböző típusú függvények akár külön fájlokba is kiszervezhetők. Most egyelőre megjegyzésekkel érdemes jelölni, melyik függvény mire való.
//Segédfüggvények function $(id) { /*...*/ } //Feldolgozó függvények function nevbolUdvozles(nev) { /*...*/ } //Eseménykezelő függvények function hello() { /*...*/ } function init() { /*...*/ } window.addEventListener('load', init, false);
Egy feladat megoldása során valószínűleg sok függvény keletkezik. A függvények megjegyzésekkel elválasztott szeparálása kisebb feladatoknál elég is lehet. De mi van akkor, ha egy oldalon belül több feladatot is meg kell oldanunk? Az egyik lehetőség, hogy az egyes feladatokhoz tartozó függvényeket külön JavaScript fájlokba szervezzük ki. Ez azonban nem ad megoldást arra, ha két feladatban ugyanolyan nevű függvény szerepel. Ebben az esetben a két függvényt valamilyen módon meg kell különböztetni egymástól.
JavaScript objektumokkal megoldást találhatunk erre a problémára is egy objektumon belülre szervezve az ugyanazon feladathoz tartozó függvényeket. Így minden egyes feladatnak külön objektuma lesz:
var feladat1 = { fgv1: function () { /* ...*/ }, fgv2: function () { /* ...*/ }, fgv3: function () { /* ...*/ } }; var feladat2 = { fgv1: function () { /* ...*/ }, fgv2: function () { /* ...*/ }, fgv3: function () { /* ...*/ } }; feladat1.fgv1(); feladat2.fgv1();
Az objektumok a feladathoz tartozó adatokat is tárolhatják az objektum adattagjaiként.
Az egyes objektumokat végül egy nagy összefogó objektum alá lehet szervezni, mely az alkalmazásunk egyetlen globális változója lesz.
var ALKALMAZAS = ; ALKALMAZAS.feladat1 = { fgv1: function () { /* ...*/ }, fgv2: function () { /* ...*/ }, fgv3: function () { /* ...*/ } }; ALKALMAZAS.feladat2 = { fgv1: function () { /* ...*/ }, fgv2: function () { /* ...*/ }, fgv3: function () { /* ...*/ } }; ALKALMAZAS.feladat1.fgv1();
Ezt a kódszervezést hívják névterekbe szervezésnek.
Természetesen a fájlokba, illetve névterekbe való kódszervezés nem zárja ki egymást. Egy adott feladathoz tartozó függvényeket megfelelő névtérbe tesszük, majd ezt a névteret külön fájlba másoljuk. Ekkor viszont minden fájl elején létre kell hozni a névtereknek az objektumait, ami felülírhatja a másik fájlban már definiált névteret. Így új objektumot csak akkor hozunk létre, ha az már nem létezik.
if (!ALKALMAZAS) { ALKALMAZAS = ; }
Tananyagunkban a kódokat a T mint tananyag névtérbe szervezzük, és ebbe hozzuk létre az egyes feladatoknak megfelelő alnévtereket. E fejezetbeli példánkhoz egy hello névteret hozunk létre és a függvényeit ez alá szervezzük. A segédfüggvények feladatfüggetlenek, így azok közvetlenül a T névtér alá mehetnek, a nagyon gyakran használtakat, így pl. a $ függvényt is, a globális névtérben hagyunk meg. A példában látható, hogy minden eddigi függvényhivatkozásunk előtt most megjelenik a T.hello névtérre hivatkozás.
//Névtér létrehozása var T; if (!T) { T = ; }; //Segédfüggvények var $ = T.$ = function(id) { return document.getElementById(id); } //Feldolgozó függvények T.hello = ; T.hello.nevbolUdvozles = function(nev) { return 'Hello ' + nev + '!'; } //Eseménykezelő függvények T.hello.hello = function() { var nev = $('nev').value; var udvozles = T.hello.nevbolUdvozles(nev); $('kimenet').innerHTML = udvozles; } //Belépési pont T.hello.init = function() { //Eseménykezelők regisztrálása $('gomb').addEventListener('click', T.hello.hello, false); } window.addEventListener('load', T.hello.init, false);
Névterek használata főleg nagyobb feladatok esetén térül meg, ahol hosszú távon a kód szervezettsége átláthatóságot és könnyebb hibajavítást eredményez. Kisebb feladatoknál a névterek kiírása feleslegesen bonyolítja a kódot. Éppen ezért tananyagunkban lesznek olyan példák, amelyek névterekbe szervezik a megoldáshoz tartozó függvényeket, de olyanra is lesz példa, hogy csak a megoldó függvényeket adjuk meg, és az olvasóra bízzuk ezeknek névterekbe szervezését.
A fenti ismeretek alapján egyszerűbb kliensoldali feladatok már könnyedén megoldhatóak. A feladatok megoldásához az alábbi általános lépéseket érdemes követni.
Addig ne álljunk neki kódolni, amíg nem tudjuk, mit szeretnénk csinálni. Ehhez át kell gondolni, hogy milyen adatokkal szeretnénk dolgozni (specifikáció), és azokkal hogyan oldjuk meg a feladatot nagy vonalakban (algoritmus).
A tervezés során fény derül a szükséges adatokra és azok szerkezetére. Ezt kell nekünk JavaScript adattípusokra leképeznünk. Az egyszerű típusoknál logikai értékekkel, számokkal és szövegekkel dolgozhatunk, az összetett adatszerkezeteknél a tömb és az objektum jöhet szóba. A szükséges adatokat első körben felvehetjük globális változókként, később az egységbe zárás elveként objektum adattagjaivá alakíthatjuk őket. Ez gyakorlatilag megfelel a névterek kialakításának. Ez a lépés független a felület kialakításától, csupán a JavaScript nyelvi elemeire van szükség.
Ugyancsak egy felületfüggetlen lépés a feldolgozó függvények kialakítása. Itt a konkrét feladatra kell koncentrálni, a bemeneti adatokat adottnak feltételezve. Ezek a függvények a kiírásról sem gondoskodnak.
Ha már a feladat során használt adatok ismertek, akkor ezek beolvasásához és kiírásához megtervezhető a felhasználói felület. Sokszor maga a feladat ad ezekhez konkrét iránymutatást, bizonyos HTML elemeket elvárva a megoldásban. Érdemes a lehetséges képernyőképekről vázlatokat készíteni, majd ezek alapján elkészíthető a megfelelő statikus HTML oldal a feladat megoldásához szükséges HTML elemekkel.
Utolsó lépésként érdemes elkészíteni a felületet a háttérlogikával összekötő függvényeket. Ezek egy része a bekövetkező események kezelését végzi el, másik része a beolvasásért, kiírásért felelhet (ha nem olvad bele az eseménykezelő függvényekbe). Ha alkalmazásunkat jól tervezzük meg, akkor itt már csak a feldolgozó függvények megfelelő meghívásáról kell gondoskodni.
Ebben a fejezetben megismerkedhettünk a kliensoldali webprogramozás alapvető technológiáival és elveivel. A fejezetben egy konkrét példa megoldása során felmerülő kérdéseket általánosítva néztük végig lépésről lépésre a szükséges ismereteket, sokszor részletesen megismerve a háttér-információkat is. Az alábbiakban összefoglaljuk a fejezet legfontosabb tudnivalóit:
Készíts webes alkalmazást kamatos kamat számolására. A számoláshoz meg kell adni a kiindulási összeget, a kamat értékét, valamint azt, hány évvel későbbi összegre vagyunk kíváncsiak. A feladat során jelenítsük meg azt is, hogy melyik évben hogyan változik az összeg.
A megoldás elkészítéséhez vizsgáljuk meg, hogy a feladat milyen adatokkal dolgozik. A leírásból egyértelműen kiderül, hogy három bemeneti adatunk lesz: a kiindulási összeg (alap), a kamat (kamat) és a vizsgált időszak hossza (év). A kimeneten nem egyszerűen a kamatos kamat összegét kell megadnunk, hanem minden évnél meg kell adnunk az adott évi kamatos kamat értékét. A kimeneten tehát egy sorozat lesz, amit tömbként valósítunk meg:
A specifikáció alapján három elemi típusra és egy tömbre lesz szükség. Ezekkel dolgoznak a feldolgozó függvények paramétereiken és visszatérési értékeiken keresztül.
A megoldás fő algoritmusában minden évre ki kell számítani az előző évi összeg alapján a kamatos összeget. Emeljük ki ebből a feldolgozási logikából a kamatos összeg számításáért felelős kódot (kamatSzamitas), és a fő függvény (kamatosKamat) egy ciklusban minden évre tárolja el a kiszámított kamatot.
function kamatSzamitas(alap, kamat) { return alap * (1 + kamat / 100); } function kamatosKamatok(alap, kamat, ev) { var osszegek = [alap]; for (var i = 0; i < ev; i++) { var alap = kamatSzamitas(alap, kamat); osszegek.push(alap); } return osszegek; };
A beolvasást űrlapon keresztül oldjuk meg. A három beolvasandó adatnak egy-egy szöveges beviteli mezőt hozunk létre alap, kamat és ev azonosítókkal. A számolást egy gombra kattintva indítjuk el, ennek gomb azonosítót adunk.
Az eredménytömböt többféleképpen megjeleníthetjük. Jó lenne, ha az összegek mellett azt is feltüntetnénk, melyik évhez tartozik egy adott összeg, így egy elem kiírásánál máris két adatot kell megjelenítenünk. Ehhez válasszuk a táblázatot, mely az ev+1 sornyi, soronként két oszlopból álló információt szépen elrendezi. A beviteli mezők alatt tehát felveszünk egy tablazat azonosítójú <table> elemet is. A kamat.html tehát a következőképpen nézhet ki:
<!doctype html> <html lang="hu"> <head> <meta charset="utf-8"> <title>Kamatos kamat</title> <script type="text/javascript" src="kamat.js"></script> </head> <body> <h1>Kamatos kamat számítása</h1> <form> Alap: <input type="text" id="alap"> <br> Kamat: <input type="text" id="kamat"> <br> Év: <input type="text" id="ev"> <br> <input type="button" value="Számol!" id="gomb"> <table id="tablazat"></table> </form> </body> </html>
Utolsó lépésként a felhasználói felületet és a feldolgozó logikát kell összekapcsolni eseménykezelők és kiírásért felelős függvényekkel. Az oldalon egyetlen eseményre kell reagálnunk: a gomb megnyomására. Így az oldal betöltése után a gomb click eseményéhez egy eseménykezelő függvényt (szamol) rendelünk:
function init() { //Eseménykezelők regisztrálása $('gomb').addEventListener('click', szamol, false); }; window.addEventListener('load', init, false);
A szamol függvényen belül be kell olvasnunk az adatokat a beviteli mezőkből, majd azokat át kell adni a kamatosKamat függvénynek, mely előállítja az összegeket tároló tömböt. Végül ezt a tömböt kell megjeleníteni a táblázatsorokként a táblázat belsejében az osszegbolSorok függvény segítségével:
function szamol() { //Beolvasás var alap = $('alap').value; var kamat = $('kamat').value; var ev = $('ev').value; //Feldolgozás var osszegek = kamatosKamatok(alap, kamat, ev); //Kiírás $('tablazat').innerHTML = osszegekbolSorok(osszegek); }
Az osszegekbolSorok függvény a tömb elemeiből készíti el a megjelenítéshez szükséges táblázatsorokat. Végigmegy a tömbön, és mindegyik tömbelemhez a megfelelő <tr> és <td> HTML elemeket állítja elő. Az eredmény szövegként kerül visszaadásra, és ez a szöveg kerül a táblázat belsejébe (innerHTML). Az összegeket a megjelenítésben egészre kerekítjük a Math.round() segítségével.
function osszegekbolSorok(osszegek) { var s = ''; for (var i = 0; i < osszegek.length; i++) { s += '<tr>'+ '<td>'+i+'</td>'+ '<td>'+Math.round(osszegek[i])+'</td>'+ '</tr>'; }; return s; }
A fenti JavaScript kódrészleteket a megfelelő segédfüggvényekkel együtt egy kamat.js fájlba tesszük, így áll össze a megoldás.
//Segédfüggvények function $(id) { return document.getElementById(id); } //Feldolgozó függvények function kamatSzamitas(alap, kamat) { return alap * (1 + kamat / 100); } function kamatosKamatok(alap, kamat, ev) { var osszegek = [alap]; for (var i = 0; i < ev; i++) { var alap = kamatSzamitas(alap, kamat); osszegek.push(alap); } return osszegek; }; //Kiírás function osszegekbolSorok(osszegek) { var s = ''; for (var i = 0; i < osszegek.length; i++) { s += '<tr>'+ '<td>'+i+'</td>'+ '<td>'+Math.round(osszegek[i])+'</td>'+ '</tr>'; }; return s; } //Eseménykezelők, beolvasás function szamol() { //Beolvasás var alap = $('alap').value; var kamat = $('kamat').value; var ev = $('ev').value; //Feldolgozás var osszegek = kamatosKamatok(alap, kamat, ev); //Kiírás $('tablazat').innerHTML = osszegekbolSorok(osszegek); } function init() { //Eseménykezelők regisztrálása $('gomb').addEventListener('click', szamol, false); }; window.addEventListener('load', init, false);
A kamat.html fájlt böngészőben megnyitva egy számolást követően az alábbi képernyőképet kell látnunk:
Bár megfelelő input esetén a fenti program jól működik, nem számokat vagy az előfeltételeknek nem megfelelő adatokat megadva rossz eredményt kaphatunk, ahogy az az alábbi képernyőképen is látszik:
Az ellenőrzésekről bővebben az űrlapkezelésnél lehet olvasni.
A fent megismert függvényeket egy csokorba szervezhetjük a T.kamat objektumba mint névtérbe. Ezzel a kamatszámításhoz tartozó üzleti és felületi logika elkülönül a többi feladat függvényeitől és adataitól.
//Névtér létrehozása var T; if (!T) { T = ; }; //Segédfüggvények var $ = T.$ = function(id) { return document.getElementById(id); }; //Feldolgozó függvények T.kamat = ; T.kamat.kamatSzamitas = function(alap, kamat) { return alap * (1 + kamat / 100); }; T.kamat.kamatosKamatok = function (alap, kamat, ev) { var osszegek = [alap]; for (var i = 0; i < ev; i++) { var alap = T.kamat.kamatSzamitas(alap, kamat); osszegek.push(alap); } return osszegek; }; //Kiírások T.kamat.osszegekbolSorok = function (osszegek) { var s = ''; for (var i = 0; i < osszegek.length; i++) { s += '<tr>'+ '<td>'+i+'</td>'+ '<td>'+Math.round(osszegek[i])+'</td>'+ '</tr>'; }; return s; }; //Eseménykezelők, beolvasás T.kamat.szamol = function() { //Beolvasás var alap = $('alap').value; var kamat = $('kamat').value; var ev = $('ev').value; //Feldolgozás var osszegek = T.kamat.kamatosKamatok(alap, kamat, ev); //Kiírás $('tablazat').innerHTML = T.kamat.osszegekbolSorok(osszegek); }; T.kamat.init = function() { //Eseménykezelők regisztrálása $('gomb').addEventListener('click', T.kamat.szamol, false); }; window.addEventListener('load', T.kamat.init, false);
A tananyag az ELTE - PPKE informatika tananyagfejlesztési projekt (TÁMOP-4.1.2.A/1-11/1-2011-0052) keretében valósult meg.
A tananyag elkészítéséhez az ELTESCORM keretrendszert használtuk.