Újratöltés nélküli adatcsere böngészőben
Eddigi cikkeimben általában biztonsággal kapcsolatos témákról volt szó. Most szakítok ezzel a hagyománnyal és ez alkalommal olyan módszerekről lesz szó, melyek használatával elérhetjük, hogy webes alkalmazásunk az oldal újratöltődése nélkül tudjon kommunikálni a szerverrel. Hűha, és miért is jó ez nekünk? Na többek közt erről is olvashatsz, ha a tovább feliratra kattintasz ;).
Két lehetséges megoldást fogunk megtekinteni. Ezek közül az első egy rejtett
Az első megoldás során valójában van újratöltődés, de ez a rejtett
A második megoldás előnye, hogy akár szinkron kommunikációt is megvalósíthatunk vele. Egyetlen hátránya, hogy jelenleg nem mindegyik böngésző támogatja: az 5.0 és nagyobb verziójú Internet Explorer illetve a Mozilla család. Ez elég jól lefedi az internetet használók böngészőinek nagy százalékát, és végképp kitűnő választás lehet intranetes alkalmazások során, ahol többnyire elő lehet írni, hogy a felhasználók milyen böngészőt használjanak.
Mielőtt belevágunk, két dologra szeretném felhívni a figyelmet. Egyrészt mindkét módszer esetén fontos látnunk, hogy JavaScript nélkül nem működnek. Ezért ha nem írható elő, hogy a JavaScript be legyen kapcsolva, akkor igyekezzünk olyan megoldásokat alkalmazni, amelyek kikapcsolt JavaScript mellett is működnek. Erre általános recept nem adható az alkalmazási terület sokrétűségéből fakadóan, de a legtöbb esetben megoldható. Másodsorban fontos látni, hogy ezek "vastagabb" klienst kívánnak, hiszen meg kell írnunk azokat a kódrészleteket, amelyek a szerver válaszának megfelelően elvégzik az oldalon a szükséges módosításokat: kiírják az új üzenetet, feltöltik a legördülő listát, kiteszik az almenüpontokat, stb. Ezért fontos megtalálni azt arányt, amely mellett ezek a megoldások kényelmesen használhatóak, de nem bonyolódik el túlságosan a kliens, ami hosszú távon lassíthatja a fejlesztés menetét.
Ennyi bevezető után lássuk a medvét!Kommunikáció
A megoldás lényege, hogy a kliens oldal az
Minden további magyarázat előtt lássuk, hogy hogyan is zajlik ez működés közben. A szerveroldali részek PHP nyelven készültek.
client.htmlserverVariable.phpserverTime.phpMint láthatjuk a példában a módszer használható mind GET, mind POST metódus esetén. Mindkét esetben a kérés célja (target attribútum) az
Ha megnézzük aFigyeljük meg, hogy a válasz szövegét nem közvetlenül írjuk ki, hanem alkalmazzuk rá a htmlspecialchars() függvényt, ugyanis ellenkező esetben ha az üzenetünk tartalmazna például egy
Ha a kérést mi állítjuk össze, akkor az beviteli elemek értékét ne közvetlenül adjuk hozzá a kéréshez, hanem használjuk a JavaScript
A fent bemutatott megoldás egyik hátránya, hogy minden egyes kérés megjelenik a böngésző history-jában, amiből gondunk adódhat a vissza gombra kattintva, illetve az oldal frissítésekor. Űrlap használatakor POST metódust alkalmazva megjelenik az a kis gyűlölt kérdés, hogy "Szeretnénk-e az űrlap adatok újbóli elküldését?". Ezen problémák kiküszöbölésére lássuk a következő továbbfejlesztett verziót. A szerveroldali részek változatlanok, csak a kliens módosult.
client.htmlElőször lássuk, hogy mi a megoldás a linkre kattintás által kezdeményezett kommunikáció esetén. A link helyett a formát használjuk, így elérjük, hogy a kérés ne kerüljön be a history-ba.
Az űrlap küldés esetén is hasonló trükköt használunk. Itt az
Ezzel kapcsolatban két észrevételem lenne. Egyrészt vegyük észre, hogy ezzel a módszerrel egy POST metódusú űrlapküldést is GET metódusúra transzformálunk. Ez két szempontból is érdekes lehet: előfordulhat, hogy több adatot szeretnénk küldeni, mint amire GET metódus esetén lehetőségünk van (és ez kevesebb mint amire POST metódus esetén lehetőségünk lenne), valamint az is fontos, hogy a szerver oldalon ne a
Ha a GET metódus által küldhető adatmennyiség számunkra nem elegendő, és kénytelenek vagyunk post metódust használni, akkor a következőket tartsuk szem előtt:
Igazából a fenti két megjegyzés bármely módszert választva jelentőséggel bír:Nem szeretném most részletezni, hogy mi mindent lehet ezzel az objektummal kezdeni, mi csak arra fogjuk használni, hogy adatokat küldjünk a szervernek post metódussal, majd a válasz megérkeztével meghívjuk a kezelő függvényt. Íme az ezt megvalósító kód:Menjünk végig lépésről lépésre, hogy pontosan lássuk, hogy mi történik itt.
Mint korábban említettem, érdemes kialakítani egy olyan rendszert, mely megkönnyíti a kiválasztott módszer használatát. A következőkben ennek egy lehetséges megvalósítását mutatom be. Először is próbáljuk ki működés közben. A kliens oldalon a lényeg a
Lássuk példánk esetén ez hogy néz ki:Az elküldendő adatok a
Most lássuk a szerveroldali részt. Minden kérést az
Ennyit szererettem volna ma ismertetni. Remélem cikkem érthető volt, a részletekért ajánlom a forráskódok megtekintését. Nem szeretném tovább szaporítani a szót, a lényeges lépésekről szó esett.
Még annyit azért megemlítenék, hogy használhatnánk olyan technológiákat is a kommunikációra, mint például a Java, ActiveX, Flash, hiszen ezek képesek kommunikálni a szerverrel, és többnyire képesek az oldal többi részét elérni. Sőt Java és ActiveX esetén foglalatokat (socketeket) is használhatunk, melyekkel lehetőségünk van olyan kommunikációra is amelyet a szerver kezdeményez. Viszont a Java kivételével ezek a megoldások nem teljesen platform függetlenek (Flash esetén nem vagyok igazából biztos ebben a kijelentésben), illetve például Mac IE5 nem támogatja a LiveConnect technológiát, amely lehetővé teszi ezen beágyazott objektumok kommunikációját az oldal többi részével JavaScripten keresztül. Ezért egy heterogén környezetben problémák adódhatnak alkalmazásukkal.
Eddig minden alkalommal a következő cikk témájával búcsúztam, most sem lesz ez másképp: fogalmam sincs. Valójában ötletem már van, de ha lenne olyan téma, amiről szívesen olvasnátok, írjátok meg bátran!
■ - Adódhatnak olyan helyzetek, amikor az állandó újratöltődés élvezhetetlenné" tenné az oldalt. Ilyen lehet például egy csevegő program, ahol lényegesen ergonómikusabb, ha a kommunikáció a háttérben zajlik.
- Sok esetben egy ilyen megoldás kényelmesebb a felhasználó számára. Gyakori eset, hogy egy beviteli elem tartalma egy másik elem tartalmától függ, pl. ország/megye kiválasztása. Ilyenkor használhatunk több lépcsős űrlapot, de mennyivel kényelmesebb, ha az ország kiválasztása után az oldal lekéri a hozzá tartozó megyéket, és a válasz alapján feltölti a megyék listáját.
- Egy komolyabb menüszerkezet esetén is hasznos lehet, ha az almenük tartalmát csak igény szerint tölti le az oldal.
- Szintén hasznos lehet olyan űrlap ellenőrzések megvalósítására, melyekhez szükség van szerver oldalon tárolt adatokra. Gondoljunk csak az "Ez a felhasználó név már foglalt" típusú üzenetekre.
- Látványos lehet adminisztrációs oldalakon történő használatuk, ami által webes programunk sokkal inkább egy asztali alkalmazás benyomását keltheti, mely a felhasználó számára is tetszetős, és ha ő örül, mi is örülünk.
- Ezenkívül volt egy olyan esetem is, amikor a szerver és a kliensek között elég kis sávszélességű kapcsolat volt, ezért fontos volt, hogy minél kevesebbszer kelljen az oldalt teljesen újratölteni. Sok funkciót ezért ily módon valósítottam meg. Például egy űrlap ellenőrzése így zajlott: adatok elküldése, ellenőrzés szerver oldalon, ha hiba volt, akkor a hibák visszaküldése, melyet a kliens megjelenített, ha nem, akkor ment a parancs, hogy a kliens továbbléphet a következő oldalra.
Két lehetséges megoldást fogunk megtekinteni. Ezek közül az első egy rejtett
<iframe>
elemet használ a kommunikáció megvalósítására, míg a másik az újabb böngészőkben elérhető, XmlHttpRequest
nevű objektumot. A neve ne tévesszen meg senkit, nem lesz szükségünk XML ismeretekre, arra fogjuk használni, hogy HTTP POST metódussal küldünk vele adatokat a szervernek, valamint fogadjuk a választ.Az első megoldás során valójában van újratöltődés, de ez a rejtett
<iframe>
elemen belül zajlik. Előnye, hogy szinte bármilyen (<iframe>
elemet) támogató böngésző esetén használható, hátránya, hogy a kommunikáció aszinkron, illetve látni fogjuk, hogy az alap megvalósítás nem minden szempontból tökéletes, ezért kicsit trükközni kell, hogy tényleg jól használható legyen.A második megoldás előnye, hogy akár szinkron kommunikációt is megvalósíthatunk vele. Egyetlen hátránya, hogy jelenleg nem mindegyik böngésző támogatja: az 5.0 és nagyobb verziójú Internet Explorer illetve a Mozilla család. Ez elég jól lefedi az internetet használók böngészőinek nagy százalékát, és végképp kitűnő választás lehet intranetes alkalmazások során, ahol többnyire elő lehet írni, hogy a felhasználók milyen böngészőt használjanak.
Mielőtt belevágunk, két dologra szeretném felhívni a figyelmet. Egyrészt mindkét módszer esetén fontos látnunk, hogy JavaScript nélkül nem működnek. Ezért ha nem írható elő, hogy a JavaScript be legyen kapcsolva, akkor igyekezzünk olyan megoldásokat alkalmazni, amelyek kikapcsolt JavaScript mellett is működnek. Erre általános recept nem adható az alkalmazási terület sokrétűségéből fakadóan, de a legtöbb esetben megoldható. Másodsorban fontos látni, hogy ezek "vastagabb" klienst kívánnak, hiszen meg kell írnunk azokat a kódrészleteket, amelyek a szerver válaszának megfelelően elvégzik az oldalon a szükséges módosításokat: kiírják az új üzenetet, feltöltik a legördülő listát, kiteszik az almenüpontokat, stb. Ezért fontos megtalálni azt arányt, amely mellett ezek a megoldások kényelmesen használhatóak, de nem bonyolódik el túlságosan a kliens, ami hosszú távon lassíthatja a fejlesztés menetét.
Ennyi bevezető után lássuk a medvét!
Kommunikáció <iframe>
használatával
A megoldás lényege, hogy a kliens oldal az <iframe>
elemen keresztül intéz kérést a szerverhez, ami a kérésnek megfelelő akciók végrehajtása után egy olyan oldalt (JavaScript kódot) generál az <iframe>
elembe, mely végrehatja a kérés kiszolgálásnak eredményétől függően a kliens oldalon esetlegesen szükséges változtatásokat.Minden további magyarázat előtt lássuk, hogy hogyan is zajlik ez működés közben. A szerveroldali részek PHP nyelven készültek.
client.html
<html>
<head>
<title>Egyszerű példa iframe elemen keresztül történő kommunikációra</title>
<script type="text/javascript">
function responseHandler(msg) {
alert(msg)
}
</script>
</head>
<body>
<iframe id="serverAction" name="serverAction" src="about:blank" style="width:0px; height:0px; border: 0px"></iframe>
<form action="serverVariable.php" method="post" target="serverAction">
Szerver változó neve: <input name="varName" value="HTTP_REFERER" />
<br />
<input type="submit" value="Lekérdez" />
</form>
<br />
<a href="/serverTime.php" target="serverAction">Szerver idő lekérdezése</a>
</body>
</html>
<?php
if (isset($_POST['varName'])) {
if (isset($_SERVER[$_POST['varName']])) {
$msg = $_SERVER[$_POST['varName']];
} else {
$msg = 'Hiba: nincs ilyen név? szerver változó!';
}
} else {
$msg = 'Hiba: a szerver változó nevét meg kell adni!';
}
header("Content-Type: text/html; charset=iso-8859-2");
echo '<script>window.parent.responseHandler("'.htmlspecialchars($msg).'")</script>';
?>
<?php
$msg = date('H:i');
header("Content-Type: text/html; charset=iso-8859-2");
echo '<script>window.parent.responseHandler("'.htmlspecialchars($msg).'")</script>';
?>
<iframe>
, melynek méreteit nullára állítjuk, hogy ne látszódjon. Használhatnánk a display:none;
CSS tulajdonságot is, de a Netscape 6-os verziója például figyelmen kívül hagyja az ilyen beágyazott kereteket, ezért célszerűbb a fenti megoldást választani.Ha megnézzük a
client.html
állományt, akkor a beágyazott keret mellett a másik érdekessége a responseHandler()
függvény. Ez egy úgynevezett callback függvény, melyekre általában igaz, hogy olyan függvények, melyek alkalmazásunkhoz tartoznak, de meghívásuk kívülről történik, és használatukkal általában lehetővé tesszük, hogy programunk egy külső eseményre tudjon reagálni. Jelen esetben ezen függvény meghívásával tudjuk jelezni, hogy a kérés kiszolgálása a szerveren megtörtént, és utasítjuk a kliens alkalmazást, hogy erre megfelelően reagáljon, ami jelen esetben egy a szerver által visszaadott üzenet megjelenítése. A függvény meghívását úgy érjük el, hogy a beágyazott keretbe a következő tartalmú választ küldjük:<script>window.parent.responseHandler("Ezt írjad ki jóóól")</script>
"
jelet, akkor a kiírt JavaScript kód hibás lenne. Még egy apróság, ha PHP oldalon a szövegeket idézőjelben adjuk meg, akkor figyeljünk arra, hogy kiíráskor a speciális karaktereket már a PHP is fedolgozza, ezért ha például egy "\n"
karaktert (sortörést) szeretnénk elhelyezni a válaszban, akkor PHP oldalon a "\\\n"
kell szerepeljen.Ha a kérést mi állítjuk össze, akkor az beviteli elemek értékét ne közvetlenül adjuk hozzá a kéréshez, hanem használjuk a JavaScript
escape()
függvényét, így biztosítjuk, hogy URL-ünk ne tartalmazzon nem megengedett karaktereket. Ezesetben akkor sem lesz probléma, ha esetleg valamelyik beviteli elem olyan karaktert tartalmazna (&
, =
) melynek egy URL-ben speciális jelentése van.A fent bemutatott megoldás egyik hátránya, hogy minden egyes kérés megjelenik a böngésző history-jában, amiből gondunk adódhat a vissza gombra kattintva, illetve az oldal frissítésekor. Űrlap használatakor POST metódust alkalmazva megjelenik az a kis gyűlölt kérdés, hogy "Szeretnénk-e az űrlap adatok újbóli elküldését?". Ezen problémák kiküszöbölésére lássuk a következő továbbfejlesztett verziót. A szerveroldali részek változatlanok, csak a kliens módosult.
client.html
<html>
<head>
<title>Továbbfejlesztett példa iframe elemen keresztül történő kommunikációra</title>
<script type="text/javascript">
function responseHandler(msg) {
alert(msg)
}
function getServerTime() {
frames["serverAction"].location.replace("serverTime.php");
return false;
}
function getServerVariable(form) {
frames["serverAction"].location.replace("serverVariable.php?varName="+escape(form.elements["varName"].value));
return false;
}
</script>
</head>
<body>
<iframe id="serverAction" name="serverAction" src="about:blank" style="width:0px; height:0px; border: 0px"></iframe>
<form action="serverVariable.php" method="post" target="serverAction" onsubmit="return getServerVariable(this);">
Szerver változó neve: <input name="varName" value="HTTP_REFERER" />
<br />
<input type="submit" value="Lekérdez" />
</form>
<br />
<a onclick="return getServerTime()" href="/about:blank" target="serverAction">Szerver idő lekérdezése</a>
</body>
</html>
onclick
eseményére egy függvényt akasztunk. Ez indítja el ténylegesen a kérést és ezután false
értékkel tér vissza, ezáltal megakadályozva a linkhez tartozó alapértelmezett működést, ami a célpontra váltás lenne. A másik kulcs momentum az, hogy a frames["serverAction"].location = "uj cim"
frames["serverAction"].location.replace("uj cim")
Az űrlap küldés esetén is hasonló trükköt használunk. Itt az
onsubmit
eseményre akasztunk egy saját függvényt, amiből hasonlóan az előzőhöz szintén false
értékkel térünk vissza, amivel meggátoljuk az űrlap elküldését. A függvényünk előveszi az űrlapmező értékét, és ezt használva a fenti módszerrel küld egy kérést a szervernek.Ezzel kapcsolatban két észrevételem lenne. Egyrészt vegyük észre, hogy ezzel a módszerrel egy POST metódusú űrlapküldést is GET metódusúra transzformálunk. Ez két szempontból is érdekes lehet: előfordulhat, hogy több adatot szeretnénk küldeni, mint amire GET metódus esetén lehetőségünk van (és ez kevesebb mint amire POST metódus esetén lehetőségünk lenne), valamint az is fontos, hogy a szerver oldalon ne a
$_POST
tömbön keresztül próbáljuk meg elérni a küldött adatokat. A másik észrevételem, hogy az űrlap elemek alapján történő kérés összeállítása automatizálható. Írhatunk egy függvényt, mellyel végigmegyünk az űrlap elemein, és a nevüknek, típusuknak illetve értéküknek megfelelően összeállítjuk a kérést.Ha a GET metódus által küldhető adatmennyiség számunkra nem elegendő, és kénytelenek vagyunk post metódust használni, akkor a következőket tartsuk szem előtt:
- Érdemes írni egy olyan függvényt, mely paraméterül megkapja a meghívandó szerveroldali szkript nevét, egy név-érték párosokból álló tömböt, és a szerver válaszát követően meghívandó callback függvényt. Ezek alapján dinamikusan létrehoz az
<iframe>
elemben egy űrlapot és elküldi azt, majd a válaszban érkező adatokkal (pl. egy JavaScript tömb) meghívja a callback függvényt. Szerettem volna erre is példát mutatni, de egyelőre a dinamikusan felépített űrlap elküldésével Opera és Mozilla alatt gond van (Internet Explorer és Konqueror esetén működik). Ha sikerül megvalósítani, akkor frissítem a cikket és ezt egy hozzászólással jelezni fogom.
- Ebben az esetben a kérés history-ban való megjelenését nem tudjuk megakadályozni, viszont az oldal frissítésének problémáját meg tudjuk oldani. A szerveroldali szkript ne közvetlenül adja ki a választ, hanem a
Location
HTTP fejléc segítségével irányítsa át magát egy olyan oldalra, ami generálja a kimenetet.
Igazából a fenti két megjegyzés bármely módszert választva jelentőséggel bír:
- Bármelyiket választjuk is, ha gyakrabban van rá szükségünk, akkor érdemes egy kis keretrendszert létrehoznunk, mely egy interfészt nyújt a kommunikáció megvalósítására mind kliens, mind szerver oldalon. Így a konkrét esetekben lényegesen rövidebb idő alatt írhatjuk meg a szükséges kódokat, és az szebb, átláthatóbb is lesz. Egy lehetséges kialakításra láthatunk példát a következő módszer ismertetésekor.
- Ha kérésünk az alkalmazás állapotának megváltoztatásával jár (pl. adatbázisban való rögzítés), akkor GET metódusú kérés esetén is célszerű használni a
Location
fejléccel való átirányítást a kimenet generálás előtt.
Kommunikáció XmlHttpRequest objektum használatával
A frissebb böngészők (IE 5.0 és újabb, Mozilla) esetén használhatjuk a fenti objektumot, mellyel lehetőségünk van a böngészőből közvetlenül kommunikálni. Gyakran láthattuk, hogy egyes amúgy szabványban rögzített dolgok megvalósítása mennyire eltérő lehet. Jelen esetben még ennyi szerencsénk sincs, jelenleg az XmlHttpRequest objektum semmilyen W3C szabványnak nem része. Ennek megfelelően az Internet Explorer alatt egy ActiveX objektumként van jelen, míg Mozilla alatt egy JavaScript konstruktor meghívásával hozhatunk létre egy ilyen objektumot. A helyzetet némileg az is bonyolítja, hogy Internet Explorer esetén az ActiveX objektum neve függ a windows alá telepített MSXML parser verziójától. No nem kell félni, szerencsére a fenti problémát már megoldották helyettünk. Használni fogjuk Erik Arvidsson XmlExtras csomagját, mely elfedi az objektum létrehozásának gondjait, használatával a létrehozáshoz csak ennyit kell tennünk:
var xmlHttp = XmlHttp.create();
/*1*/ var xmlHttp = XmlHttp.create();
/*2*/ xmlHttp.open('POST', 'aMeghivandoScript.php', false);
/*3*/ xmlHttp.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded; charset=utf-8');
/*4*/ xmlHttp.send("param1=value1¶m2=value2");
/*5*/ responseHandler(xmlHttp.responseText);
- Objektum létrehozása.
- Kapcsolat létrehozása. Itt megadjuk a kérés típusát, a kérés célját, a
false
megadásával pedig azt jelezzük, hogy szinkron kommunikációt szeretnénk.
- Itt a kérés fejlécét állítjuk be, melyben jelezzük, hogy milyen adatokról is van szó, és megadjuk a karakter kódolás típusát is. Fontos tudni, hogy szöveg küldése esetén ez az objektum mindenféleképpen UTF-8 kódolást használ.
- Elküldjük a tényleges adatokat. A szinkron kommunikáció használata miatt a
send()
függvény csak akkor tér vissza, ha megérkezett a válasz. Majd látni fogjuk, hogy hogyan kell kezelni azt az esetet, ha aszinkron kommunikációt szeretnénk használni.
- Meghívjuk a kezelő függvényt a válasszal, ami tetszőleges szöveg, nekünk kell gondoskodni az értelmezéséről.
Mint korábban említettem, érdemes kialakítani egy olyan rendszert, mely megkönnyíti a kiválasztott módszer használatát. A következőkben ennek egy lehetséges megvalósítását mutatom be. Először is próbáljuk ki működés közben. A kliens oldalon a lényeg a
serverAction
objektum. Ez egy általunk gyártott JavaScript objektum, melyen keresztül meg tudunk hívni egy szerver oldali funkciót, ehhez meg kell adnunk a funkció nevét, az átadandó adatokat, és a callback függvény nevét, mely a válasz kezelését végzi. Ezután a szerveroldali rész meghívja a megfelelő funkciót, melynek egy $result
nevű tömbbe kell pakolnia azokat az adatokat, melyeket szeretne a kliensnek visszaküldeni. Ezt követően a szerveroldali rész ezt a tömböt átalakítja egy olyan szöveggé, amellyel ha meghívjuk az eval()
JavaScript függvényt, akkor a PHP tömbnek megfelelő JavaScript tömb jön létre. A responseText
ezt a szöveget fogja tartalmazni, melyre alkalmazzuk az eval()
függvényt, és a keletkezett result
tömbbel hívjuk meg a kéréskor megadott kezelő függvényt.Lássuk példánk esetén ez hogy néz ki:
function addItem() {
itemName = document.getElementById("itemName");
var data = new Array();
data[data.length] = serverAction.createNVArray("itemName", itemName.value.trim());
serverAction.call("addItem", data, addItemHandler);
}
data
tömbbe kerülnek, melynek elemei olyan tömbök melynek name
kulcsa tartalmazza a paraméter nevét, value
kulcsa pedig a paraméter értéket. A createNVArray()
segédfüggvény csak annyit csinál, hogy egy név-érték párosból csinál egy ilyen tömböt. A lényeg a call()
függvény, ahol megadjuk, hogy a szerveren az addItem
funkciót szeretnénk meghívni, megadjuk az elküldendő adatokat és hogy a választ kezelő függvény az addItemHandler
legyen. Lássuk a call függvényt:
serverAction.call = function(action, data, requestHandler) {
/*1*/
var params;
params = '__action__=' + action;
for(var i = 0; i < data.length; i++) {
params += '&' + data[i].name + '=' + encodeURIComponent(data[i].value);
}
var xmlHttp = XmlHttp.create();
xmlHttp.open('POST', 'serverActionHandler.php', true);
/*2*/
xmlHttp.onreadystatechange = function () {
if (xmlHttp.readyState == 4) {
var result = new Array();
eval(xmlHttp.responseText);
serverAction.callHandler(result);
}
}
xmlHttp.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded; charset=utf-8');
xmlHttp.send(params);
/*3*/
this.requestHandler = requestHandler;
}
- Összeállítjuk a paramétereket tartalmazó szöveget. Először is lesz egy
__action__
nevű paraméter, mely a meghívandó szerver oldali funkció nevét fogja tartalmazni. A másik lényeges dolog, hogy mivel UTF-8 kódolásról van szó, nem használhatjuk azescape()
függvényt, hanem helyette azencodeURIComponent()
-re van szükség. Persze ez például IE 5.0 esetén még nem volt, pedig ez a böngésző most benne van a célközönségünkben, de itt találhatunk erre is megoldást.
- A nagyobb rugalmasság kedvéért (lassú válasz esetén sincs gond) aszinkron módú kommunikációt fogunk használni. Ennek megvalósításának módja, hogy egy eseménykezelőt akasztunk a
onreadystate
attribútumra, ami ennek változásakor mindig meghívódik. Ha ennek értéke 4 (válasz megjött), akkor azeval()
segítségével létrehozza a már fent említettresult
tömböt, majd meghívja aserverAction
objektumcallHandler()
függvényét, mely tulajdonképpen annyit csinál, hogy meghívja a szükséges kezelőfüggvényt ezzel a tömbbel. Azért nem közvetlenül azt hívjuk meg, mert így lekezelhetőek általánosan olyan szituációk, mind például rossz, nem létező funkciót szerettünk volna meghívni.
- Az objektum
requestHandler
változójába elmentjük a kezelőfüggvényt. A megfogalmazás furcsának tűnhet, de a JavaScript nyelvben van függvény típusú változó. Ezt fogja majd használni acallHandler()
függvény a válasz kezelésekor.
Most lássuk a szerveroldali részt. Minden kérést az
serverActionHandler.php
nevű script fogad:
<?php
/*1*/
include_once('include/arrayToJsArray.php' );
define('NL', "\n");
/*2*/
foreach( $_POST as $name => $value ) {
$_POST[$name] = iconv('UTF-8', 'ISO-8859-2' , $value);
}
/*3*/
$result = array();
$result[ 'errorCode' ] = 0;
$result[ 'errorMsg' ] = '';
/*4*/
if (isset($_POST['__action__'])) {
if (file_exists('action/'.$_POST['__action__'].'.php')) {
ob_start();
include('action/'.$_POST['__action__'].'.php');
$error = ob_get_contents();
ob_end_clean();
if ($error != '') {
// Ide jöhet egyéb hibakezelés (pl.: loggolás)
$result['errorCode'] = -2;
$result['errorMsg'] = 'A ( '.$_POST['__action__'].') parancs végrehajtása közben hiba történt!';
}
} else {
$result['errorCode'] = -1;
$result['errorMsg'] = 'Ilyen parancs ( '.$_POST['__action__'].') nem létezik!';
}
}
/*5*/
// Szokásos cachelést megakadályozó fejlécek
header('Expires: Mon, 26 Jul 1997 05:00:00 GMT');
header('Last-Modified: '.gmdate('D, d M Y H:i:s' ).' GMT');
header('Cache-Control: no-store, no-cache, must-revalidate');
header('Cache-Control: post-check=0, pre-check=0', false);
header('Pragma: no-cache');
// Válasz típusának beállítása
header('Content-type: text/plain; charset=utf-8');
echo arrayToJsArray($result, 'result', NL, 'utf-8');
?>
- Betöltjük a tömbkonverziót végző függvényt, illetve definiáljuk a sorvége jel konstanst (Benjamin után szabadon ;)).
- Az adatokat UTF-8 kódolással kapjuk. Többnyire nekünk viszont ISO-8859-2 formátumban van rájuk szükségünk. Az átalakításra a PHP iconv modulját használom, mert ez létezik mind Windows, mind Linux platformon. Itt jegyezném meg, hogy a JavaScript tömb generálásakor viszont UTF-8 kódolású szöveget kell generálnunk, a szükséges karakter konverziót a tömb konvertáló függvény elvégzi. Amennyiben UTF-8 kódolással van szükségünk, akkor ezt a részt ki is hagyhatjuk, és a konvertáló függvénynek sem kell az utolsó paramétert átadni.
- Definiáljuk a
$result
tömböt. Ennek van két kiemelt fontosságú eleme. AzerrorCode
nevű elemmel jelezhetjük a kliens oldali kezelő függvénynek, hogy a funkció végrehajtásakor valami gond volt. Én azt a konvenciót használom, hogy a negatív értékek jelentik a "külső" hibákat (nincs ilyen funkció, valami gond volt, igény szerint kiegészíthető jogosultság kezeléssel is), ezek kezelése történhet egységesen acallHandler()
függvényben. Pozitív értékek jelentik a funkció végrehajtása során keletkezett hibákat. AzerrorMsg
nevű elemben helyezhetjük el az esetleges szöveges hibaüzenetet.
- Betöltjük némi ellenőrzéssel a funkciót megvalósító PHP kódot. Jelen esetben ez az
__action__
paraméter értékével megegyező nevű PHP állomány azaction
könyvtárban. Ez igazából a$_POST
tömbön keresztül kapja meg a küldött adatokat, és a$result
tömbbe téve küldhet adatot a kliensnek. Sok funkció esetén szükséges lehet ezek csoportosítása. Erre azt ajánlom, hogy a funkciót lehessen ilyen formában megadni:"xxx.item.add"
, amelyhez azaction/xxx/item/add.php
állomány tartozik. Ezt nem nagy ördöngősség megoldani, és tapasztalatból mondhatom, hogy megéri a káosz elkerülése végett.
- Ezután már csak kiadjuk a szükséges HTTP fejléceket (ne gyorsítótárazódjon a válasz, válasz típusa), majd kiírjuk a JavaScript tömböt megvalósító szöveget.
A cikkhez kapcsolódó forráskódok (10.02 KB)
Még annyit azért megemlítenék, hogy használhatnánk olyan technológiákat is a kommunikációra, mint például a Java, ActiveX, Flash, hiszen ezek képesek kommunikálni a szerverrel, és többnyire képesek az oldal többi részét elérni. Sőt Java és ActiveX esetén foglalatokat (socketeket) is használhatunk, melyekkel lehetőségünk van olyan kommunikációra is amelyet a szerver kezdeményez. Viszont a Java kivételével ezek a megoldások nem teljesen platform függetlenek (Flash esetén nem vagyok igazából biztos ebben a kijelentésben), illetve például Mac IE5 nem támogatja a LiveConnect technológiát, amely lehetővé teszi ezen beágyazott objektumok kommunikációját az oldal többi részével JavaScripten keresztül. Ezért egy heterogén környezetben problémák adódhatnak alkalmazásukkal.
Eddig minden alkalommal a következő cikk témájával búcsúztam, most sem lesz ez másképp: fogalmam sincs. Valójában ötletem már van, de ha lenne olyan téma, amiről szívesen olvasnátok, írjátok meg bátran!
ez tök jó
Annak ellenére, hogy ismerem a technikákat szívesen olvastam végig. Jól írsz :)
Én még annyit hozzátennék, hogy egyoldalú (cmsben egy sor törlése, látogató felbontásának db-be rögzítése stb...) komunikációnál pl lehet használni ezt a megoldást is:
Újratöltés nélküli adatcsere böngészőben
és végre itt van az újjaim alatt a technológia...
már csak használni kell :)
--------
Poetro
Hmm
A módszerek jók, csak a szük átmérő itt az IE, és sajna az árnyékában nehéz igazán jól müködő webes felületet kiépíteni. Majd most a nagy IE lyukak folytán javul a helyzet és tényleg érdemes lesz ilyenekre beruházni.
MaDog voltam!
még JS-t, még!!!
> Valójában ötletem már van, de ha lenne olyan téma,
> amiről szívesen olvasnátok, írjátok meg bátran!
JavaScript témában elég elhanyagolt volt eddig a weblabor, ezen javíthatnál! Konkrét kívánság nincs, csak legyen benne sok JS, mert az jó.
xmlhttprequest - böngészőtámogatás
Update 13 May 2004: Ned Baldessin writes in to say that Safari now handles the XMLHTTPRequest object just like Mozilla. Fantastic stuff - hopefully that feature is backported to Konqueror - that would leave Opera as the only browser not yet supporting this feature.
Update 10 June 2004: Mendrik writes in with a possible solution for Opera. Using Java's liveConnect in Opera allows exchanging of XML documents. He has an example page up. Quick test in Opera 7.20 worked.
Vagyis ha minden igaz Safarival és Operával is megvalósítható az xmlhttprequest.
Forrás: http://www.isolani.co.uk/blog/atom/JavascriptAtomApiClientUsingXmlHttpRequest
Kicsit kuzdeni kell majd
Koszi a linket.
A safarit mar nezegettem egyszer, de az o tamogatasahoz kell nemi valtoztatast eszkozolni.
http://developer.apple.com/internet/webcontent/xmlhttpreq.html
"Note: It is essential that the data returned from the server be sent with a Content-Type set to text/xml. Content that is sent as text/plain or text/html is not accepted by the instance of the request object."
Az operas megoldast megnezem majd alaposabban, kar hogy nem tul szoszatyar a forras.
Felho
Inicializálás
Opera 7.60 már ismeri ezt a kellemes függvényt. Konqueror is...
Sajnos XP SP2-es Windózon futó alap beépített explórerben h
Nem tudo mi lehet a gond, az lenne a jó, ha alapbeállításokkal működne a dolog, mert így nem adhatok közzé dolgokat, mivel az esetek 90%-ában alapbeállítású böngészők futnak, legalábbis sztem...
Köszi a választ!
üdv.: Pistván.
Fájl feltöltése
Ha nem járható út, mit javasoltok helyette? Esetleg arra gondoltam, hogy egy iframe-be rakom a képfeltöltő formot és úgy postolom, hogy a szülő változatlan maradjon, de ez problémásnak tűnik számomra, és nem is elegáns.
Nem
iframe
-es megoldás egész járható útnak tűnik: az iframe legyen teljes ablak méretű, majd mikor feltöltötted a fájlt, rejtsed el. Vagy egy iframe legyen már eleve a target-e a kitett formnak, akkor sem töltődik újra az oldal. Az biztos, hogy ez nem problémás. Az elegánsság egy másik kérdés, de szerintem azzal sincsen baj. Ennek ellenére szerintem ebben az esetben felesleges ezt a technológiát használni. Tippért esetleg elugorhatsz a GMail-hez, hogy ők hogyan oldották meg a fájlok csatolását.-boogie-
IFrame
A megoldás az lett, hogy a dialógus aljára tettem egy iframet, abban van a form, ami a szerveren a megfelelő php-nak átlöki a fájlt. Semmi target megadás nem kell, önmagába tér vissza.
Target
Ezt az iframe-es read only-t nem tudom, mi, de ilyen nincs, a weblap szerkesztője mondja meg akár így, akár úgy, hogy mi kerüljön bele. Sőt, a felhasználó nem is nagyon tudja ezt befolyásolni. (?) Szerintem a feltöltő mező viselkedésével kevered, de nem értem.
-boogie-
Félreérthető volt
<form id="kuld">
<input type="file" id="up">
document.getElementById("file") = "c:\winnt\php.ini";
document.getElementById("kuld").submit();
Közben elővettem a könyvet, a 720. oldalon ír erről, de az írja, hogy csak a korai böngészőkben volt read-only, szóval most már működne ez.
Csak nem jó
Esetleg valami áthidaló megoldás?
iframe a target
Attila
Mert nem tudomhogyan
Ez az iform:
Ez a tartalma:
<input type="submit" value="upload">
<input type="hidden" name="op" value="upload">
Köszi
kód
Attila
BEmásoltam
Hiba: uncaught exception: [Exception... "Component returned failure code: 0x80004005 (NS_ERROR_FAILURE) [nsIObserverService.removeObserver]" nsresult: "0x80004005 (NS_ERROR_FAILURE)" location: "JS frame :: chrome://global/content/bindings/browser.xml :: destroy :: line 569" data: no]
böngésző
Ilyesmi kódnak működnie kell elvileg:
upload.html:
Így sem
két form?
"Ami nincs eltörve, ne ragaszd meg." :-)
Attila
:S
Az a nagy büdös helyzet, hogy hülye vagyok ehhez :(
Ha elmagyarázná valaki hogy a fájlt, hogy töltöm fel nagyon megköszönném
A feltöltő ürlap kész van, a php vel vagyok gondban
A feltöltendő fájl hol jelenik meg? :S vagy egyáltalán hova töltöm fel?
kicsit zavaros az egész...
php kézikönyvet is olvastam már, de nem jutottam előbbre
A segítséget előre is köszönöm
sajax
Sajax nem játszik?
dátum ;)
Nézd meg a cikk dátumát. Amikor ez íródott, még AJAX sem létezett. :) Ráadásul a cucc amiről a végén szó van, az 2003 januárjában készült.
Felhő
InternetExplorer probléma
Valaki meg tudja mondani hogy a mai legújabb InternetExplorer böngészőben miért nem működik ez az XmlHttpRequest-es megoldás?
Ugyanakkor Safariban, Operában, Chromban és Mozillában is működik.
Nagy segítség lenne, köszönöm.
Melyik
Internet Explorer 9-ben
Letöltöttem a "A cikkhez kapcsolódó forráskódok" csomagot és az XmlHttpRequest mappában található kódokat használtam fel.
Eredetileg a példaprogram azt csinálja, hogy egy üres select mezőbe felveszi elemként az általunk egy input mezőbe megadott szavakat. Mikor még hozzá sem nyúltam a kódhoz már akkor sem működött Explorerben csak a másik 4 böngészőben amiket az eredeti kérdésemben megneveztem.
Ezt csak azért írom mert közben úgy alakítottam át a kódot, hogy ha az input mezőbe lenyomok egy gombot akkor a program azonnal kiírja hogy a jelenleg az inputban található szórészletre milyen találatok vannak az adatbázisban. De végig ellenőriztem: nem hibáztam amikor átírtam a progit és ebben biztos vagyok és mint előbb írtam már az eredeti program sem működött.
Van egy ilyen részlet a cikkben: "nem használhatjuk az escape() függvényt, hanem helyette az encodeURIComponent()-re van szükség. Persze ez például IE 5.0 esetén még nem volt"
Gondolom azóta már ismeri ezt a függvényt az Explorer, de én kipróbáltam, hogy egyetlen egyszerű betűt elküldtem ezen függvény kihagyásával és ez szintén csak a másik 4 böngészőben működött.
A program JavaScript kódjai 3 fájlba vannak elosztva + a html oldalban található JS és én nekem erős a gyanúm hogy talán ott van egy olyan megoldás amit az Explorer nem ért és nem a PHP kódban. De mivel csak most először csinálok olyat, hogy oldalfrissítés nélkül kérjek le adatokat így én nekem nem sikerült hibát találnom a kódokban.
7 éves
Álljon itt egy kicsit frissebb XHR objektum létrehozó kód a jQuery 1.5 alapján:
Internet Explorer 9 hibák
Illetve köszönöm a segítségedet, ma este újra neki ülők a kérdésnek.
Azzal pedig tisztában vagyok, hogy nagyon régi a cikk, de ez azóta is a legjobb leírás amit én megtaláltam. Ez egy olyan alap amin én hobbiprogramozó is eltudok indulni az új megoldások felé.
IE 9: F12 =>" firebug"
Személyes javaslat: használj keretrendszert. még hobbi projectként is. Én is anno amikor ismerkedtem a JavaScript-el megírtam a saját keretrendszerem, de rájöttem hogy a tanulási fázist leszámítva totál felesleges. jQuery és a hozzá egymillió plugin az alap problémák 99% -át orvosolja, minimális időráfordítással.
Köszönöm
Egyszerű hiba volt
20.sor: "itemName" helyett "var itemName" a helyes
30.sor: "itemList" helyett "var itemList" a helyes
Ilyen alapvető hibákra nem számítottam. Meg egy kicsit fura, hogy a többi böngésző ezt engedi.
Köszönöm mindenkinek aki hozzászólt a kérdésemhez és köszönöm az F12-es exploreres segítséget!