Vissza az előzőleg látogatott oldalra (nem elérhető funkció)Vissza a modul kezdőlapjáraUgrás a tananyag előző oldaláraUgrás a tananyag következő oldaláraFogalom megjelenítés (nem elérhető funckió)Fogalmak listája (nem elérhető funkció)Oldal nyomtatása (nem elérhető funkció)Oldaltérkép megtekintéseSúgó megtekintése

Tanulási útmutató

Összefoglalás

A leckében megismerkedünk azzal, hogy hogyan lehet a kliensoldalra leküldött statikus HTML oldalakat programozottan dinamikussá tenni. Az ehhez szükséges JavaScript alapszintű ismeretét feltételezve a dokumentum manipulálását keretrendszeren keresztül oldjuk meg. A példaalkalmazás kliensoldali funkcióinak bemutatásával ismertetjük ennek gyakorlati vonatkozásait.

Követelmény

A lecke elolvasásával a hallgató betekintést nyer a jQuery JavaScript keretrendszerbe, és elvárható tőle, hogy alapvető kliensoldali funkciókat segítségével megvalósítson.

Önállóan megoldható feladatok

Kliensoldali funkciók - JavaScript használata webes alkalmazásokban

A tananyagban eddig elsősorban azt tekintettük át, hogy adatvezérelt webes alkalmazások írása során a szerveroldalon milyen lehetőségeink vannak. Megnéztük, adatbázisokat milyen módon érhetünk el, és lépésről lépésre eljutottunk a szerveroldali kódunk olyan strukturálásához, amely megfelelő alapot biztosít a gyors fejlesztéshez, a könnyebb hibakereséshez, és a hosszú távú karbantartáshoz. Több szinten megismerkedtünk absztrakt interfészekkel, amelyek az interfész alatti rétegek sokszínűségét fedték el (ld. pl. PDO, amelyek a sokféle adatbázis-kezelő függvény elé helyez egy egységes programozói felületet), vagy pedig tipikus, gyakran ismétlődő problémákat oldanak meg, vezetnek vissza paraméterezési kérdéssé (ld. pl. a CodeIgniter jó pár osztályát).

A kliensoldal azonban eddig csak az alkalmazásunk nézeteként szerepelt, s mint ilyen azt vártuk el tőle, hogy a megfelelő HTML és CSS kóddal megjelenítse oldalainkat. A felhasználó minden eseménye azonban végső soron a szerveren került feldolgozásra, hiszen a hivatkozásokat és az űrlapküldéseket mind a szerveroldal szolgálta ki. Alapvetően tehát minden adatfeldolgozás és kiszolgálás a szerveroldalon történik meg, a kliensoldalon semmilyen logika nincs.

Ez a felépítés kiválóan biztosítja azt, hogy oldalaink a lehető legtöbb eszközön jól jelenjenek meg, azonban sok esetben körülményessé vagy lassúvá teheti alkalmazásunk használatát. Egy mező kötelezőségének vizsgálatához miért kell az összes adatot elküldeni a szerverre, és megvárni, hogy az az egész oldalt újra visszaküldje a kliensnek, ha ezt magán a kliensen is megtehetjük? Ez és a hasonló igények egyáltalán nem számítanak újdonságnak a webprogramozás területén, sőt a web, a böngészők megjelenésével egyidősek. Szükség van tehát – elsősorban a felhasználói igények, a kényelmesség, gyorsaság, kisebb adatforgalom miatt – kliensoldali logikát is elhelyezni, amit a JavaScript nyelv és környezet biztosít a böngészőkben.

A JavaScript használatával kliensoldalon tudunk reagálni a felhasználói eseményekre. Segítségével életre kelnek az oldalak, hiszen lehetőségünk nyílik arra, hogy a szervertől kapott kliensoldalról tekintve statikus oldal dinamikussá váljon, és minden részét programozottan vezéreljük. A JavaScripttel így a kliensoldali dinamikus webprogramozás válik lehetségessé. Az AJAX technológiával kiegészítve pedig hatékony, gyors és felhasználóbarát módon kommunikálhatunk a szerverrel is.

A következőkben azt tekintjük át, hogy milyen lehetőségeink és eszközeink vannak a kliensoldali dinamikus webprogramozás biztosítására. Az alábbiakban néhány feltételezéssel élünk, miszerint a következő fogalmak ismertek:

A JavaScript népszerűsége

A kliensoldali technológiák, a weboldalak programozott manipulálása szinte egyidős magukkal a weboldalakkal, a HTML technológiával, és velük együtt fejlődött. Manapság nagyon gyakori a használata, hiszen gyakorlatilag az összes népszerű oldal erre támaszkodik a kényelmes és felhasználóbarát megoldások miatt. Elég, hogyha csak különböző Google termékeket nézünk: GMail, a Google Maps, Google Plus, Google Translate, Google Drive és Documents, de természetesen a Facebook is, és szinte minden oldal intenzíven alkalmazza ezt a technológiát.

A JavaScript használatának azonban vannak nehézségei, amellyel szinte minden programozó szembesül. Egyrészt – a böngészők fejlődésének történetéből fakadóan – nagyon nagyok az egyes böngészők közötti különbségek, amelyek megnehezítik alkalmazásunk kompatibilissá tételét minden futtatókörnyezetre. Másrészt böngészőbeli implementációs hibákat is figyelembe kell venni a megvalósítás során, ha egyáltalán tudunk ezekről. Végül néha nagyon nagy a szakadék az alap nyelvi lehetőségek és az elérni kívánt funkcionalitás között, ami miatt sokszor nagyon sokat kell kódolni bizonyos funkció eléréséhez.

Ahhoz, hogy felülkerekedjünk ezeken a nehézségeken, legjobb megoldás, ha JavaScript keretrendszereket használunk. Ezek tulajdonképpen egy absztrakt interfészt definiálnak, amellyel egyrészt egységesítik a különböző böngészők eltérő függvényelnevezéseit és interfészeit, másrészt elfedik a tipikus hibák megoldásait, harmadrészt sokszor egyszerűsítik az egyes műveleteket, végül további magas szintű funkciókkal bővítik az alap nyelvi lehetőségeket, pár utasítással elérhetővé téve olyan funkciókat, amelyeket mi magunk pár száz vagy ezer sor megírása után tudnánk biztosítani. Mindezeket pedig viszonylag megbízhatóan és hibamenetesen tálalják, hiszen szinte mindegyik keretrendszer mögött egy nagy fejlesztői (és tesztelői) közösség áll.

