A JavaScript elsődleges célja mind a mai napig interaktivitást hozni a böngészőbe. Ez a fejezet azt mutatja be, hogy ez milyen módon történik, milyen egyéb programozási interfészek segítenek ebben, és hogyan valósul meg a felhasználói tevékenységekre történő reakció.
A tananyag eddigi fejezeteiben kizárólag a JavaScripttel mint nyelvvel foglalkoztunk. Megismerhettük a nyelv fő koncepciót és főbb nyelvi elemeit, illetve láthattuk, hogy milyen lehetőségek rejlenek ezekben, és hogyan használhatóak ezek összetettebb struktúrák építésére, illetve más nyelvekben elérhető funkcionalitások szimulálására. Az előző fejezetekből kiderült, hogy a JavaScript objektumszemléletének rugalmasságából milyen sokoldalúság fakad, és ezt jól támogatják a JavaScript függvényei, amelyekkel a nyelv funkcionális aspektust kap. Összességében, amit eddig láttunk, az a JavaScript központi nyelvi része (JavaScript Core) volt, vagyis az a szintaktikai-, szemantikai alap, az az eszközkészlet, mely szükséges bármilyen, kliens- vagy akár szerveroldali szoftver írásához.
Nem beszéltünk azonban eddig a futtatókörnyezetről. A JavaScript beágyazott szkriptnyelvként mindig egy gazdakörnyezetben fut. Tradicionálisan ez a böngésző. A JavaScript születésekor éppen arra volt hivatott, hogy a böngészőbe betöltött oldalakat dinamikussá tegye. Így a böngészőkbe a HTML oldalak megjelenítése mellett helyett kapott a JavaScript értelmező is, amely az oldalban megjelenő szkripteket értelmezte. Eleinte tehát csak a böngészőkben volt lehetőség JavaScript kódok értelmezésére.
Szerencsére manapság már többféleképpen is lehetőség van JavaScript kód futtatására. Bár korábban több próbálkozás is volt a JavaScriptnek parancssori értelmezésére, az igazi áttörést a Node.js hozta el, amely a Google korszerű V8 JavaScript motorjára építve készítette el parancssori környezetét. Ebből a parancssori felhasználásból nőtt ki a JavaScript szerveroldali ágazata, így mostanra a kliensoldal mellett már a kiszolgáló oldalon is lehetőség van JavaScript használatára. A Node.js környezet aszinkron filozófiájával méltó vetélytársat teremtett szerveroldalon a hagyományos technológiáknak, így a JavaScript szerepe ott is egyre jelentősebb.
Bárhol is használjuk azonban a JavaScriptet, a futtatókörnyezetnek biztosítania kell egy olyan kapcsolódási felületet, amelyen keresztül a nyelvi elemekkel a gazdakörnyezet szolgáltatásai elérhetőek. Ez a kapcsolódási felület nem más, mint a futtatókörnyezet szolgáltatásai elé készített programozói felület (API).
Ebben a fejezetben a továbbiakban kizárólag a kliensoldali alkalmazásokkal foglalkozunk, így futtatókörnyezet alatt a böngészőt értjük. A böngészőnek kell tehát megfelelő API-kat biztosítania a szolgáltatásai felé. A böngésző legfontosabb szolgáltatása a HTML oldalak megjelenítése, de ezen kívül számos egyéb dolgot is elérhetővé tesznek a böngészők: a böngészőablak megjelenésének, az oldal URL-információinak, az előzmények kezelését, adatok tárolását, stb.
A böngészők tehát a JavaScript nyelvi maggal való kapcsolattartást alapvetően két modell segítségével biztosítják:
A BOM – ahogy fent is elhangzott – a böngésző szolgáltatásait képviselő objektumhierarchia. A BOM nem szabványos, ennek ellenére meglepően egységes a különböző böngészőkbeli implementáltsága. A hierarchia csúcsán a window objektum áll, amely a böngésző egy ablakához, illetve azon belül is egy lapjához tartozik. A böngésző minden lapjához külön window objektum jön létre, és ezek egymástól teljesen függetlenek. A window objektumnak két főbb szerepe van:
Mivel ez utóbbi elsősorban a nyelvi elemekről szól, ezért ezzel kapcsolatban ebben a fejezetben elég annyit megmutatni, hogy minden globális változó a window objektum részeként jön létre.
//Globális változó létehozása var globalis = 42; ok( globalis === window.globalis, 'A window objektumon keresztül elérhető a globális változó'); ok( globalis === window['globalis'], 'A globális változók a window objektum adattagjaként jönnek létre');
Ez a fejezet azonban a böngészők szolgáltatásairól szól, úgyhogy sokkal inkább a window objektum első szerepével szükséges foglalkoznunk, és ezen szolgáltatások közül a fontosabbakat bemutatni. Nem is törekedhetünk a teljesség igényére, hiszen ez a tananyag egyrészt nem referencia, arra ott vannak a megfelelő dokumentációk (pl. az MDN JavaScript referenciája), másrészt nem is tudnánk mindent bemutatni, mert szerencsére a böngészők évről évre egyre több szolgáltatást tesznek elérhetővé, amelyekkel még funkció-gazdagabb webes alkalmazások írására adódik lehetőség. Például a HTML 5-ös verziója rengeteg API-t vezetett be ezen célból, és ezek mind a window objektum alá tagozódnak be.
A böngésző egyes szolgáltatás-API-jai objektumok formájában tehát a window objektumon keresztül érhetőek el, faszerkezetet alakítva. Néhány fontosabb szolgáltatás:
A window objektum a böngészőablakot reprezentálja, olyan metódusokat és tulajdonságokat definiálva, amelyeken keresztül az ablak állapota lekérdezhető és megváltoztatható. A window objektum és az általa képviselt ablak között élő kapcsolat van, azaz a window-ban bekövetkező változásoknak azonnal megjelenítésbeli következménye van, és viszont, azaz az ablak állapotváltozásai a window objektumon keresztül lekérdezhetők.
A window objektum néhány fontosabb tulajdonsága és metódusa:
Az ablak méretéhez kapcsolódóan két eseményt kell kiemelnünk:
A böngészőablaknak helyet adó képernyő tulajdonságai a screen objektumon keresztül érhetőek el (window.screen). Néhány fontosabb tulajdonsága:
Az aktuális weboldal URL-információival kapcsolatos adatok és műveletek a location objektumban jelennek meg. A location segítségével nem csak az adott weboldal URL-jét és annak részleteit tudjuk lekérdezni, de egyszerű átirányításokat is itt tehetünk meg. A location objektum adattagjaiként a következő információk nyerhetők ki az URL-ről:
console.log(window.location);
A metódusain keresztül az oldal újratöltése érhető el JavaScriptből:
A böngészési előzményekhez a window objektum history objektumán keresztül férhetünk hozzá. Ennek egyik, hagyományos része lehetővé teszi, hogy az előzmények között előzőre (back()) vagy következőre (forward()) váltogassunk, vagy ezek között ugorjunk (go(n)). A HTML5 ezek mellett azonban olyan új lehetőségekkel ruházta fel ezt az objektumot, amely lehetővé teszi új bejegyzések létrehozását a böngészési előzményekhez (pushState(state, title, url)) anélkül, hogy az oldal újratöltődne, valamint eseményt vált ki akkor, ha pl. a böngésző vissza gombjának megnyomásakor a böngészési előzmények listájában változás állna be (popstate).
Annak érdekében, hogy megértsük e bővítés szükségességét, azt kell látnunk, hogy az URL alapvetően egy egyedi erőforrást azonosít. Ez általában egy weboldal, de lehet egy kép, stb. Ezt az URL-t aztán el lehet menteni könyvjelzőként, másnak elküldeni levélben, ezt indexeli be a keresőmotor. Régebben a böngészők úgy működtek, hogy az URL változtatásával a böngésző újratöltötte az egész oldalt. Igen ám, de a modern kliensoldali alkalmazások gyakran úgy változtatják meg állapotukat, hogy csak a háttérben történik adatcsere a szerverrel, mégis jó lenne az így létrejött oldalállapothoz egy URL-t rendelni.
A HTML5 history API bővítése ezt teszi lehetővé. A pushState() metódus meghívásával úgy lehet újabb URL-t az oldalhoz rendelni és a böngészési előzményeket bővíteni, hogy az egész oldal ne töltődjön újra. Minden oldalhoz el lehet menteni egy állapotleíró objektumot (state paraméter). Amikor a felhasználó megnyomja a vissza gombot a böngészőjében, akkor erről a popstate eseményen keresztül értesülhetünk, és ennek eseményobjektumán keresztül hozzáférhetünk az elmentett állapothoz. Ezek mellett még van egy replaceState() metódus, mely – mint azt a neve is mutatja – az aktuális URL-t cseréli a böngészési előzményekben. Az oldalhoz tartozó állapotot pedig a history.state tartalmazza.
A history API újdonságait az alábbi kis programmal szemléltethetjük. Egy oldalon van egy számláló. Egy gombot nyomogatva a számláló értéke nő, és ez az oldalon is megjelenik. A vissza gombot megnyomva az előző állapotra szeretnénk jutni. Ehhez a következőt kell tennünk: a gomb megnyomásakor elmentjük az oldal állapotát, a vissza gomb lenyomásakor bekövetkező eseményt kezelve pedig lekérdezzük azt, és kiírjuk. A minimális HTML a következő:
<p>Number: <span id="number"></span></p> <input type="button" id="next" value="Next"> <script type="text/javascript" src="history.js"></script>
A history.js pedig a következőképpen néz ki:
var counter; var init = function() { document.getElementById('next').addEventListener('click', next, false); document.getElementById('number').innerHTML = counter; window.addEventListener('popstate', change, false); window.addEventListener('load', change, false); counter = window.location.search.substr(1) || 1; } var next = function() { counter += 1; var state = { counter: counter }; window.history.pushState(state, null, '/history/history.html?' + counter); document.getElementById('number').innerHTML = counter; } var change = function(e) { var state = e.state; counter = (state && state.counter) || 1; document.getElementById('number').innerHTML = counter; } init();
Az oldalt a localhost/history/history.html URL-en keresztül érhetjük el. Az init() függvény az oldal betöltésekor fut le, ebben kiolvassuk az URL ? utáni részét, ha van. A next() függvény a gomb megnyomásakor aktivizálódik, itt módosítjuk a history objektumot, a change() függvény a popstate eseménykor fut le.
A window.navigator objektumon keresztül lehetőség van lekérdezni a szkriptet futtató böngésző adatait. Régebben ezt arra használták, hogy az eltérően működő böngészők igényeinek megfelelően futtassák a kódot. Ezt böngésződetektálási technikának hívják (angolul browser detection). Ezzel a technikával az a baj, hogy a kódunk nem képes az újabb böngészőkre felkészülni, nem jövőbiztos megoldás, és a böngésző szignatúrája egyszerű böngészőkiterjesztésekkel megváltoztatható, azaz szkriptünk becsapható. Ezek miatt a böngésződetektálás elavult technikának számít. Helyette az ún. tulajdonságdetektálást (angolul feature detection) alkalmazzuk olyan esetekben, amikor nem vagyunk biztosak abban, hogy egy adott tulajdonság vagy metódus az adott böngészőben elérhető. Ekkor a tulajdonság meglétére kérdezünk rá egy elágazásban, és annak meglétekor alkalmazzuk a kérdéses tulajdonságot. Ezzel a szkriptünk jövőbiztossá abban az értelemben, hogy ha a tulajdonság valamikor is elérhető lesz az adott böngészőben, akkor a szkript ki is fogja használni.
Az alábbi példában a megfelelő eseményregisztrálási módot választjuk ki:
//Böngésződetektálás (elavult) if (navigator.userAgent.indexOf('MSIE') !== -1) { document.attachEvent('onclick', console.log); } //Tulajdonságdetektálás if (document.attachEvent) { document.attachEvent('onclick', console.log); } //Vagy még specifikusabban if (typeof document.attachEvent !== "undefined") { document.attachEvent('onclick', console.log); }
A window objektum néhány hasznos metódussal is kiegészíti a JavaScript nyelvi magot, így például a függvények futásának időzítésére szolgáló setTimeout(fn, ms) és setInterval(fn, ms) függvényekkel. Első paraméterében mindkettő egy nevesített vagy anonim függvénykifejezést vár, de amíg az előbbi a második paraméterében megkapott idővel (milliszekundumban) késleltetve futtatja a függvényt, addig az utóbbi ugyanezen paraméterét felhasználva, meghatározott időközönként újra és újra meghívja azt. Mindkettő egy number típusú azonosítóval tér vissza, melynek segítségével a clearTimeout megakadályozza a függvény futását (ha az még nem történt meg), illetve a clearInterval – hasonló módon – véget vet az adott időközönkénti újrahívásnak.
//setTimeout és clearTimeout példa var delayed = function() { console.log('Etel evett egy tehenet'); } // Nevesített függvénykifejezéssel var a = setTimeout(delayed, 1000); // 1mp múlva megjelenik a konzolon: 'Etel evett egy tehenet' // Anonim függvénnyel ugyanez var b = setTimeout(function() { console.log('Etel evett egy tehenet'); }, 1000); // Az előbbi (időzített anonim) függvény futásának megelőzése if (typeof b === "number") { clearTimeout(b); } //setInterval és clearInterval példa var c = setInterval(function() { console.log('de lehet nem ette meg!'); }, 1000); // 1mp-es időközönként megjelenik a konzolban: 'de lehet nem ette meg!' // A setInterval leállítása if (typeof c == "number") { clearInterval(c); }
Ha egy meglehetősen kis értéket (pl. nullát) adunk át a setTimeout()-nak, akkor a paraméterül adott függvény nem azonnal fog lefutni, hanem akkor, amint lehetséges. Ez azt jelenti, hogy az adott eseményhez (oldal betöltődés, kattintás, stb.) kapcsolt összes eseménykezelő (gyakorlatilag JavaScript kód) lefut, majd az utóbbi csak ezek után fog kiváltódni. Hasznos trükk, ha egy esemény után közvetlenül szeretnénk valamit futtatni, gyakorlatilag egy esemény destruktorát tudjuk így definiálni. Gyakran használják a setTimeout()-ot hosszan futó feldolgozási logikák részekre bontására is.
A setInterval() használata kézenfekvőnek tűnhet, azonban mégis elkerülendő. A probléma ugyanis az, hogy a függvény adott időközönként mindig megpróbálja futtatni azt, amit átadunk neki. Ezzel alapvetően nem is lenne gond, azonban előfordulhat, hogy hosszasabb számolások még nem értek véget, amikor a setInterval() már le akarna futni. Mivel nem tud, így várakozik – idővel aztán az is elképzelhető, hogy több setInterval() lépés is egymás után fog torlódni. Amint lehetőséghez jutnak, az értelmező egymás után, hirtelen lefuttatja a sorban álló függvényeket. Az eredmény – érthető módon – nem valami szép. A setInterval() kiváltására a lenti példához hasonlóan, a setTimeout függvényt használjuk önmaga futását időzítve. Ekkor mindig, még a leghosszabb várakozások során is, csak legfeljebb egy lépés várakozik, ami szépen megvárja, hogy rá kerüljön a sor – így nem torlódnak egymásra a függvényeink.
var d; (function tick() { console.log('de lehet nem ette meg!'); d = setTimeout(tick, 1000); }()); clearTimeout(d);
A DOM egy olyan programozói interfész, amin keresztül a megjelenített HTML dokumentum manipulálható. Tulajdonképpen nem más, mint a HTML dokumentum API-ja. A dokumentum objektummodell tetszőleges fastruktúra leírásához és kezeléséhez szükséges ismeretek tartalmazza egy szabvány keretében. Mivel a HTML dokumentum elemei is ilyen hierarchiát alkotnak, így a HTML struktúrára is alkalmazható modellről van szó (HTML DOM).
A DOM minden HTML elemnek egy objektumot feleltet meg rengeteg adattaggal, és ezeket éppúgy rendezi fastruktúrába, ahogy a HTML elemek is egymásba vannak ágyazva. A DOM tehát nem más, mint a HTML elemek fastruktúrájának leképezése egy objektum-fába. A fa gyökere a document objektum, ezen keresztül érhető el bármelyik elem az oldalon. Ahogy a HTML dokumentum a böngésző része, úgy a document is a window objektumon keresztül érhető el (window.document).
A megjelenített HTML elemek és a nekik megfelelő DOM objektumok között is élő kapcsolat van. Azaz a DOM objektumban megváltoztatva egy értéket, az azonnal a megjelenítésben is változással jár, és visszafele, a felületi változások rögtön kiolvasható az adott elemnek megfelelő DOM objektumon keresztül. Ennek az élő kapcsolatnak a fenntartása rengeteg erőforrást vesz el a böngészőtől, ezért lehetőleg ritkán nyúljunk hozzá. Általában szkriptünk leglassabb része a felületi elemek kezelésével kapcsolatos.
A DOM egy hatalmas szabvány, rengeteg adattagot és metódust definiál. Ebben a tananyagban nem célunk ezt mind bemutatni, csupán néhány hasznos metódust és programozási mintát ismertetnénk ebben a fejezetben.
A DOM csomópontok fáját definiálja. A HTML DOM sokféle csomópontot különböztet meg, ezek közül a fontosabbak a következők:
Az alábbi csoportokba soroljuk a DOM műveleteket:
Ahhoz, hogy egy adott elemmel dolgozzunk, el kell jutnunk hozzá. Ez megtehető a gyökérelemtől történő bejárással (ld. nemsokára), de sokkal egyszerűbb valamilyen tulajdonságánál fogva kiválasztani az elemeket. Erre a DOM több lehetőséget is ad.
Dokumentumszinten:
Dokumentum vagy bármilyen elem szinten:
A legtöbb DOM metódus valamilyen gyűjteménnyel tér vissza. Ezek vagy HTMLCollection vagy NodeList típusúak, de alapvetően ugyanazt tudják. Amit érdemes róluk tudni, hogy általában élő gyűjtemények, azaz mindig tükrözik az adott dokumentum állapotát, azaz változik a hosszuk és a tartalmuk, ha közben a dokumentum szerkezete is megváltozik. Ez alól kivételt a querySelector() és querySelectorAll() metódusok jelentenek.
Egy másik fontos tulajdonságuk ezeknek a gyűjteményeknek, hogy úgy viselkednek, mint a tömbök, de mégsem azok. Számokkal indexelhetők, és van length tulajdonságuk, ezért egy for ciklusban feldolgozhatók, de tömbmetódusok nem érhetők el rajtuk. Ha ezeket használni szeretnénk, akkor át kell alakítanunk őket, pl. a slice() metódus kölcsönvételével, de ekkor élő tulajdonságukat elvesztjük.
//Paragrafusok lekérdezése var pars = document.getElementsByTagName('p'); //Tömbbé alakítás var arrayOfPars = [].slice.call(pars); //Sorrend megfordítása arrayOfPars.reverse();
Az egész faszerkezet bejárható a dokumentum gyökerétől a szülő- és gyerekelemek kapcsolatán keresztül. Ebben a szerkezetben minden elemnek csak egy szülője lehet (a dokumentumnak nincs), és valahány gyerekeleme (0 vagy több). Az alábbi felsorolásban mindegyik tulajdonságként (és nem metódusként) érhető el, és null értékkel térnek vissza, ha nincs megfelelő csomópont.
Gyerekelemek lekérdezése:
Szülőelem lekérdezése:
Testvércsomópontok lekérdezése:
A DOM bejárásában gyakran nehézséget okoz, hogy a szöveges csomópontok az elemek csomópontjaival együtt jelennek meg a tulajdonságok által visszaadott gyűjteményekben. Ezeket a nodeType attribútumuk alapján ki lehet szűrni, ugyanis ez szöveges csomópontoknál 3.
Az elemhez tartozó attribútumok kezelésére az alábbi metódusok és tulajdonságok állnak rendelkezésre:
Az elemek eggyel magasabb szintű módosítása, amikor nem az elem tulajdonságait módosítjuk, hanem új elemeket hozunk létre, meglévőeket helyezünk át vagy törlünk a dokumentumból.
Új elem létrehozása:
Elem áthelyezése:
Elem törlése:
Az előzőekben betekintést nyerhettünk abba a programozói interfészbe, amely a JavaScript nyelvi mag és a megjelenített HTML elemek közötti kapcsolatért felelős. A DOM egy nagyon hasznos és szükséges interfész a kliensoldali programozásban. Ugyanakkor azt is látni kell, hogy használata sokszor igen nehézkes. Ennek egyik elsődleges oka az, hogy – történeti okok miatt ugyan – mind a mai napig vannak eltérések az egyes böngészők implementációjában. Szerencsére az írás időpontjában a böngészők legutóbbi verziói már viszonylag egységesek, mégis gondot okozhat az, hogy az egyik böngésző eltérő módon támogat bizonyos funkciókat, mint a másik. Ez a probléma továbbá mindaddig aktuális lesz, amíg a böngészők régebbi típusait is támogatni szükséges. A DOM használatában rejlő nehézségek másik oka a szabvány mindenre kiterjedő aprólékossága. Ez alapvetően nem baj, hiszen ezáltal válik az oldal a legutolsó betűig manipulálhatóvá, de sokszor plusz akadályokat gördít az oldal feldolgozása elé. Példának okáért a DOM fa bejárásakor általában nincsen szükségünk a szöveges csomópontokra, ezek mégis megjelennek a csomópontlistában, és további elágazásokkal szűrhetők ki. Végül a gyors fejlesztést maga a bőbeszédű DOM API is akadályozhatja: a tulajdonság és metódusnevek sokszor nagyon hosszúak, és emiatt kényelmetlennek tűnhetnek.
Ezen a ponton kerülnek a fejlesztők látóterébe az egyes kliensoldali keretrendszerek, amelyek alapvetően három célt próbálnak elérni. Egyrészt elfedik az egyes böngészők és böngészőverziók közötti különbségeket egy egységes, absztrakt interfész kialakításával; másrészt további, magasabb szintű műveleteket vezetnek be a gyakran előforduló problémákra; végül teszik ezt a keretrendszer mögött álló filozófiának megfelelő programozói stílusban. A keretrendszerek tehát egy olyan absztrakt réteget alakítanak ki a DOM felett, amivel teljesen elfedik annak fapados, hibás és kényelmetlen programozói felületét, csupán e réteg utasításainak ismeretében bármit el tudunk érni, amit egyébként a DOM segítségével megtehetnénk.
A következőkben egy keretrendszer, a jQuery segítségével fogjuk megismerni a böngészővel való kapcsolattartást. Tehetnénk ezt bármelyik társával is, hiszen a mai keretrendszerek funkcióikban, sebességükben már nagyon hasonlatosak – azonban a kódolás stílusukban mindegyik egy kicsit máshogy tekint a nyelvre. Van, amelyik a klasszikus objektum-orientált szemléletet akarja rákényszeríteni a JavaScriptre, de van olyan is, amelyik a nyelv adott, funkcionális lehetőségeit próbálja kiaknázni. A jQuery az utóbbiak közé tartozik, emiatt közelebb áll a natív DOM programozáshoz is. Egyszerű, intuitív és átlátható stílusa miatt, rövid idő alatt az egyik legnépszerűbb keretrendszerré vált.
A jQuery egy gyors és tömör JavaScript függvénykönyvtár, használata megkönnyíti a HTML dokumentumok bejárását, eseménykezelését, animálását és a különböző Ajax műveleteket. Első változatai John Resig nevéhez fűződnek és 2005-re datálódnak, ám szélesebb körben csak egy évvel később, az Ajax felfedezésének idején terjedt el. Hirtelen népszerűségét nem csak annak köszönhette, hogy a hasonló keretrendszerek nagyok és nehézkesek voltak hozzá képest: a jQuery egy új szemléletmódot testesített meg a JavaScript alapú fejlesztésben. Nem csupán a modern és kényelmes tervezési mintákat hozta (és máig hozza) be a köztudatba, hanem ezeket úgy tálalta, hogy nem rugaszkodott el a JavaScript alapvető programozási stílusától és a kezdők számára is érthető tudott maradni.
Mint minden keretrendszerben, így a jQueryben írt kódunk is böngészőfüggetlen lesz, azaz bármilyen böngészőben ugyanazt az eredményt fogjuk kapni. A saját és mások kódjai biztonságban lehetnek, nem kell névütközéstől tartani, hiszen a jQuery csupán két változóval terheli a globális névteret (jQuery és $) és a JavaScript beépített objektumaihoz sem nyúl. Ha szükség van rá, minden további nélkül együttműködik egy másik keretrendszerrel vagy önmaga korábbi verzióival, így a kompatibilitási problémákat is elkerülhetjük, ha egy idegen rendszerbe szeretnénk jQuery alapú kódunkat elhelyezni.
A népszerűségére jellemző, hogy saját bővítménytára van, melyet a lelkes közösség már több ezer kiegészítéssel töltött fel. Ha valamit nem tudnánk, vagy időnk nem engedi megoldani, akkor több mint valószínű, hogy találunk itt egy olyan bővítményt, amely leveszi vállunkról ezt a terhet.
A keretrendszer egyes részeit 2009-ben szétválasztották, így alakult meg a jQuery Project, mely többek között magába foglalja a jQuery Core-t, annak szelektorokkal foglalkozó részével, a Sizzle-lel, továbbá a jQuery UI, a jQuery Mobile és a QUnit nevű egységtesztelésre használatos keretrendszert. Az alábbiakban elsősorban a jQuery Core-ral foglalkozunk, a többire csak különleges esetekben van szükségünk.
Amikor feladatot oldunk meg a felületi elemekkel kapcsolatban, akkor ez általában a következő lépésekben történik:
A jQuery ezeket a lépéseket könnyíti meg:
Ami marad, az a lényeg: mely elemekkel mit szeretnénk elvégezni. Mindehhez pedig egy nagyon beszédes API társul. Ha például az összes paragrafust szeretnénk eltüntetni, akkor ezt a következőképpen tehetjük meg:
$('p').hide();
A jQuery filozófia egyik központi eleme a jQuery objektum. Ez az az adatszerkezet, amelyet az elemek kiválasztásakor kapunk vissza. Ez egy tömbszerű objektum, ami egy vagy több HTML elemnek megfelelő JavaScript objektumot tartalmaz, és ezeken számos hasznos művelet elvégzésére ad lehetőséget. Létrehozása a jQuery() vagy $() függvény segítségével lehetséges.
Ezzel el is érkeztünk a jQuery filozófia másik fontos eleméhez, a $() függvényhez. Ez hármas célt szolgál:
A jQuery műveletek néhány nagyobb kategóriába csoportosíthatók:
Az alábbiakban a fontosabb műveletcsoportokat emeljük ki. Itt is hangsúlyozzuk, hogy ez a tananyag nem referencia, hanem csupán iránymutatás a témakör iránt érdeklődőknek. A hivatalos dokumentáció a jQuery honlapján olvasható.
Ahogy fentebb már említettük, az elemek kiválasztása kizárólag CSS szelektorokkal történik. A jQuery a CSS 1-3 szelektorokat támogatja régebbi böngészőkön is (ott általában a teljesítmény rovására). Ezen kívül számos további elemmel egészítették ki a szelektorok körét. Körülbelül a következő szelektorok vannak:
A kiválasztott elemek számát a jQuery objektum length tulajdonsága adja meg.
$("#profilkep"); $(".adat"); $("li"); $("div.adat"); $("#profilkep, #adatok"); $("img[src=profil.jpg]"); $("ul li.adat b"); $("ul li:first"); $("b:contains('Végzettség:')");
Az elemek kiválasztása nagyobb dokumentum vagy bonyolultabb szelektorok esetén igen költséges lehet, így ha ezekkel az elemekkel többször szeretnénk dolgozni, akkor több lehetőségünk van:
A cache-elés során egy változóba mentjük a jQuery objektumot, így azt nem kell újra és újra előállítani. Sokszor ez a változó a magyar jelölés szerint $ jellel kezdődik, jelezvén, hogy a $() függvény eredményeképpen jött létre. Érdemes figyelnünk arra, hogy az oldal tartalmának változásával az eltárolt jQuery objektum nem változik, így ilyen esetekben szükséges lehet tartalmának frissítése.
var $p = $('p');
A jQuery metódusok nagy része egymás után fűzhető, mivel a jQuery objektumot adják vissza. Ezzel a megoldással is több metódust alkalmazhatunk egy kiválasztás esetén. Ezt a megoldást láncolásnak hívják, és a programozási mintáknál foglalkozunk az implementációs kérdéseivel.
$('p').addClass('important').hide();
A kiválasztott elemektől kezdve lehetőségünk van a fastruktúra bejárására. Az alapvető koncepció nem változott a DOM-hoz képest, változás egyrészt az egyszerűbb elnevezésekben, másrészt abban van, hogy a jQuery a bejárás során automatikusan kihagyja a szöveges csomópontokat.
A kiválasztott elem(ek)hez képesti elmozdulásért, illetve az ottani elemek kiválasztásáért a következő metódusok felelősek:
A kiválasztott elemeket tovább módosíthatjuk:
A legtöbb függvény CSS szelektorokat is elfogad, így csak azok az elemek kerülnek kiválasztásra, amelyek megfelelnek ezeknek. Ezek a függvények továbbá módosítják a kiválasztott elemek körét is (új jQuery objektum jön létre), az előző azonban egy kiválasztási verembe kerül. Az előző kiválasztáshoz az end() metódus segítségével lehet visszatérni.
//Bejárás // szülő kiválasztása $("#adatok").parent(); // a következő elem kiválasztása $("li").next(); // az előző elem kiválasztása $("li").prev(); // az előző elem kiválasztása, de csak ha adat osztályú $("li").prev(".adat"); // az elem felmenői közül az első, amelyikre igaz, hogy div $("b:first").closest('div'); // az elem leszármazottai, melyekre igaz, hogy adat osztályúak $("#adatok").find(".adat"); // a kiválasztott elemek közül az első $("li").first(); // a kiválasztott elemek közül az utolsó $("li").last(); // a kiválasztott elemek közül a harmadik $("li").eq(2); // az elem azon testvérei, melyek h1 típusúak $("#adatok").siblings("h1"); //Szűrés // az elem jQuery objektumához hozzáadjuk az összes li-t is $("#adatok").add("li"); // a választásból kivesszük az összes li típusút $("#adatok, li").not("li"); // a kiválasztott elemekből csak a képek maradnak $(".adat").filter("img"); //Visszalépéses szelekció $("#adatok") // hatókör: div#adatok .find("li") // hatókör: div#adatok .css({ padding: "5px" }) // hatókör: div#adatok li .find("b") // hatókör: div#adatok li .html('Csak a címeket hackoltam meg!!!') // hatókör: div#adatok li b .end() // hatókör: div#adatok li b .end() // hatókör: div#adatok li .animate({ paddingTop: '+=100' }, 200);
Ha kiválasztottunk néhány elemet, gyakran szeretnénk velük valami speciális dolgot is tenni, nem csupán a többi jQuery metódust használni rajtuk. Erre a keretrendszer az each() nevű függvényt biztosítja, mely úgy fut végig az elemeken, hogy a paraméterül kapott függvényt mindegyikre meghívja. Gyakorlatilag egy – máshonnan talán ismerősebb – for-each szerkezetet kapunk. Természetesen az átadott függvény – mint mindenhol – ezúttal is lehet anonim vagy nevesített.
A paraméterül adott függvény this változójában az az elem lesz, melynél az iterálás éppen tart. Vigyázzunk azonban, a this az elem DOM objektumát fogja tartalmazni – ezt még át kell konvertálnunk jQuery objektummá ($(this)), mielőtt a megszokott metódusainkat futtatnánk rajta.
var cimkek = []; $("b").each(function() { cimkek.push( $(this).text().replace(':','') ); }); cimkek.join(', ');
A jQuery rengeteg lehetőséget biztosít a HTML struktúra módosítására. Ezeket referenciaszerűen felsorolni nem célunk, csupán néhány műveletcsoportot emelünk ki a jobb áttekinthetőség érdekében.
//Új jQuery objektumok létrehozása var $a = $("<a class='12a' href='index.html'><b>Béla, Bulcsú</b></div>"); // Hasonló elemdefiníció attribútum-objektummal: var $a = $("<a />", { className: '12a', href: 'index.html', html: '<b>Béla, Bulcsú</b>' }); //Attribútum és tartalom módosítása $("<a />") .addClass('12a') .attr('href', 'index.html') .html('<b>Béla, Bulcsú</b>') .appendTo($("body")); $("body").find('.12a').attr('href'); // => 'index.html' $("body").find('.12a').html(); // => '<b>Béla, Bulcsú</b>', HTML tartalom $("body").find('.12a').text(); // => 'Béla, Bulcsú', tartalom tag-ek nélkül //jQuery objektumok beillesztése és törlése $a.appendTo($("body")); // $a beillesztése a body végére $a.prependTo($("body")); // $a beillesztése a body elejére $a.insertAfter($("#adatok")); // $a beillesztése az adatok id-jű div után $a.insertBefore($("#adatok")); // $a beillesztése az adatok id-jű div elé $a.remove(); // $a elem törlése //Ugyanez másik szemszögből $("body").append($a); // A body végére illeszti a $a-t $("body").prepend($a); // A body elejére illeszti a $a-t $("#adatok").after($a); // Az adatok id-jű div után illeszti a $a-t $("#adatok").before($a); // Az adatok id-jű div elé illeszti a $a-t
A HTML manipulálása a DOM élő kapcsolata miatt nagyon költséges dolog. Jól bevált gyakorlat, hogy próbáljunk a DOM-hoz minél kevesebbszer nyúlni, az ott elvégzendő módosításokat a lehető leghatékonyabban elvégezni.
Ha egy HTML struktúrát építünk fel JavaScriptben és ezt akarjuk hozzácsatolni a weboldalunkhoz, akkor az elemeket ne egyenként adjuk hozzá az oldalhoz, hanem egy lépésben.
var $adatok = $("#adatok"), $ujStruktura = $("<div />"); for (var i=0, $div; i<10; i++) { $ujStruktura.append("<div>"+i+"</div>"); } $ujStruktura.appendTo($adatok);
Ha több CSS tulajdonságot egyszerre módosítunk valamely elemen, akkor megfontolandó, hogy inkább egy CSS osztályba tegyük ezeket az értékeket és egyszerűen ezt az osztályt adjuk át az elemnek – a sok kis attribútum-változtatás helyett így csak egyetlenegyszer nyúlunk a DOM objektumhoz.
//Sok egyedi módosítás $(".adat").css({ color: 'red', fontSize: '14px', lineHeight: '16px' }); //E helyett egy .kiemelt { color: red; font-size: 14px; lineHeight: 16px; } CSS osztályra való csere optimálisabb $(".adat").addClass('kiemelt');
Ha nem lehetünk biztosak benne, hogy a kiválasztott elem létezik, akkor mindenképpen ellenőrizzük mielőtt több metódust is futtatnánk rajta – bár hiba nem fog történni, de az összes metódust meghívja, felesleges függvényhívásokat generálunk ezzel.
$(".nemLetezoElem").css({ width: '100px'}).html('Nem létezek?'); // A fenti helyett optimálisabb ha megvizsgáljuk a szelekció sikerességét var $nemLetezo = $(".nemLetezoElem"); if ($nemLetezo.length) { $nemLetezo.css({ width: '100px'}).html('Nem létezek?'); }
A jQuery-ben van még egy speciális lehetőség a DOM-manipulálások lassúságának elkerülésére. A detach metódus segítségével úgy választhatjuk le a módosítandó elemet vagy elemeket az oldalstruktúráról, hogy annak minden tulajdonságát, eseménykezelőjét megtartjuk. Ezt követően nyugodtan állítgathatunk bármit, módosíthatjuk a tartalmát, csak a jQuery objektumon fog megtörténni a változtatás, a DOM-ban nem. Ha végeztünk minden módosítással, akkor visszailleszthetjük az oldalunkba.
//Sok egyedi manipulálás $("#adatok").css({ width: '100px', color: 'red', opacity: 0.5 }).html('Izébizé'); //Helyette: leválasztás, manipulálás, visszaillesztés $("#adatok") .detach() .css({ width: '100px', color: 'red', opacity: 0.5 }).html('Izébizé') .appendTo($("body"));
Ugyan a stílusok kezelése a DOM része, hiszen tulajdonképpen HTML attribútumok beállításáról, illetve a style attribútum mögött álló CSSProperties objektum módosításáról van szó, jQuery-ben mégis kiemelt szerepet kapnak a stílusmanipulálással kapcsolatos függvények, ugyanis a felhasználói felület megjelenéséhez, és így a felhasználói elégedettséghez a stílusok jelentős mértékben járulnak hozzá. Ezeken keresztül valósulnak meg ugyanis a különböző animációk és effektusok, amik nélkül a modern felületeken szinte elképzelhetetlenek. A jQuery a CSS jól megkülönböztetett szintjeihez, az egyedi stílusinformációk, valamint a stílusosztályok kezeléséhez ad magasabb szintű metódusokat, illetve az animációk készítését könnyíti meg.
A kiválasztott elemek egyedi stílustulajdonságait (style attribútum) a css() metódus segítségével lehet beállítani. Ez vagy két paraméterként fogadja el a beállítandó stílustulajdonság nevét és értékét, vagy többet is megadhatunk egyetlen objektumliterálban. A lekérdezés is ezen a függvényen keresztül történik (számított stílus).
Stílusosztályok kezelésére az addClass(), removeClass(), toggleClass() és hasClass() metódusok szolgálnak.
Az animáció tulajdonképpen nem más, mint a CSS tulajdonságok gyors, egymás utáni változtatása. Ehhez a jQuery egy igen kifinomult animációs keretrendszert biztosít számunkra az animate() metóduson keresztül. Ebben paraméterként megadható, hogy
A gyakran előforduló, alapvető effektusok előre megvalósítva elérhetőek magas szintű metódusokként. Ezeknek paraméterül meglehet adni az animáció sebességét és további paramétereket (ld. referencia):
Az animációkra egységesen elmondható, hogy egy elemen alkalmazott több animáció ún. animációs sorba kerül, azaz egymás után kerül alkalmazásra az elemen. Több elemen meghívott animáció viszont párhuzamosan fut le. Az animációs sor kezelésére is kapunk függvényeket a jQuery-től a queue(), dequeue() és stop() személyében.
//Egyszerű animáció $("#adatok").animate({ marginTop: '100px' }, 400); // Ugyanazon elemen futtatott két animate() metódus egymás után fog lefutni, tehát a második megvárja az elsőt $("#adatok").animate({ marginTop: '100px' }, 400).animate({ marginTop: 0 }, 300); //Párhuzamos animációk, két külön elemen futtatott animáció párhuzamosan történik meg $("#adatok").animate({ marginTop: '100px' }, 400) .animate({ marginTop: 0 }, 300); $("li").animate({ fontSize: '30px', opacity: 0.5 }, 300) .animate({ fontSize: '12px', opacity: 1 }, 400); //Az animate paraméterei //Relatív módosítás, azaz mindig 100-al növeli a felső margót. A harmadik paraméter a csillapítás, mely alapesetben a "linear", de további pluginek nélkül még egy választható, a "swing". A negyedik paraméter egy callback függvény, mely akkor kerül meghívásra, amikor az animáció lefutott $("#adatok").animate({ marginTop: '+=100' }, 400, "swing", function() { alert("Véget ért a móka mára."); }); //Magas szintű művelet $('li').hide('slow');
Egy elem méretének és helyzetének lekérdezése és beállítása nem tartozik az egyszerű dolgok közé, mivel az értékek függnek attól, hogy mihez képesti elrendezést vizsgálunk, illetve hogy beleszámoljuk-e a méretbe a margó, keret, bélés értékeit. Ezekhez a tipikus feladatokhoz a jQuery tartalmaz pár hasznos függvényt:
A böngészők az egyes történésekre való reagálást eseményvezérelt módon biztosítják, hasonlóan az asztali alkalmazásokban megszokottakhoz. Ebben a modellben a dokumentum (és az ablak) elemei különböző eseményeket tudnak kibocsátani (tipikusan a felhasználói tevékenységre reagálva), és JavaScriptben ezekhez az eseményekhez kapcsolhatunk függvényeket, amelyek az esemény bekövetkeztekor futnak le. Ez utóbbiakat eseménykezelő függvényeknek hívjuk. Nagyon sokféle esemény van, amelyek egy részét a DOM szabvány eseményekkel kapcsolatos része szabályozza, másik része viszont a böngészőkbe integrált újabb és újabb funkcionalitásoknak megfelelően egyre növekszik. A kiváltott események darabszáma is igen nagy lehet. Például egy egyszerű egérmozgatáskor az összes elem, amely fölött az egérmutató elhalad egy mousemove eseményt tesz közzé nem is egyszer. Ebben az esetben az esemény kiváltó oka az egér mozgatása volt a felhasználó részéről, erre válaszul bocsátották ki az elemek a mousemove eseményt, amelyre egy függvénnyel feliratkozva programozottan lehet reagálni a kódban. Az események sokféleségét ez a példa is jól szemléltetheti, hiszen a mousemove mellett minden elem azt is jelzi, ha belépett a területére az egérmutató (mouseover) vagy ha elhagyta azt (mouseout).
Az eseménykezelés a böngészők történetének egyik olyan eklatáns területe volt, ahol az egyes böngészőimplementációk között jelentős eltérések voltak, megkeserítve a webfejlesztők munkáját. Az egyes kliensoldali keretrendszerek létrejöttét éppen ezek ez eltérések kezdeményezték. Szerencsére mára ezek a különbségek eltűnőben vannak, és az eseménykezelés a modern böngészőkben szabványosan és egységesen történik.
Az eseménykezeléshez kapcsolódó ismeretek az alábbiak:
Bár történetileg nagyon sokféle módszer alakult ki, manapság a szabványos módszer jelenti a legtisztább és leghatékonyabb megoldást eseménykezelő függvények regisztrálására. Ennek kezelése két függvény segítségével történik:
Ezek a metódusok bármelyik DOM elemnél elérhetők. Mindkét függvénynél a useCapture azt a fázist jelöli ki, amikor az eseményt kezelni szeretnénk (ld. buborékolás).
var hello = function () { console.log('Hello!'); }; var adatok = document.getElementById('adatok'); adatok.addEventListener('click', hello, false);
Az esemény részleteiről az eseménykezelő függvényen belül az eseményobjektum tartalmaz információkat. Ezt az eseménykezelő függvény első paramétereként kapjuk automatikusan. Történetileg itt is különböző megoldások születtek, de a modern böngészők ebben is egységesek.
var listener = function (e) { };
Az eseményobjektum az esemény körülményeiről tartalmaz információkat. Ezek között vannak olyanok, amelyek minden típusú eseménynél megjelennek, de vannak olyanok is, amelyek függnek az esemény típusától. Az eseményobjektum tulajdonságainak egy részéről a szabvány gondoskodik, a többi részéről pedig JavaScript konzol segítségével győződhetünk meg kiíratva az eseményobjektumot.
var listener = function (e) { console.log(e); };
A fontosabb közös tulajdonságok a következők:
Egy egéresemény ezek mellett a következő információkat tartalmazhatja:
Egy billentyűzetesemény pedig többek között a lenyomott billentyű kódját tartalmazza (keyCode).
Néhány eseményhez alapértelmezetten tartozik valamiféle művelet is. Ilyen például az, hogy ha egy hivatkozásra kattintunk, akkor a böngésző betölti a link URL-je mögötti oldalt, vagy egy szöveges beviteli mezőben lenyomva valamelyik billentyűt, az beíródik a mezőbe. Ezeket az alapértelmezett műveleteket az eseményobjektum preventDefault() metódusának meghívásával lehet megakadályozni.
A kiváltott események számát és a programozási lehetőségeket jelentősen megnöveli az, hogy egy eseményt nemcsak az az objektum jelez, aki közvetlenül érintett volt, hanem az események láncszerűen az összes szülőelemen is jelzésre kerülnek. Az esemény tehát az adott elemtől felmegy a legfelső, dokumentumszintig. Ez alapján ezt a jelenséget buborékolásnak nevezzük.
A teljes képhez hozzájárul az is, hogy ezt követően dokumentumszinttől a közvetlenül érintett elemig is kiváltódnak az események. Ez utóbbit elkapási fázisnak nevezik. Az addEventListener() és removeEventListener() metódusok utolsó, logikai paramétere éppen azt határozta meg, hogy szeretnénk-e az eseménykezelőt az elkapási fázisban aktivizálni. A legtöbb feladat elvégezhető buborékolási fázisban, ezért általában oda hamis értéket adunk meg.
Bármelyik szinten kapjuk is el az eseményt, az eseményt elsőként jelző elem mindig lekérdezhető az eseményobjektum target tulajdonságán keresztül.
A buborékolás, vagy az esemény továbbhaladása megállítható, az eseményobjektum stopPropagation() metódusával.
var listener = function (e) { console.log(e.target); e.stopPropagation(); };
A buborékoláshoz egy gyakran alkalmazott, hatékony programozási minta is kapcsolódik. Képzeljük el, hogy egy táblajátékot fejlesztünk, ahol minden cellában történő kattintásra valamilyen módon szeretnénk reagálni. Ha minden cellához külön-külön rendelnénk eseménykezelő függvényt, akkor egyrészt igencsak megnövekedne az eseménykezelők száma, ami mind a programozó, mind a böngésző részéről plusz erőforrásokat kíván a kezelésükhöz, másrészt további sorok és oszlopok beszúrására nem lenne elég rugalmas. Ilyen esetekben kihasználhatjuk a buborékolás jelenségét, és az eseménykezelőt az összes érintett elem valamelyik közös ősén, ebben az esetben például a <table> elemen kezelhetjük, viszont az eseményobjektumon keresztül az eseményben érintett <td> cella lekérdezhető. Az egész feladathoz így egyetlen eseménykezelő regisztrálása szükséges, ráadásul a megoldás a táblázaton belüli cellák számától és dinamikus változásától is független. Ezt a fajta eseménykezelést hívjuk delegálásnak.
Lehetőség van beépített és saját események kiváltására is a szabványos dispatchEvent() metódus segítségével. Paraméterül egy Event interfészt vagy annak valamilyen leszármazottját megvalósító objektumot kell megadni. Ilyen lehet pl. többek között a MouseEvent vagy KeyboardEvent, egyedi eseményeknél pedig a CustomEvent.
//Beépített esemény kiváltása: a függvény a paraméterül megkapott jelölőmezőre kattintást szimulálja var simulateClick = function(checkbox) { var event = new MouseEvent('click', { 'view': window, bubbles: true, cancelable: true }); return checkbox.dispatchEvent(event); }; //Egyedi esemény kiváltása var publishCustomEvent = function(obj, type, data) { var event = new CustomEvent(type, { detail: data, bubbles: true, cancelable: true }); return obj.dispatchEvent(event); }; var body = document.body; body.addEventListener('something', function(e) { console.log(e.detail); }, false); publishCustomEvent(body, 'something', { foo: 42 });
A jQuery keretrendszer akkor született meg, amikor még a böngészők között jelentős eltérések voltak az eseménykezelést illetően, ezért benne is megjelentek megoldások ezen eltérések elfedésére egy absztrakt interfész mögött. A manapság ajánlott API-t számos korábbi előzte meg, de mára elmondhatjuk, hogy jQuery-ben is az eseménykezelésért egyetlen műveletcsoport a felelős: az on() és az off() metódusok. Az előbbi az eseménykezelők regisztrálásáért, az utóbbi a leiratkozásért felel. Ezeknek a metódusoknak alapvetően kétféle paraméterezése van (részletekért ld. a dokumentációt):
Az első változatot akkor használjuk, ha egy elemhez közvetlenül szeretnénk eseménykezelőt kapcsolni. Ekkor az fn függvényen belül a this az adott elem DOM objektumát tartalmazza, első paraméterként pedig egy böngésző-függetlenített eseményobjektumot kapunk.
A második változat a delegáláshoz szolgál. Ekkor $obj elemhez kapcsoljuk az eseménykezelőt, de a futtatását visszadelegáljuk a selector-ban megadott gyerekelemhez. Így az fn függvény this kontextusa már az érintett gyerekelem lesz. Az előző táblázatos példát használva jQuery-ben a delegálás a következőképpen történik:
$('table').on('click', 'td', function (e) { console.log(this); console.log(e); });
Ha egy elem egy bizonyos eseményére több eseménykezelőt is szeretnénk kötni, annak semmi akadálya – nyugodtan megtehetjük a fenti módszerekkel. Ekkor azonban egy súlyosabb problémába ütközhetünk, mivel előfordulhat, hogy ugyanazt az eseménykezelőt többször is hozzáfűznénk az elemhez. Ez utóbbi érthetően gondot okozhat, hiszen egy kattintásra felugró figyelmeztetés többszöri lefutása nem épp a pozitív felhasználói élményeket gazdagítja. Ha csak egyetlen függvényről van szó, akkor nincsen gond, az off()-fal minden további nélkül megszüntethetjük az összes rá vonatkozó kötést. Ha azonban egy elem, például egy link kattintás eseményére többen is feliratkoztak, és mi csak egy bizonyos eseménykezelőt szeretnénk megszüntetni, akkor nem árt valahogy hivatkoznunk erre az egyre. Ennek a problémának a feloldására névterekkel láthatjuk el eseményeinket. Itt nem kell semmi bonyolult dologra gondolni, egyszerűen az esemény neve, és a hozzá egy ponttal fűzött tetszőleges azonosító egy ilyet alkot, ezt követően ezzel hivatkozhatunk rá.
$('table').on('click.myGame', 'td', function () ); $('table').off('click.myGame', 'td', function () );
A megsokszorozott eseménykezelők hibájába sokszor beleeshetünk, ezért érdemes minden eseményt névtérrel ellátni, és a lehetséges eseménykezelőit megszüntetni, mielőtt definiálnánk őket (nem létező eseménykezelőkre az off() nem produkál hibát). Az eseménykezelők létrehozásában mindenképpen az alábbi példa követendő.
$('table').off('click.myGame').on('click.myGame', 'td', function () );
Végül érdemes megemlíteni, hogy beépített és saját események programozott kiváltására is lehetőség van a trigger() metódus segítségével.
$("#adatok").trigger("click");
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.