ugrás a tartalomhoz

Újratöltés nélküli adatcsere böngészőben

Hodicska Gergely · 2004. Júl. 14. (Sze), 04.24
Ú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 ;).
  • 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>
serverVariable.php

<?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>';
?>
serverTime.php

<?php
    $msg = date('H:i');

    header("Content-Type: text/html; charset=iso-8859-2");
    echo '<script>window.parent.responseHandler("'.htmlspecialchars($msg).'")</script>';
?>
Mint 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 <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>
Figyeljü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 " 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>
Először lássuk, hogy mi a megoldás a linkre kattintás által kezdeményezett kommunikáció esetén. A link 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"
helyett a
frames["serverAction"].location.replace("uj cim")
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 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();
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:

/*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&param2=value2");
/*5*/ responseHandler(xmlHttp.responseText);
Menjünk végig lépésről lépésre, hogy pontosan lássuk, hogy mi történik itt.
  1. Objektum létrehozása.
  2. 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.
  3. 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.
  4. 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.
  5. 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);
}
Az elküldendő adatok a 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;
}
  1. Ö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 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, pedig ez a böngésző most benne van a célközönségünkben, de itt találhatunk erre is megoldást.
  2. 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 az eval() segítségével létrehozza a már fent említett result tömböt, majd meghívja a serverAction objektum callHandler() 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.
  3. 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 a callHandler() 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');
?>
  1. Betöltjük a tömbkonverziót végző függvényt, illetve definiáljuk a sorvége jel konstanst (Benjamin után szabadon ;)).
  2. 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.
  3. Definiáljuk a $result tömböt. Ennek van két kiemelt fontosságú eleme. Az errorCode 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 a callHandler() függvényben. Pozitív értékek jelentik a funkció végrehajtása során keletkezett hibákat. Az errorMsg nevű elemben helyezhetjük el az esetleges szöveges hibaüzenetet.
  4. 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 az action 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 az action/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.
  5. 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.

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!
 
1

ez tök jó

connor · 2004. Júl. 14. (Sze), 12.05
Tetszik a cikk, tök jó a téma :))
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:

<img src="/x.php?adat=1&adat2=2">
/me
2

Újratöltés nélküli adatcsere böngészőben

Poetro · 2004. Júl. 14. (Sze), 17.51
Na erre a cikkre vártam már 1K éve...
és végre itt van az újjaim alatt a technológia...
már csak használni kell :)
--------
Poetro
3

Hmm

MaDog · 2004. Júl. 14. (Sze), 23.58
Megint csak gratula a cikkhez. Jól írsz.

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!
4

még JS-t, még!!!

T.G · 2004. Júl. 15. (Cs), 00.25
Csatlakozom előttem szólókhoz: gratula & köszönet!!!!

> 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ó.
5

xmlhttprequest - böngészőtámogatás

gongadze · 2004. Júl. 15. (Cs), 04.35
Mivel fordítani nem tudok csak másolok:

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
6

Kicsit kuzdeni kell majd

Hodicska Gergely · 2004. Júl. 15. (Cs), 11.32
Szia!

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
7

Inicializálás

chx · 2004. Aug. 25. (Sze), 07.41
Itt is találhatunk egy inicializáló függvényt, és nekem úgy tűnik, hogy éppen az IE különböző dolgait jobban kezeli mint az XMLExtras-ban lévő, valószínűleg a kettő összegyúrását érdemes használni. Írok az XMLExtras emberkének mindenesetre.

Opera 7.60 már ismeri ezt a kellemes függvényt. Konqueror is...
8

Sajnos XP SP2-es Windózon futó alap beépített explórerben h

Anonymous · 2005. Feb. 9. (Sze), 10.24
Sajnos XP SP2-es Windózon futó alap beépített explórerben hiba keletkezik ha a 'XmlHttpRequest' példát betöltöm és felvetetnék egy elemet, ugyanakkor FireFox-al simán futik. a hibaüzenet: "Az objektum nem támogatja ezt a tulajdonságot vagy metódust." Egyébkéntg minden ActiveX engedélyezett...
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.
9

Fájl feltöltése