Jelenleg nagyon sokféle JavaScript keretrendszer közül választhatunk. Mindegyik a fenti funkcionalitást biztosítja, eltérés általában az interfész nyújtotta programozási stílusban, dokumentáltságban, valamint a támogatottságban van. Bármelyiket is válasszuk, a keretrendszer filozófiájával, parancsaival használata előtt mindenféleképpen meg kell ismerkedni. A keretrendszer használata tehát nem megy egyik napról a másikra, eleinte sok olvasással jár, első feladatainkat nehezebben tudjuk megoldani, megismerve viszont gyorsan és intuitíven tudunk vele programozni.

Vissza a tartalomjegyzékhez

jQuery

A sokféle keretrendszer közül mi a jQuery-t választottuk, egyszerű és hatékony programozási felülete, jó dokumentáltsága miatt. Az egyik legnépszerűbb JavaScript keretrendszer, rengeteg bővítmény tölthető le hozzá. Használata megkönnyíti HTML dokumentumok bejárását, manipulálását, animálását, stílusok módosítását, események kezelését, az AJAX-os kommunikációt. A benne írt kódunk böngészőfüggetlen lesz, azaz bármelyik böngésző bármelyik verziójában ugyanazt az eredményt kapjuk. A jQuery-t az utóbbi időben több projektre bontották, a jQueryUI különböző felületi komponenseket definiál, míg a jQuery Mobile mobilalkalmazások készítését könnyíti meg jQuery alapokon különböző felületi elemeket definiálva.

A jQuery filozófiája a következő:

A jQuery mindhárom lépésben segít. Az elemek kiválasztását CSS szelektorokkal végzi el, az elemeket automatikusan berakja egy tömbbe, és a művelet végrehajtásakor a ciklust is automatikusan elvégzi a háttérben, azaz a művelet az összes kiválasztott elemre vonatkozik. Azzal, hogy a jQuery átveszi tőlünk a fentebbi folyamat mechanikus részeit, a kód már csak a lényeget tartalmazza, a kiválasztási szabály megadását és a művelet meghatározását, ahogy az az alábbi példában látható:

$('p').hide()

Anélkül, hogy részletesen magyaráznánk, azt látjuk, hogy a $() függvény segítségével kijelöljük az oldalon az összes p elemet, majd azokat eltűntetjük.

A jQuery filozófiájának középpontjában a jQuery() vagy $() függvény áll. Ez paraméterül egy szövegként megadott CSS3 kompatibilis szelektorkifejezést vár. A szelektor alapján a dokumentumból kiválogatja a megfelelő DOM elemeket, azokat egy tömbbe és ezt adja vissza. Ez a visszatérési érték a jQuery objektum, amin számos egyéb metódus meghívható. Alapvetően kétféle metódus van. Az egyik lekérdez valamilyen értéket, ezek a getterek, a lekérdezett értékkel térnek vissza. A másik beállít valamilyen értéket, ezek a setterek, és általában nincsen speciális visszatérési értékük. jQuery-ben a setterek magával a jQuery objektummal térnek vissza, így lehetővé válik egy kifejezésen belül több metódus meghívása is egymás után. Ezt hívják láncolásnak.

$('p') //visszatérési értéke [p, p , p] 
$('p').addClass('piros').show() //láncolás 
$('p').text() //visszatér a tartalmával

A jQuery() függvénynek még kétféle funkciója van paraméterének jellegétől függően. Ha paraméterként HTML kifejezést tartalmazó szöveget kap, akkor megpróbálja a kifejezésnek megfelelő elemeket létrehozni a memóriában, és e struktúrának a legkülső elemét teszi a jQuery objektumba. Használatára később mutatunk példát.

Végül a jQuery() függvény egy függvénykifejezést is paraméterül kaphat. Az így átadott függvény akkor hívódik meg, amikor az oldal betöltődött, pontosabban egy kicsit előtte, amikor az oldal DOM struktúrája felépült. Használata nagyon hasznos, hiszen az oldalon belüli elemekkel csak akkor tudunk dolgozni, ha azok már a DOM-ban szerepeltek. Mivel pedig a JavaScript kódunkat tartalmazó <script> elem általában a <head>-ben szokott szerepelni, ezért akkor még nincsenek betöltve a <body> elemei a DOM-ba. Ha azonban a szkriptben lévő kódot akkor hívjuk meg, amikor a betöltést megtörtént, akkor már mindegyik elérhető. Ezért tipikusan egy jQuery-t használó szkript a következőképpen szokott kezdődni:

$(function () {
    //ide jön a kód
});

// vagy

$(init);

var init = function() {
    //ide jön a kód
};

Használat

A jQuery használatához le kell tölteni az aktuális verziót a jQuery oldaláról, és azt a dokukmentumunk <head> részében egy <script> elemmel külső forrásként betölteni. Ekkor már lokálisan tudunk vele dolgozni.

<script src="jquery.js"></script>

Állandó internetkapcsolat mellett másik lehetőség egy CDN szerveren tárolt változat használata.

<script src="http://code.google.com/apis/ajaxlibs/documentation/index.html#jquery"></script>

A következőkben ismertetett példák tesztoldalául példaalkalmazásunk egyik oldalát választottuk.

<!DOCTYPE html>
<html>
    <head>
        <title>DYSS</title>
        <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
        <script src="jquery.js"></script>
    </head>
    <body>
        <div id="content">
            <div class="column1 column">
                <h3>címke2</h3>
                <ul class="lista">
                    <li>
                        <div class="fo">
                            <a href="#">
                                <img src="pelda.png" /><br />
                            </a>
                            <div class="info">
                                <h4>Példa bemutató</h4>
                                <p class="kisbetu">Példa bemutató leírása</p>
                                <p class="kisbetu">
                                    Megtekintések: 17<br />
                                    Kedvencek: 0<br />
                                    Felhasználó: <a href="#">user1</a>
                                </p>
                                <p class="cimkek kisbetu">Címkék: </p>
                                <ul class="cimkek kisbetu">
                                    <li>
                                        <a href="#">címke2</a>
                                    </li>
                                    <li>
                                        <a href="#">címke3</a>
                                    </li>
                                    <li>
                                        <a href="#">cinke</a>
                                    </li>
                                </ul>
                            </div>
                        </div>
                    </li>
                </ul>
            </div>
        </div>
    </body>
</html>

A jQuery mellett az oldalhoz tartozó JavaScript állományt is be kell tölteni. A példák egy részét a böngészők JavaScript konzoljába is bemásolhatjuk, módosíthatjuk, kipróbálhatjuk.

Szelekció

Minden művelet előtt szükséges kiválasztanunk azokat az elemeket, amelyekkel dolgozni szeretnénk. A jQuery erre a CSS szelektorok kibővített halmazát használja. A JavaScript programozáshoz is elengedhetetlen tehát a CSS szelektorok ismerete. Ha tehát egy oldalon belül minden kisbetu osztályú p-t megfelelő stílussal el tudunk látni, akkor ezekkel az elemekkel jQuery-ben is tudunk dolgozni. Ennek az anyagnak nem része a szelektorok részletes bemutatása, a jQuery dokumentáció ide tartozó része részletesen bemutatja mindegyiket.

Példák a szelekcióra:

$('#content');
$('div');
$('.kisbetu');
$('#content a');
$('div.fo > a');
$('ul.lista ul li:first');
$('div.fo, div.info');
$('img[src$="pelda.jpg"]');
$('table');

Manipuláció

A kiválasztott elemekkel sokféleképpen lehet dolgozni, általában lekérdezni szeretnénk valamilyen paraméterüket, vagy módosítani azokat. A jQuery metódusai általában egyszerre setterek és getterek is. Ha nem kapnak paramétert, akkor általában az adott tulajdonság értékével térnek vissza, paramétert kapva azonban beállítják azt. A setterek általában mindegyik elemre érvényesülnek, a getterek általában a tömbben lévő első elemre vonatkoznak.

Manipulálni az elemek attribútumait, tartalmát, stílustulajdonságait lehet, valamint elemeket létrehozni, áthelyezni, törölni.

CSS tulajdonságok manipulációja, az animációk

Egy elem kinézetét a stílusokkal tudjuk megváltoztatni. A style attribútum módosítására a jQuery a css() függvényt vezette be, melynek első paramétere a módosítandó stílusattribútum, második paramétere az érték. Stílusosztály módosítása az addClass(), removeClass() és toggleClass() függvényekkel lehetséges, melyek hozzáadnak, elvesznek vagy váltogatnak egy osztálybejegyzést. A hasClass() függvénnyel pedig lekérdezhető, hogy az elemnek van-e adott stílusosztály beállítva.

Egy elem méretét és pozícóját is sokszor kell állítgatni, gondoljunk az elemek mozgatására, összecsukására. A szélesség és magasság kérdése nem egyértelmű, hiszen attól függ, bele kell-e számolni a bélést vagy a keretet. A jQuery mindegyikre választ ad. A height() függvény a tartalom magasságát adja meg, az innerHeight() a tartalom+bélés magasságát, míg az outerHeight() metódus a tartalom+bélés+keret együttes magasságát adja meg. Ez utóbbinak egy true értéket adva paraméterül a margó is beleszámolódik. Ugyanezek a függvények megvannak a width tulajdonságra is.

$('div.info').height();
$('div.info').innerHeight();
$('div.info').outerHeight();
$('div.info').outerHeight(true);

$('div.info').width();
$('div.info').innerWidth();
$('div.info').outerWidth();
$('div.info').outerWidth(true);

A pozíció is egy relatív érték, hiszen attól függ, mihez viszonyítjuk az elem elhelyezkedését. A position() függvény a pozíció szempontjából érvényes szülőhöz képesti elhelyezkedést, az offset() a dokumentumhoz képest top-left párból alkotott objektumot adja vissza.

$('div.info').position();
$('div.info').offset();

Elemek animálása nagyon látványos az oldalakon és megvan a maga ergonómiai szerepe. Az animálás nem más mint a CSS tulajdonságok bizonyos időközönkénti állítása. Ahelyett, hogy ezt magunknak kellene időzítőkkel megoldanunk, a jQuery egy általános animate() függvényt bocsát rendelkezésünkre ehhez. Ez a függvény sokféleképpen paraméterezhető (ld. leírás), egyik változatában egy objektumliterálban megadjuk az animálandó stílusokat és végértéküket, majd azt, hogy mindez hány ezredmásodperc alatt fusson le. Az egyedi animációk mellett előre beépítettek is vannak, ilyen a hide(), show(), toggle(), a slideUp(), slideDown(), slideToggle(), valamint a fadeOut(), fadeIn(), fadeToggle() hármasok, amelyek az eltűntetésért, megjelenésért és váltogatásért felelnek a nevüknek megfelelően. A hide(), show(), toggle() hármas annyiban kilóg a sorból, hogy ha nem kapják meg az animáció hosszát paraméterként, akkor animáció nélkül tűntetik el vagy jelenítik meg az elemeket. Mindegyik animációs függvények utolsó paraméterként átadhatunk egy függvényt, amely az animáció végeztével fut le. Végül, ha ugyanazon elemen több animálást is végrehajtunk, akkor azok egymás után következnek be az elemen. Két különböző elemen végrehajtott animálás viszont egyszerre fut le. Az animációs sor tehát elemenként tárolódik.

$('img').animate({margin: "100"}, 1000);
$('img').animate({width: "+=50"}, 1000);
$('img').hide();
$('img').show();
$('img').hide(1000);
$('img').toggle('slow');
$('img').fadeOut('normal');
$('img').fadeIn('fast', function () {
    console.log('Vége');
});

Tartalom manipulálása

A jQuery rengeteg lehetőséget ad a tartalom manipulálására. Mindegyiket felesleges végignézni, a dokumentáció megfelelő része ezt részletesen bemutatja. Meglévő elemeknél az attribútum módosítása vagy lekérdezése az attr() függvénnyel történik. Az elem tartalmának módosításáért a text() és html() függvények a felelősek. Egy elem mozgatását sokféleképpen megtehetjük. Az adott elemet hozzáfűzhetjük a paraméterül megadott elem gyerekei közül az utolsónak (appendTo), elsőnek (prependTo()), paraméterül adott elem után (insertAfter()) vagy elé (insertBefore()). Ugyanígy egy adott elem gyerekei közé felvehetjük a paraméterként megadott elemet utolsónak (append()), elsőnek (prepend()), adott elem után (after()) vagy elé (before()). A kiválasztott elemet törölni a remove() metódussal lehet. Elem létrehozására a jQuery() vagy $() függvény szolgál. Ennek a HTML kódot megadva az elemek létrejönnek.