Anonymous · 2005. Már. 19. (Szo), 21.38
Van-e arra lehetőség, hogy ezzel a megoldással fájlt töltsek fel? A TinyMCE-hez szeretnék írni egy képkezelő dialógust. A szerver oldali képböngészés már kész van, azzal semmi probléma nincs, de kellene bele egy olyan rész, ahol képet tudok átadni a szervernek.

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.
10

Nem

Bártházi András · 2005. Már. 19. (Szo), 22.42
Ezzel a módszerrel sincs lehetőség arra, hogy a kliens gépen turkáljon valaki. :) Szerencsére. Az 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-
11

IFrame

Anonymous · 2005. Már. 19. (Szo), 23.03
Akkor maradok az iframenél, bár úgy tudtam, hogy az pont ilyen esetek kivédésére read only, csak az user választhat ki vele tartalmat, nem lehet a háttérben feltölteni.

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.
12

Target

Bártházi András · 2005. Már. 20. (V), 00.25
Persze, hogy nem kell, de ha feltöltő HTML mezőt nem az iframe-be teszed, hanem az oldalba, az ezt tartalmazó formot egy iframe-be irányítod, akkor egyáltalán nem fog látható újratöltődés végrehajtódni. De a lényeg, hogy megy. :)

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-
13

Félreérthető volt

Anonymous · 2005. Már. 20. (V), 10.13
Bocs, félreérthető voltam. A file input mezőről olvastam a JS Bible 4-ben, hogy read only, hogy ne lehessen egy hidden formon keresztül fálokat tölteni a gépről a user tudta nélkül. Csak a user adhat tartalmat a file mezőnek, vagy kézzel, vagy a selectorral. Ott azt írták, nem működik ez:
<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.
14

Csak nem jó

Anonymous · 2005. Már. 20. (V), 11.40
Megpróbáltam a rejtett formos feltöltést, az eredeti lapon egy file mezőben bekértem a fájlt, egy button onclickre rákötöttem, hogy másolja át a fájl paraméterét a rejtett formba és küldje el, de hosszas kínlódás után is arra jutottam, hogy nem engedi, az belőbb említett biztonsági okokból. Csak úgy enged fájlt feltölteni, ha a user maga választotta ki és személyesen bök a submitra.

Esetleg valami áthidaló megoldás?
15

iframe a target

attlad · 2005. Már. 20. (V), 12.12
Miért nem az iframe-et adod meg a form target-jének ahogy már javasolták? Resetelni meg tudod a file mezőt is.

Attila
16

Mert nem tudomhogyan

Anonymous · 2005. Már. 20. (V), 13.37
Mert nem tudom hogyan kell, esetleg konkrét forrással bemutatnád?

Ez az iform:
<iframe id="uploader" name="uploader" src="uploader.php" style="width:280px; height:70px; border:0px;"></iframe>

Ez a tartalma:
<input type="file" name="upload" size="30" id="dummyfile">
<input type="submit" value="upload">
<input type="hidden" name="op" value="upload">


Köszi
17

kód

attlad · 2005. Már. 20. (V), 13.50
Kb. ez kerül egy fájlba:

<form action="uploader.php" target="uploader">
  <input type="file" name="upload" size="30" id="dummyfile">
  <input type="submit" value="upload">
  <input type="hidden" name="op" value="upload">
</form>

<iframe id="uploader" name="uploader" src="uploader.php" style="width:280px; height:70px; border:0px;"></iframe>
Az uploader.php meg egy másik fájl.

Attila
18

BEmásoltam

Anonymous · 2005. Már. 20. (V), 19.12
Így egy-egyben bemásoltam, ez a válasz:

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]
19

böngésző

attlad · 2005. Már. 20. (V), 21.00
Ne egy az egyben másold mert az nem kész kód volt, hiányzik belőle az enctype meg a metódus megadása is. Amúgy nálam nincs ilyen hiba, nem lehet, hogy vmi bővítmény okozza vagy hasonló? Más böngészővel se megy?

Ilyesmi kódnak működnie kell elvileg:
upload.html:

<form action="upload.php" target="uploadFrame" enctype="multipart/form-data" method="post">
  <input type="file" name="upload" id="upload">
  <input type="submit" value="Feltöltés">
</form>

<iframe id="uploadFrame" name="uploadFrame" src="upload.php" style="width: 500px; height: 300px;"></iframe>
upload.php:

<pre>
<?php