$li = $('<li>Új elem</li>');

$li.appendTo('ul.cimkek');
$li.prependTo('ul.cimkek');
$li.insertAfter('ul.cimkek > li:eq(1)');
$li.insertBefore('ul.cimkek > li:eq(1)');

$('ul.cimkek').append($li);
$('ul.cimkek').prepend($li);
$('ul.cimkek > li:eq(1)').after($li);
$('ul.cimkek > li:eq(1)').before($li);

$li.remove();

Bejárás

A kiválasztott elemekhez képest néha el kell tudnunk mozdulni a DOM fában. Erre azért lehet szükség, mert pontosan az elemet nem tudjuk meghatározni, csak valamelyik részét, vagy azért, mert a kiválasztott objektumot tömbjét szeretnénk bővíteni.

Adott elem közvetlen gyerekei között a children() metódussal, leszármazottai között a find() metódussal tudunk keresni, paraméterként további szelektort megadva. A szülők között a parent() a közvetlen szülőt, parents() az összeset, a closest() a paraméterként szelektornak megfelelő legközelebbi szülőt adja meg. A testvérek között a siblings() az összes testvért megadja, a next() a következőt, a prev() az előzőt, a nextAll() és prevAll() az összes következőt vagy előzőt adja meg. Mindegyiknek adhatunk meg szelektort paraméterként. Ezek a metódusok a kiválaszott elemek listáját megváltoztatják az új elemkre. Az előző választáshoz az end() függvénnyel térhetünk vissza.

A kiválasztott elemeket tovább szűrhetjük. A first() az első, a last() az utolsót, az eq() a paraméterként megadott sorszámú elemet adja meg. Az add() függvénnyel hozzáadhatunk a kiválasztott elemekhez, a not() kiveszi a paraméterében megadott szelektornak megfelelő elemeket, a filter() csak a megfelelő elemeket tartja meg. Itt is az előző választáshoz az end() függvény tér vissza.

Lekérdezéshez az is() parancs használható, mellyel ellenőrizzük, hogy az adott elem a paraméterként megadott szelektornak megfelel-e. A has() parancs a kiválasztott elemeket leszűri azokra, amelyeknek van a paraméterének megfelelő leszármazottja.

Példa:

$('div.info').parent();
$('div.info').parents();
$('div.info').closest('ul');

$('div.info').children();
$('div.info').children('p');
$('div.info').find('li');

$('h4').prev();
$('h4').next();
$('h4').next('.kisbetu');
$('h4').siblings();

$('li').first();
$('li').last();
$('li').eq(1);
$('li').add('.kisbetu');
$('li, .kisbetu').not('li');
$('.cimkek').filter('p');

$('p:fisrt').is('p');
$('div').has('.cimkek');

Kiválasztott elemek egyedi feldolgozása

Sokszor szükségünk lehet arra, hogy ne speciális jQuery metódusokkal dolgozzuk fel a kiválasztott elemeket, hanem egyedi kódot írjunk rá. Ekkor szükség lehet a kiválasztott elemeken végigmenni. Ezt a célt szolgálja az each() metódus, amelynek egy névtelen függvényt kell paraméterül átadni, ami minden kiválasztott elemre meghívódik egyesével. Tulajdonképpen az each() függvény egy ciklus megvalósítása, ahol nekünk csak a ciklusmagról kell gondoskodnunk. A függvényparaméteren belül a this kulcsszó a kiválasztott elem DOM objektumát tartalmazza.

$('p.kisbetu').each(function () {
    console.log( $(this).text() );
});

Eseménykezelés

Az 1.7-es verzióval a jQuery egységesítette az eseménykezelési eszköztárát az on() és off() függvény segítségével. Az on() függvénnyel egy adott elemhez adott eseményére lehet feliratkozni egy függvénnyel, míg az off() függvény a leiratkozást szolgálja. Alapvetően két esetet különböztethetünk meg eseménykezeléskor.

Az első esetben ahhoz az elemhez kapcsoljuk az eseménykezelő függvényt, ami az eseményt kiváltja. A jQuery objektumon meghívott on() függvény első paramétereként ekkor meg kell adni az esemény jellegét (pl. click, mouseenter, submit, azaz a megismert események ‘on’ nélküli változatai), második paraméterként pedig megadjuk az eseménykezelő függvényt. A függvényen belül a this kulcsszó az eseményt kiváltó DOM elemet tartalmazza. A kiválasztott elemekről az off() függvénnyel tudjuk a regisztrált eseménykezelőket lecsatolni.

$('p').on('click', function () {
    console.log( $(this).text() );
});

A másik esetben nem ahhoz az elemhez kapcsoljuk közvetlenül az eseménykezelőt, amelyik az eseményt kiváltja, hanem valamelyik szülőeleméhez. Azért tehetjük ezt meg, mert a böngészők eseménykezelési modellje úgy működik, hogy egy esemény bekövetkeztekor az esemény nemcsak a kiváltó objektumon hívódik meg, hanem annak szülőjén, majd annak is a szülőjén, stb. Ezt a folyamatot hívják az esemény felbuborékoltatásának, hiszen az esemény a DOM hierarchiában egyre feljebb és feljebb váltódik. Ha bármelyik elemhez a megfelelő eseménykezelő van csatolva, akkor az a buborékoló eseményre meghívódik.

Ennek akkor van szerepe, ha sok azonos típusú elemnél kellene ugyanannak a függvénynek meghívódnia. Ekkor nem kell mindegyikhez egyesével hozzárendelni a függvényt, hanem elég a szülőobjektumnál megtenni ezt, úgyis mindegyik gyerekobjektum eseménye eléri őtt is. Ezt hívják az eseménykezelés delegálásának. Másik gyakori felhasználási módja a delegáltak készítésének az, amikor egy szülőelembe dinamikusan kerülnek újabb elemek. Ekkor nem kell ezekhez egyesével az eseménykezelőt rendelni, itt is a szülő fogja felé delegálni azt.

Delegáltak készítése is az on() függvénnyel lehetséges. A szülőobjektumon kell meghívni ezt a függvényt, az első paraméter továbbra is az esemény típusa, de ebben az esetben második paraméterben egy szelektort kell megadni. Csak azokra a leszármazottakra fog meghívódni az eseménykezelő, amelyekre az adott szelektor érvényes. Harmadik paraméterként pedig magát az eseménykezelő függvényt kell megadnunk.

$('div.info').on('click', 'p', function () {
    console.log( $(this).text() );
});

A bekövetkezett eseményeknek számos paramétere van: milyen típusú az esemény, hol van a kattintás x-y koordinátája, le volt-e nyomva a Ctrl billentyű, stb. Ezt az információt az ún. eseményobjektum tartalmazza. A jQuery az eseményobjektumot az eseménykezelő függvény első paramétereként adja át. A lenyomott billentyű kódját ezen objektum which tulajdonsága, míg az egér koordinátáját a pageX és pageY tulajdonság tartalmazza.

Vannak olyan elemek az oldalon, amelyhez alapértelmezetten tartoznak műveletek. Ilyen például a hivatkozás, amelyre kattintva a böngésző követi a hivatkozást; vagy ilyen az űrlap submit gombja, amelyre kattintva a böngésző elküldi az űrlap adatait; ilyen a beviteli mezők viselkedése is, hiszen ha lenyomunk egy billentyűt, akkor beírja azt a mezőbe. Néha szükségünk van az alapértelmezett műveletek letiltására, ezt az eseményobjektum preventDefault() metódusával tehetjük meg. Arra is szükségünk lehet, hogy egy esemény buborékozását megállítsuk, ezért a stopPropagation() metódus felel.

Az eseményobjektum többi tulajdonságát ld. a dokumentációban.

$('div.info').on('click', 'a', function (e) {
    console.log( this );
    console.log( e.type );
    console.log( e.pageX );
    console.log( e.pageY );
    e.preventDefault();
});

Ajax

A jQuery az AJAX hívásokat is egyszerűbbé teszi metódusaival, gyakorlatilag paraméterezési kérdéssé válik használatuk. A legmagasabb szintű metódus ahhoz a gyakori feladathoz ad segítséget, melynek során egy kiválasztott elembe szeretnénk a szerverről egy HTML részletet betölteni. A kiválasztott elemen a load() metódust meghívva paraméterként a hivatkozást kell megadni. Második paraméterként megadhatunk egy objektumliterált, amelyek GET paraméterként adódnak a hivatkozáshoz, végül harmadik paraméterként egy függvényt adhatunk meg, amely a betöltés végeztével hívódik meg. Az első paraméterben a hivatkozás után szóközzel elválasztva lehetőségünk van egy szelektort megadni, amely kiválasztja, hogy a letöltött HTML oldal melyik részét használja fel.

$('#content').load('tartalom.html');
$('#content').load('tartalom.php', {id: 12, user: 'user1'});
$('#content').load('tartalom.php', {id: 12, user: 'user1'}, function () {
    console.log('Betöltődtem!');
});
$('#content').load('tartalom.php', function () {
    console.log('Betöltődtem!');
});
$('#content').load('tartalom.html #tartalom');

Alacsonyabb szintű és jobban paraméterezhető AJAX hívásokat tudunk végrehajtani azokkal a jQuery függvényekkel, amelyeket nem kiválasztott elemeken hívhatunk, hanem közvetlenül a jQuery vagy $ függvény metódusaként. Ezek még mindig speciálisak abban az értelemben, hogy egy-egy konkrét hívástípus, vagy eredménytípus feldolgozását hívatottak elvégezni. A $.get() függvénnyel GET, a $.post() függvénnyel POST hívásokat kezdeményezhetünk. Ha a válaszban JavaScript kódrészletet várunk, és azt szeretnénk is futtatni, akkor ezt a $.getScript() függvénnyel tegyük meg, amely az adatok megfelelő ellenőrzését és feldolgozását is elvégzi. Ha pedig JSON formában kapjuk a választ a szervertől, akkor erre a feladatra a legalkalmasabb a $.getJSON() metódus. Mindegyik függvény első paraméterként a hívandó URL-t tartalmazza, második opcionális paraméterként hívási paramétereket adhatunk meg objektumliterál formájában, harmadik, ugyancsak opcionális paraméterként pedig a sikeres kommunikáció végén lefutó függvényt adhatunk meg.

$.get('delete.php');
$.get('delete.php', {id: 12}, function () {
    console.log('Sikeres törlés.');
});
$.post('delete.php', {id: 12}, function () {
    console.log('Sikeres törlés.');
});
$.getScript('valami.js');
$.getJSON('lista.php', {par: 34}, function (adat) {
    console.log(adat);
});
$('form').on('submit', function (e) {
    e.preventDefault();
    
    $.post('feldolgoz.php', $(this).serializeArray(), function (adat) {
        console.log(adat);
    })
});

Ha az AJAX kérésünket finomhangolni szeretnénk, vagy minden paramétert mi magunk szeretnénk felügyelni, akkor a legalacsonyabb szintű és legáltalánosabb AJAX metódust, a $.ajax függvényt érdemes használnunk. Ezt egy objektumliterálon keresztül tudjuk felparaméterezni, itt kell megadnunk az URL-t, a hívás módját, a várt eredmény típusát, de itt tudunk a kérés különböző fázisaira és lehetséges kimeneteleire is a legjobban felkészülni többek között. Érdemes általában is ezt a függvényt használni, hiszen az objektumliterálon keresztül paraméterezésben könnyen bővíthető, illetve módosítható eszközt kapunk. A részletes paraméterekről a dokumentáció ad leírást.

$.ajax({
    url: 'tartalom.php',
    type: 'POST',
    data: {
        id: 12,
        par: 'alma'
    },
    dataType: 'json',
    success: function (adat) {
        console.log('Sikeres válasz: ', adat);
    },
    error: function (xhr, status) {
        console.log('Sikertelen kérés', status);
    }
});

Minden AJAX hívásra globális beállításokat a $.ajaxSetup() metódussal tudunk elvégezni. A kiválasztott elemekhez pedig globális AJAX eseménykezelőket köthetünk az ajaxStart(), ajaxSuccess(), ajaxError() függvények és társai segítségével, amelyek mindegyik AJAX hívásnál meghívódnak. Ezekkel lehet például oldal szinten jelezni, ha egy AJAX hívás van a háttérben, vagy megfelelő log műveletek elvégezni.