if ($_SERVER['REQUEST_METHOD'] === 'POST') {
  var_dump($_FILES);
}

?>
</pre>
Attila
20

Így sem

Anonymous · 2005. Már. 20. (V), 22.25
Természetesen azt betettem, csak arra mondom, hogy innen másoltam át, amit írtál, elgépelés esete nem áll fenn, de ugyan azt az üzenetet kapom mindig. Ezt adta akkor is, amikor kívülről próbáltam átrakni a file mező tartalmát. Arra gyanakszom, hogy biztonság okokból nem engedi manipulálni a másik formban a file mezőt. Ha a formot fizikailag is az iframe forrásában helyezem el, semmi baja.
21

két form?

attlad · 2005. Már. 21. (H), 10.26
Most akkor nem egy form van? Nem egészen világos miért akarod a file mezőt "manipulálni"/változtatni. Szerintem nincs erre szükség. Mindenesetre a file mező értékét alapvetően nem tudod megváltoztatni csak resetelni lehet meg olvasni, (meg ff alatt pl. cloneNode függvénnyel másolni is, bár ez inkább bug). De ha iframebe rakva működik minden jól, akkor szerintem használd úgy, ha így sehogy se akar összejönni.

"Ami nincs eltörve, ne ragaszd meg." :-)

Attila
24

:S

highooo · 2006. Júl. 2. (V), 19.31
Hi!!!

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
22

sajax

Anonymous · 2006. Már. 30. (Cs), 01.13
Sziasztok!

Sajax nem játszik?
23

dátum ;)

Hodicska Gergely · 2006. Már. 30. (Cs), 11.08
Szia!


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ő
25

InternetExplorer probléma

haho · 2011. Ápr. 18. (H), 23.32
Hello!
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.
26

Melyik

Poetro · 2011. Ápr. 19. (K), 00.27
Melyik Internet Explorerben, melyik megoldás? Tudnál mutatni kódot?
27

Internet Explorer 9-ben

haho · 2011. Ápr. 19. (K), 13.07
Tehát a legújabb Explorerben nem akar működni (pontos verzió: 9.0.8112.16421).

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.
28

7 éves

Poetro · 2011. Ápr. 19. (K), 15.29
Gondolom, az nem riaszt vissza, hogy a cikk 7 éves. És természetesen nem tudom, hogyan néz ki a kódod, ugyanis IE esetében egyetlen kisebb hiba is az egész alkalmazás működését hiúsíthatja meg. Érdemes lenne tudni, milyen hibákat jelent az Internet Explorer.
Álljon itt egy kicsit frissebb XHR objektum létrehozó kód a jQuery 1.5 alapján:
var XmlHttp = {
  create: window.ActiveXObject ?
    /* Microsoft failed to properly
     * implement the XMLHttpRequest in IE7 (can't request local files),
     * so we use the ActiveXObject when it is available
     * Additionally XMLHttpRequest can be disabled in IE7/IE8 so
     * we need a fallback.
     */
    function() {
      if ( window.location.protocol !== "file:" ) {
        try {
          return new window.XMLHttpRequest();
        } catch( xhrError ) {}
      }
  
      try {
        return new window.ActiveXObject("Microsoft.XMLHTTP");
      } catch( activeError ) {
      }
      throw new Error("Your browser does not support XmlHttp objects");
    } :
    // For all other browsers, use the standard XMLHttpRequest object
    function() {
      return new window.XMLHttpRequest();
    }
}
29

Internet Explorer 9 hibák

haho · 2011. Ápr. 19. (K), 16.36
A 9-es Explorer már nem jelzi az állapotsoron a JavaScript hibákat. Hol tudom azokat megtekinteni vagy milyen kiegészítőt kéne letöltenem hozzá?

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é.
30

IE 9: F12 =>" firebug"

solkprog · 2011. Ápr. 19. (K), 16.58
F12 és fel ugrik egy firebug szerű valami, ott valahol ott lesz.

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.
31

Köszönöm

haho · 2011. Ápr. 19. (K), 17.06
Köszönöm a segítségedet.
32

Egyszerű hiba volt

haho · 2011. Ápr. 19. (K), 19.24
Egy alapvető hiba volt. A példaprogram client.html fájljában található JavaScriptben nem volt rendesen bevezetve két változó:
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!