$.ajaxSetup({
    cache: false,
    error: function (xhr, status) {
        console.log('Hiba: ', status);
    }
});

$('#loader')
    .ajaxStart(function () {
        $(this).show();
    })
    .ajaxComplete(function () {
        $(this).hide();
    });
    

Komponensek a felhasználói felületen – jQuery UI

A jQuery keretrendszert használva könnyebben lehet magasabb szintű komponenseket megvalósítani. Ezt tették a jQuery UI projektben, ahol a jQuery-re épülő felületi komponensek lettek megvalósítva. Megtalálható itt a dátumbekérő elem, csúszkák, modális dialogusablakok, tartalmak különböző elrendezésére szolgáló elemek, plusz effektusok, stb. Ezek tárgyalása már nem ennek a tananyagnak a része, de aki hozzászokott a jQuery stílusához, annak a jQuery UI használata sem okoz semmilyen meglepetést. Gyakorlatilag az összes komponens működését paraméterezésre vezették vissza, az egyéni viselkedéseket pedig a komponensek eseményeire feliratkozó eseménykezelőkkel lehet elvégezni.

JSON

A JSON a JavaScript adatliteráljaira épülő adatcsere-formátum. Az összetett adatokra a tömb és az objektum literál szolgál, ezek elemeiként lehet megadni az egyszerű típusok literálformáját. Egyre népszerűbb kommunikációs formátum, mivel nem annyira bőbeszédű, mint az XML, és egyszerű kezelni. Számos nyelvben létezik JSON sorosítást, illetve visszakódolást végző függvény. Ha például a kliensnek szeretnénk leküldeni a bemutatóhoz tartozó képek sorszámát és elérhetőségét, akkor azt a következőképpen tehetjük meg:

[
   {
      "sorszam":"1",
      "fajlnev":"http://localhost/10/index.php/main/kep/1f6359e5c6271a43b018887492823835.jpg"
   },
   {
      "sorszam":"2",
      "fajlnev":"http://localhost/10/index.php/main/kep/df4351637ccbf52a021b750637bfed65.jpg"
   },
   {
      "sorszam":"3",
      "fajlnev":"http://localhost/10/index.php/main/kep/be5bd50b8859fb70ae63b2037fb5a01a.jpg"
   }
]

Ezt a szöveget a böngészővel kiértékeltetjük, ami által előáll egy JavaScript tömb, ezt pedig JavaScript kóddal feldolgozhatjuk.

Vissza a tartalomjegyzékhez

Példaalkalmazás

Az alábbiakban néhány példát mutatunk be a JavaScript alkalmazására a bemutatókat kezelő példaalkalmazáson keresztül. Eddig úgy építettük fel alkalmazásunkat, hogy hivatkozásokon és űrlapküldéseken keresztül mindig a szerver dolgozta fel az adatokat és generálta le az oldalt. A JavaScript használatától kényelmesebb felületet és gyorsabb adatforgalmat várunk.

Az alábbiakban bemutatott példaalkalmazás forráskódja innen letölthető. A JavaScript állományok a js mappában helyezkednek el.

A bemutató vetítése

Az alkalmazásunk leglátványosabb része a bemutatók vetítése. Eddig az egyes képek megtekintéséhez az előre-, hátra nyilakkal kellett navigálnunk, amelyek hatására újragenerálódott az oldal a kérdéses képpel. Az oldal újbóli letöltése azonban lassú, hiszen egyrészt az egész megjelenítés egy kérés-válasz kommunikációból áll a szerver és a kliens között, másrészt egy újabb kép kedvéért újra és újra az egész oldalt le kell küldenünk. Másrészt látványosabbá is tehetnénk a vetítést például azzal, hogy két kép közötti váltást megfelelő animációkkal végezzük el, mondjuk ki- és beúszással. Végül jó lenne, ha billentyűzettel, a balra és ajobbra nyíllal is lehetne navigálni az egyes képek között.

Az előkészületek során – mivel a bemutató a sablonoktól elkülönült eddig – a views/bemutato.php állomány <head> részébe kellett felvenni a jQuery forrását, a logikáért felelős JavaScript állomány (bemutato.js), valamint a vonatkozó CSS állományok (bemutato.css) hivatkozását.

Célunk az, hogy ne kelljen az egész oldalt újratölteni, csupán csak a képeket jelenítsük meg egymás után. Erre alapvetően két lehetőségünk van. A következő, illetve előző gombokra AJAX technológia segítségével kérdezzük le a következő vagy előző kép elérhetőségét, és ezt tesszük be az <img> elem src attribútumába. Ekkor természetesen azt is le kell kérdezni, hogy az új képet mi előzi meg és mi követi. A másik lehetőség során már az oldal generálásakor elrejtjük az oldalon belül az összes kép elérhetőségét. Erre tulajdonképpen már példa is van, hiszen a bélyegképek az összes képet megjelenítik kicsiben. A bélyegképek legenerálásához pedig szerveroldalon az összes kép információját, így a nagy képek elérhetőségét is lekértük az adatbázisból ($kepek tömb). Kérdés, hogy hol tároljuk ezt az információt. A HTML5 erre egy nagyon elegáns eszközt ad segítségül, az ún. HTML5 adatattribútumokat. Bármelyik elem attribútumai közé felvehetünk egy data- kezdetű paramétert, és eltárolhatunk hozzá értéket. Pl. <div data-id="12">. A jQuery ezeket az adatattribútumokat támogatja, lekérdezni és beállítani is lehet őket a data() metóduson keresztül. Egészen bonyolult értékek is tárolhatók bennük, pl. JSON szövegek. A bemutatóért felelős showroom azonosítójú div-ben el is tárolhatjuk az adatokat, ahol JSON szövegként egy tömböt tárolunk, minden tömbelem pedig egy képről szóló információt tárol objektum formájában. Az objektum tárolja a kép sorszámát (sorszam) és elérhetőségét (kepfajl). A generálást így végezzük el:

<?php
$keplista = array();
foreach($kepek as $kp) {
    $keplista[] = htmlspecialchars(json_encode(
        array('sorszam' => $kp['sorszam'], 'fajlnev' => site_url('main/kep/' . basename($kp['kepfajl'])))));
}
$keplista = '[' . implode(',', $keplista) . ']';
?>

Ezt a $keplista változót írjuk ki a nézetben, a generált kód pedig a következő:

<div id="showroom" data-kepek="[{&quot;sorszam&quot;:&quot;1&quot;,&quot;fajlnev&quot;:&quot;http:\/\/localhost\/10\/index.php\/main\/kep\/1f6359e5c6271a43b018887492823835.jpg&quot;},{&quot;sorszam&quot;:&quot;2&quot;,&quot;fajlnev&quot;:&quot;http:\/\/localhost\/10\/index.php\/main\/kep\/df4351637ccbf52a021b750637bfed65.jpg&quot;},{&quot;sorszam&quot;:&quot;3&quot;,&quot;fajlnev&quot;:&quot;http:\/\/localhost\/10\/index.php\/main\/kep\/be5bd50b8859fb70ae63b2037fb5a01a.jpg&quot;}]">
                

A sok &quot; jelre azért van szüksége, mert a HTML macskakörmei között csak így tudjuk a JSON szöveg macskakörmeit elhelyezni. Ebben segített nekünk a fenti PHP részletben a htmlspecialchars() függvény.

Ezzel megvan tehát a bemutató összes képének az elérhetősége. A megjelenítéshez azonban valamilyen animációt szeretnénk használni. Az előre-hátra mozgás jobbra-balra úszásként valósuljon meg. Ezt úgy oldjuk meg, hogy a kép helyett egy div lesz, ez egy felsorolást (ul) tartalmaz, ami pedig három listaelemet (li), amelyek egy-egy képet tartalmaznak. A felsorolást megfelelő szélesre véve és a listaelemeket lebegtetve elérhetjük, hogy a listaelemek egymás mellett helyezkedjenek el csakúgy, mintha egy filmtekercs egyes kockái lennének. A mozgatás során ezt a filmetekercset (ul) mozdítjuk el a div előtt, egyszerűen a left tulajdonságának animálásával (ehhez persze egyéb stílusbeállítások is szükségesek). Mindig a 2. listaelemet pozícionáljuk középre, és jobbra-balra mozgatáskor vagy a jobb oldali, vagy a bal oldali szomszédjához animálunk. Mivel ekkor egy szélső elem kerül a nézőtérbe, aminek egyik oldala üres, másik oldalán két kép is van, ezért a legtávolabbi képet átrakjuk a másik oldalra, ezzel elérve, hogy újra az aktuális elem legyen középen. Ez az elképzelés kiválóan mutatja be az egyes DOM műveleteket.

A megoldást a js/bemutato.js állomány tartalmazza. Ebben kezdeti lépésként létrehozzuk a fenti struktúrát, és lecseréljük a szerver által generált képet.

var $sr = $('#showroom'),
    keplista = $sr.data('kepek'),
    szel = 820,
    $div = $('<div></div>')
        .addClass('carousel'),
    $ul = $('<ul></ul>')
        .width(3 * szel)
        .css('left', -szel),
    akt,
    eakt,
    init = function () {
        var $li;
        akt = eakt = getAkt();
        for (var i = 0; i<3; i++) {
            $li = $('<li><img src="' + getImgSource(akt-1+i) + '" /></li>');
            $ul.append($li);
        }
        $div.append($ul);
        $('#showroom img').replaceWith($div);
        //...
    },
    ...
init();

A fenti kódrészlet azt is mutatja, hogy hogyan szerezzük meg a képek listáját, illetve hogyan hozunk létre jQuery segítségével elemeket. Az animált léptetésért a next() és a prev() függvény felel. Ezekben először megnézzük az akt értéke alapján, hogy van-e következő kép. Ha van, akkor az ul elemet szel szélességben elcsúsztatjuk a left stílustulajdonság animálásával, majd az akt értékét megfelelően változtatjuk, végül az első vagy utolsó elemet átrakjuk a másik oldalra, és az ul-t újra középre pozícionáljuk. Az eakt segédváltozó azt kezeli, hogy a nyíl többszöri megnyomásával ne léphessünk túl a képek listáján. A setLathatosag() függvény pedig az előre-hátra nyilak megjelenését szabályozza.

next = function (e) {
    if (e) e.preventDefault();
    if (eakt + 1 < keplista.length) {
        eakt++;
        $ul.animate({left: '-=' + szel}, function () {
            akt++;
            $first = $ul.children().first();
            $first.find('img').attr('src', getImgSource(akt + 1));
            $ul.append($first);
            $ul.css('left', -szel);
            setLathatosag(akt);
        });
    }
}

Végül a megfelelő eseményekhez kell a függvényeket hozzákötnünk. Ha a szerveroldali generáláskor nem jöttek volna létre a nyilak, akkor azokat létrehozzuk az init() függvényben.

if ($('#showroom a.elozo').length == 0) {
    $('<a href="#" class="elozo"><span>Elozo</span></a>').prependTo($sr);
}
if ($('#showroom a.kovetkezo').length == 0) {
    $('<a href="#" class="kovetkezo"><span>Kovetkezo</span></a>').prependTo($sr);
}
setLathatosag(akt);

Az eseménykezelést delegáltakkal oldjuk meg. Egyrészt a nyilakra kattintáshoz, másrészt a megfelelő billentyű leütéshez kapcsolunk eseménykezelőket. Az előbbihez közvetlenül rendeljük a next() és prev() függvényt, az utóbbinál egy közbülső függvény, a keydown() dönti el, melyik billentyű lett leütve, és aszerint hívja meg a megfelelő léptető függvényt.

init = function () {
    //...
    $sr.on('click', 'a.kovetkezo', next);
    $sr.on('click', 'a.elozo', prev);
    $(document).on('keydown', keydown);
    //...
},

keydown = function (e) {
    switch (e.which) {
        case 37: 
            prev(e);
            break;
        case 39: 
            next(e);
            break;
    }
},

Végül a bélyegképekre kattintást is szabályoznunk kell, hogy akkor se töltődjön újra az oldal. Itt is delegáltakkal dolgozunk. Kattintáskor megnézzük, hányadik bélyegképre kattintottunk, majd megnézzük, ez milyen messze van a sorszámtól, és ennyiszer hívjuk meg vagy a prev(), vagy a next() függvényt.

Megjegyzés

Úgy is célszerű lett volna megoldani a tárolást, hogy az összes képet felvesszük az ul felsorolásba, és a balra-jobbra mozgatáskor ezen a filmtekercsen mozgunk a megfelelő irányba. Ekkor nem kell a li elemeket előre hátra pakolni, és elegánsabb a bélyegképeknél két kocka közötti animáció is.

Bemutatók törlése AJAX-szal

A bemutatók törlésénél is felesleges az egész oldalt újratölteni azért, hogy eggyel kevesebb bemutatónk legyen. Ráadásul illik rákérdezni a felhasználónál arra, hogy valóban törölni szeretné-e a bemutatót, így elkerülve a félrekattintásból származó véletlen törléseket.

Az első felvetésben az AJAX technológia segít nekünk, hiszen az oldal újratöltése nélkül el lehet küldeni a szervernek az adatbázisbeli törlési igényünket, és sikeres visszajelzés esetén az oldalból DOM művelettel kivesszük azt.

Az előkészületek során kicsit felokosítottuk a views/template.php állományt, felkészítve arra, hogy akár oldalanként eltérő JavaScript vagy CSS állomány legyen betölthető.

<script type="text/javascript" src="<?php echo base_url('js/jquery-1.7.2.min.js'); ?>"></script>
<script type="text/javascript" src="<?php echo base_url('js/jquery-ui-1.8.22.custom.min.js'); ?>"></script>
<link rel="stylesheet" type="text/css" href="<?php echo base_url('styles/smoothness/jquery-ui-1.8.22.custom.css'); ?>" />
<?php if (isset($css)) : ?>
    <?php foreach ($css as $c) : ?>
        <link rel="stylesheet" type="text/css" href="<?php echo base_url("styles/$c.css"); ?>" />
    <?php endforeach; ?>
<?php endif; ?>
<?php if (isset($js)) : ?>
    <?php foreach ($js as $j) : ?>
        <script type="text/javascript" src="<?php echo base_url("js/$j.js"); ?>"></script>
    <?php endforeach; ?>
<?php endif; ?>

Így a bemutatók listázásáért felelős vezérlőben jelezni tudjuk, hogy ezen az oldalon szükségünk van a js/torol.js állományra is, amelyben a funkció megvalósítása található.

<?php
public function lista() {
    //...
    $this->template->set_data('js', array(
        'torol'
    ));
    //...
}
?>

Először is tudnunk kell, mikor kattint a felhasználó a törlés funkcióra. Sok hivatkozás van az oldalon, ezek közül kell a megfelelő kiválasztani. Ehhez a nézetben a törlés hivatkozáshoz egy stílusosztályt rendeltünk (torol), amely most nem megjelenésért felelős, hanem a JavaScript számára ad egy csatolási pontot. Nem minden egyes töröl gombhoz rendelünk eseménykezelőt, hanem megint csak delegálttal dokumentumszinten definiálunk egy függvényt, amely minden torol osztályú hivatkozás kattintási eseményét elkapja. Ezen belül az alapértelmezett műveletet megakadályozza, és megerősítést kér a felhasználótól a törlésre a confirm() függvénnyel.

$(function () {
    $(document).on('click', 'a.torol', function (e) {
        e.preventDefault();
        if (confirm('Biztosan törölni szeretné a bemutatót?')) {

        }
    });
});

Ezt követi a lényegi logika. Előkészítjük a szükséges adatokat: az url változóba kimentjük a hivatkozás eredeti URL-jét, a $bemutato változóba pedig a hivatkozáshoz tartozó bemutatót, illetve az ennek megfelelő li elemet. Ezt követően egy AJAX hívást intézünk a megfelelő URL-re. Válaszként JSON szöveget várunk a szervertől, amelyben egyetlen mező van, sikerült-e a törlés. Ha igen, akkor a DOM-ból eltávolítjuk a bemutatót.

var $this       = $(this),
    url         = $this.attr('href'),
    $bemutato   = $this.closest('.funkciok').closest('li');
    
$.ajax({
    url:        url,
    type:       'GET',
    dataType:   'json',
    success:    function (json) {
        if (json.sikerult) {
            $bemutato.slideUp('normal', function () {
                $(this).remove();
            });
        }
    },
    //...
});

Mivel a hívott URL nem változott, ezért szerveroldalon meg kell tudnunk különböztetni az adott vezérlőben, hogy a hívás hagyományosan vagy AJAX-szal történt. Ehhez egy segédosztályt hoztunk létre a libraries/Request.php állományban, amely egy Request osztályt definiál, és annak egyetlen metódusát, az is_ajax() függvényt. Ez megnézi, hogy a HTTP fejlécek között érkezett-e egy bizonyos fejléc. A jQuery ugyanis az AJAX híváskhoz automatikusan hozzáteszi ezt a fejlécet, így ez szerveroldalon vizsgálható.

<?php 
class Request { 
    function is_ajax() { 
        return (isset($_SERVER['HTTP_X_REQUESTED_WITH']) && ($_SERVER['HTTP_X_REQUESTED_WITH'] == 'XMLHttpRequest')); 
    } 
}
?>

A vezérlőben (Bemutato osztály torol metódusa) a feldolgozás a nézet megjelenítéséig független a hívás mikéntjétől. Ezt követően kell eldönteni, hogy milyen kimenet legyen. A Request osztály segítségével megnézzük, hogy AJAX-os hívás történt-e, ekkor ugyanis a megfelelő JSON szöveget kell válaszolnunk, amit a views/json/torol.php állományban definiáltunk. Ha hagyományos hívás volt, akkor pedig az eddig alkalmazott sablonmegjelenítés kell az egész oldal generálásához.

<?php
public function torol($bemutato_id) {
    $felhasznalo_id = $this->session->userdata('id');
    $siker = $this->bemutato_model->bemutato_torol($bemutato_id);

    $this->load->library('request');
    if ($this->request->is_ajax()) {
        $this->load->view('json/torol', array(
            'siker' => $siker,
        ));
    }
    else {
        redirect("bemutato/lista/{$felhasznalo_id}");
    }
}
?>

AJAX-os hívás esetén a választ a nézet biztosítja, és ennek megfelelő JSON szöveget ad vissza.

{
    "sikerult": <?php echo $siker; ?>
}

Vissza a tartalomjegyzékhez

Fel a lap tetejére
Új Széchenyi terv
A projekt az Európai Unió támogatásával, az Európai Szociális Alap társfinanszirozásával valósul meg.