AJAX fejlesztés - megjelenítés
AJAX fejlesztés - megjelenítés
Az első kérdés egyszerű: hogyan manipulálható az oldal HTML forrása JavaScriptből? Erre a válasz is egyszerű, van körülbelül három módszer: a
document.write()
, az .innerHTML
és a DOM segítségével (X)HTML felépítés, manipuláció. Az utóbbi kettő közelít egymáshoz, de azért közel sem ugyanaz.A
document.write()
segítségével nagyjából lineárisan, az oldal forrását bővítve tudjuk írni a HTML oldalt. Ennek a gyakorlati haszna egy webalkalmazás interfészének fejlesztésekor közelít a nullához, esetleg valamilyen trükkhöz használható fel, de most ilyen se nagyon jut eszembe. Ezen kívül az XHTML korában nem egy igazán javasolt módszerről van szó - ha szabvány szerint, XML-ként szolgáljuk ki az XHTML oldalt, akkor nem fog működni. De most úgyse tudjuk mire használni.A következő lehetőség egy adott oldalelem
.innerHTML
tulajdonságának manipulálása, még pontosabban egy adott HTML részlet az adott oldalelemhez rendelése. Kérdés, hogy ez a HTML hogyan áll elő, erről pár bekezdéssel később. Mindenekelőtt essék szó arról, hogy .innerHTML
tulajdonság nem szabványos. A Microsoft vezette be az Internet Explorer egy régebbi verziójában (ha minden igaz, már a 4.0-ban is használható volt) más, nem szabványos lehetőségekkel (mint az .outerHMTML
) együtt. A megoldást, mint egy elég jól használható lehetőséget, később átvette a többi böngésző is, így ma már minden elterjedt böngészőben használható (IE, Firefox, Opera, Safari), gyakorlatilag kvázi szabvánnyá vált. Ha XML-ként szolgáljuk ki XHTML oldalunkat (amit például Explorerrel nem lehet megoldani), akkor esetlegesen problémákba ütközhetünk, például a Firefox 1.0 szériája nem támogatta ilyenkor a használatát. Az 1.5 szériával már nincsen gond. Bár jelenleg nem szabványos a megoldás, a következő DOM szabványnak a WhatWG tevékenységének köszönhetően várhatóan része lesz.Végül az emlegetett három lehetőség közül a DOM maradt utoljára. Egy HTML szerkezet felépítése, majd az adott oldalrész lecserélése egy modern és "szép" lehetőség. "Szép", mivel ez az a megoldás, ami teljesen szabványosnak mondható, s szép, mert faszerkezetet (az oldal böngészőbeli reprezentációja kifejezetten az) nem feltétlenül sztringekkel a legkívánatosabb összeállítani.
A fenti lehetőségeket, pontosabban a megmaradt két lehetőséget súlyoztam praktikusság, sebesség és legutoljára a szépség szerint. Praktikusság alatt az átláthatóságot, a karbantarthatóságot értem, sebesség alatt azt, hogy a webalkalmazásban az igazán sebességigényes részek milyen gyorsak: ha az oldal kezdeti felépítése 500ms helyett 700ms-ig tart, nem igazán fontos, de ha az oldal folyamatos változtatása időigényes, az már igen.
Az
.innerHTML
esetén a praktikusság kérdése úgy merült fel, hogy hogyan állítható elő a beállítandó HTML részlet. Maga az értékadás egy eléggé egyszerű folyamat, hiszen az oldalon a lecserélendő elemet id
alapján gyorsan és könnyen meg lehet találni. A HTML előállítása JavaScriptből, sztringek összefűzésével a karbantarthatóság kapcsán felállított követelményeimnek nem felelt meg. Semmi kedvem nem volt ahhoz, hogy figyeljek az idézőjelezésre és az amúgy nagyon lassú konkatenáció megkerülésére. Az eredmény nem lett volna más, mint egy nem túl átlátható kódtömeg, az összeállított HTML+CSS kódrészlet pedig transzformációt igényelt volna. A felmentősereg a korábban már felderített JavaScript Templates (JST) volt a TrimPath-től, ami nem más, mint a szerver oldali nyelveknél megszokott sablonkezelő rendszer, ezúttal teljes mértékben JavaScriptben implementálva (egyébként a Smarty-ra hajaz a formátum). A lehetőségek listája elég szimpatikusra sikeredett (értelmes formában átadható változók, "gyorsítótárazható" sablonok), s nem utolsó sorban a kivitelezését sem egy amatőr végezte - próbáltam ugyan optimalizálni rajta, de semmit sem sikerült... Egy másik, de elvetett lehetőség a Perlben használatos Template Toolkit dinamikusan fejlesztett JavaScript implementációja, a Jemplate lett volna, de mivel ez előzetes transzformációt igényelt volna, illetve más apró problémám is volt vele, ezért végül mint lehetőség kizárásra került.A DOM-nál az adott kódrészlet előállítása - gondolom nem túl nagy a meglepetés - DOM függvényekkel történhet. Tömörnek semmiképpen sem nevezném ezt a módszert, eléggé szószátyár megoldás a
document.createElement()
, majd az .appendChild()
függvények használata. Bár a kódot fel lehet építeni áttekinthetően, maga a végeredmény egy több képernyőnyi sorminta is lehet, amit ember legyen a talpán, aki átlát. Egy viszonylag bonyolult HTML+CSS designterv alapján nem igen vállalkoznék rá, hogy megírjak egy ilyen programrészletet, itt esetleg az automatizálás segíthet. A jobb áttekinthetőségre persze van itt is megoldás, a DomBuilder nevű pár soros osztály egy tömör lehetőséget kínál (illetve vannak más, hasonló megoldások is). A programkód előállítása viszont nem lett egyszerűbb, itt is célszerű valamilyen előfeldolgozót igénybe venni, ha erre a megoldásra esik a választás.Mivel a webalkalmazásnál igen fontos szempont volt a sebesség is, ezért fontos volt megvizsgálni ebből a szempontból is a kérdést. Ebben egyértelműen az
.innerHTML
lett a győztes. A belinkelt mérés, és a saját méréseim is egyértelmű győztesnek hozták ki, viszont volt egy utalás arra, hogy a DOM gyorsabb tud lenni, ha klónozást használ az ember, és nem újra és újra előállítja a megfelelő HTML részletet. A kérdést megvizsgáltam közelebbről is. Ha adott egy sztring és adott egy már felépített HTML töredék, a nyertes körülbelül dupla sebességgel a HTML töredék DOM függvényeket használó hozzárendelése az oldal adott pontjához. A különbség persze nem sok, pár ezredmásodperc, de mint az korábbi hozzászólásomban is kiderült, adott esetben ez sokat számíthat. Ez alapján úgy hangzik, mintha a DOM lenne a nyerő, azonban a kérdésnek van egy másik fele is, mégpedig a megfelelő sztring, illetve a megfelelő HTML töredék előállítása. Nos, a sztring előállítása sokkal gyorsabban megoldható - annyival gyorsabban, hogy behozza a megjelenítésbeli különbséget, és az .innerHTML
megoldás nyeri a sebességben is a versenyt.A győztes tehát - ahogy a fenti leírásomból talán egyértelmű - az
.innerHTML
+ JST kombináció lett, s azt kell mondjam, hogy ezt a döntést nem is bántam meg a későbbiek folyamán. Azaz egy kis kiegészítést még kell, hogy tegyek: egy nagyobb oldalrészlet cseréje villogást eredményezhet, illetve a fókusz, kijelölések is elvesznek a csere alatt - így ahol szükség volt rá, egy kiegészítő megoldást bevezettem, mégpedig a közvetlen oldalmanipulációt, egy-egy konkrét elem cseréjét DOM-mal.Biztosan létezik még másik út is, melyen el lehet indulni, egy egyszerűbb oldalfelépítésnél például, ahol nincsenek összetett felületek, lehet, hogy valakinek a DOM megoldás fog bejönni. Vagy egyszerűen mindent kitesz, és utána csak a tartalmat változtatja, elrejt, megjelenít dolgokat. Ha az oldalon nincsen sok elem, akkor ez sebességben sem fog gondot okozni, hiszen egy 100 ms-os és egy 200 ms-os megoldás között a felhasználó nem nagyon tud különbséget tenni. A kérdés a skálázhatóság, azaz az, hogy az oldal bonyolultságának növekedésével sem fog-e sebesség probléma fellépni, de nagyon könnyen előfordulhat, hogy előre tudható: a felület végleges, s nem lesz nagy változás benne.
Végül egy zárógondolat: a webalkalmazásoknál - mint minden olyan alkalmazásnál, ahol van felhasználói interfész - nagyon fontos irányelv lehet az MVC design pattern használata: nem árt, ha az adatok megjelenítése elkülönül a program többi részétől. További, az asztali alkalmazásoknál jól bevált, de a webalkalmazásoknál nem használt technikák is hasznosak lehetnek, illetve nagyon fontos (de hát ez végülis mindenhol így van) a felhasználói élmény, például a felhasználói felület jól használhatóvá tétele. Vagyis ne csak elkészüljön az az alkalmazás, de legyen átlátható, használható, s épüljön a felhasználó által korábban megszokott felhasználói interfész elemekre.
az XML és XSLT
IE alatt ActiveX objektum támogatja, fx alatt pedig az XSLTProcessor, a többit nem ismerem, azaz
objOutput.innerHTML=xmlDoc.transformNode(xslDoc);
}
else{
var xsltProcessor = new XSLTProcessor();
xsltProcessor.importStylesheet(xslDoc);
var fragment =xsltProcessor.
transformToFragment(
xmlDoc,document);
objOutput.innerHTML = "";
objOutput.appendChild(fragment);
}
uff :)
naszóval
következzék a very_basic xml-xsl példa,
első a html szakasz
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:template match="/">
<table id="table1">
<tr>
<th align="left">NAME</th>
</tr>
<xsl:for-each select="persons/person">
<tr>
<td><xsl:value-of select="name"/></td>
</tr>
</xsl:for-each>
</table>
</xsl:template>
</xsl:stylesheet>
következne a css fájl, de azt majd máskor...
konkluzióféle:
ez egy nagyon egyszerű példa ami főleg a megvalósításra koncentrál, ezért felmerülhet a kérdés mire jó az egész, miért nem generáljuk egyből a végső táblát szerveroldalon és megyünk haza, a kulcsszó a könnyű módosíthatóság. XSL-t nagyon egyszerűen lehet megváltoztani, nem okoznak problémát az idézőjelek, tag szintaxisok, attribútumok, eseménykezelők felrakása stb. mivel nagyon hasonló a formátuma a html fájlokéhoz, a dizájnernek sem jelent túl sok problémát a megértése.
Továbbá, csupán az xsl fájl cseréjével lehet témát, megjelenítés dimenziót, nyelvet stb. cserélni
http://toxin.hu/xml/names.html
http://toxin.hu/xml/xmlxslajax.ZIP
ui: hozzászólásomat az Ajax in Action c. könyv 12. fejezte : Live search using XSLT támogatta , a hejessírási hibáimat meg én :)
jó, jó, de... :)
Csak van egy apró probléma.
Mind az XML-t, mind pedig az XSLT-t utaztatni kell minden lekérésnél, ami nem kevés adatmennyiség. Így az AJAX elveszíti az egyik legnagyobb előnyét. Nevezetesen azt, hogy apró csomagokkal update-lni lehet az oldalt.
A ProtoJax fejlesztése során én is beleestem abba a hibába, hogy olvasható tag-eket alkalmaztam, aztán rájöttem, hogy egy izmosabb update-kor sokat számít az is, hogy "setcontent" helyett "sc" -nek hívom azt a tagot, amivel az "n" nevű div tartalmát lecserélem.
S itt már nem pár bájtról van szó, hanem adott esetben egész bonyolult XSLT-ről. Azt meg, ha memóriába el is tárolod, tekintve a böngészők memória-hisztijeit, sem bizonyul igazán jó megoldásnak.
re
toxin
XML + XSLT
Persze nem ez volt az egyedüli indok, nekem más okból sem lett volna jó: a kliens oldalon XML-t kellett volna előállítani, ha valamit meg szerettem volna jeleníteni. Mivel nem minden infó a szerver oldalról jön, s van jelentős feldolgozás is, ezért ez megint egy kényelmetlenség lett volna, az egyszerű JS adatszerkezet (literál) igen kényelmes, egyszerű és átlátható átadásához képest.
ActiveX
Merhogy az AJAX alapjául szolgáló xmlHTTPRequest object is ActiveX IE 6 alatt :D
Az XML önmagában jó, jól olvasható adatszerkezet, és relative kevés az overhead. Én ezért alkalmazom, lásd az itteni első hozzászólásomnál levé ProtoJax cuccot. :D
Jajj
eventek
onclick
tehát
"Simán működik"
Behavior.js
Kipróbáltam, működik.
event:selectors
Event.observe ?
Felhő
Villogás
Villogás probléma
helyesírás
AJAX - megjelenítés
Ami nekem bejött:
Prototype + Xajax. Írtam is egy kvázi framework-öt a kettő alapján.
Az oldalmanipuláció és az Ajax hívás alapvetően a prototype feladata, a framework másik fele, PHP, egyetlen osztály, ami előállítja a választ.
A kis "házifrémvörk" elsősorban a szabványos DOM manipulációt használja, kivéve, ahol kifejezetten "content" frissítés van, ahol is az innerHTML megoldást. (content alatt sosem új tag-ek beszúrását értem, hanem mindig szöveget, bár elvileg nem tiltja meg a framework a keresztbe használatot)
Akit érdekel 0.3-as verzió, még van vele meló:
http://members.chello.hu/sandor.fejer/protojax.rar
(hamarosan értelmesebb tárhelyre is felteszem)
Azokat a részeket, amelyeket gyakran használok, előre generálom, és max, ha indulásnál nincs rá szükség, elrejtem.
Én azért hagytam meg a PHP oldali template-ezést, mert az oldal nem AJAX-os része is ott van, így egy helyen tudom tartani a template-eket.
Jó
XJSON
hogy érthető legyen
http://toxin.hu/xml/pajax_xjson.zip
csak annyire módoltam hogy menjen (így ebédidő alatt), a PEAR-es JSON konvertbe most ne tessék belekötni :)
protojax_xjson.js ill. protojax_xjson.lib.php -vannak
kösszi ha ránézel :)
köszi
Én egyelőre nem foglalkoztam a JSON-nel, mert a core sem használja, magyarán még nem volt rá szükség.
Megnézem majd a cuccot, és ha stílusban is oké, akkor beépítem, és beírlak creditbe is :D
De ezt leghamarabb csak este.
szerver oldali előállítás
Én inkább a fenti megoldásra szavazok. Pl. xajax-szal ezt elég kényelmesen lehet használni (illetve majd a protoxajax-szal ;)). Ami fontos előnyét látom, hogy egy helyen vannak az oldalt generáló, valamint a majd később ezt manipuláló kódok. Ráadásul így nem szükséges egy újabb template kezelő használata.
Felhő
Két külön dolog
Ha viszont egy önálló webalkalmazás készül, ahol a szerver oldali rész az adatkezelésre szorítkozik csak, akkor sokkal logikusabb lehet, ha csak a kliens oldalon van sablonkezelés.
Van még egy igen fontos nézőpont. Vegyünk egy olyan alkalmazást, ahol másodpercenként több száz felhasználót kell kiszolgálnia a szervernek (egy látogatottabb oldalnál simán). Ilyenkor a szerver oldali részt a lehető legminimálisabbra kell szorítani, a lehető legegyszerűbb lekérdezések történhetnek csak meg, és minél inkább koncentráltan. Nem azért, mert hogy kliens oldalon gyorsabb lenne mondjuk egy sablonkezelés (nem az), sokkal inkább azért, mert nem mindegy, hogy 1000 számítógép dolgozik a feladaton, vagy 1 db.
Egyetértés meg minden
Elkövettem azt a hibát, hogy a smarty-n átengedem a sorokat, mondván, template-zhető lesz minden.
Igen ám, csak éppen megfeledkeztem a "compile" funkcióról.
Reggel töröltem le 200.000 db compile fájlt a szerverről. :D
(Inkrementális server-polling sor lekérés van benne per user megvalósítva.)
Az AJAX-szal bonyolódik a struktúra, jobban oda kell figyelni az elsőre aprónak tűnő részletekre is. :)
lásd 24
Felhő
nem meta
Mondjuk nagy probléma nem lett belőle azon túl, hogy, amikor megnyitottam a compile könyvtárat mc-ben, felszökött kicsit a load a szerveren. :D
Nem, kifejezetten nem BBCode-ra volt szükségem, hanem az egyes (eltérő tipusú) sorok template-ezhetőségére.
Pl: más a felépítése egy normál chatsornak
normál sor:
<timestamp> [<usernév] <szöveg>
privát sor:
<timestamp> [<usernév]>[címzettuser] <szöveg>
kilép/belép notify:
*** <usernév> <szöveg>
stb.
Ennek kezelésére vezettem be a smartyt, mert kényelmesnek tűnt. Nos az, de így nem oké, ráadásul ebben az esetben nincs is szükség a compiled file letárolására, ezt pedig a smarty tudomásom szerint mindig megteszi.
Úgyhogy asszem' írok rá saját "parser"-t, és marad template-ben. :)
compile
Igen, generálja a fájlt, és ob_xy függvények között includolva szerzi meg a kimenetét, szóval ezt nehezen lehetne iszedni belőle.
Felhő
nem fekete-fehér
Egy webalkalmazásnál is lehet igény arra, hogy néha teljesen frissüljön az oldal, szóval nem érdemes abszolút kategóriákban beszélni.
Mi köze van a lekérdezéseknek a template kezelőhöz?
Amúgy meg nyilván mérlegelni kell adott esetekben, de ha a peremfeltételek nem követlik meg speciális megoldásokat, akkor érdemesebb a kényelmesebb megoldásokat használni, plusz törekedni a minél könnyebben módosítható program struktúra kialakításra.
Felhő
Olvass
A szerver oldali rész minimálisra szorításáról írtam. Azt mondtam, hogy a lehető legminimálisabbra kell leszorítani a szerver oldali munkát, és csak a legalapvetőbb dolgok maradhatnak szerver oldalon, ha nem akarsz (feleslegesen) szerver farmot összerakni. Te is jól tudod, hogy másodpercenként több száz felhasználót kiszolgálni csak egy igen jól optimalizált szerver környezettel lehet. A lekérdezéseknek a sablonkezeléshez van közük, de az a lényeg, hogy az általános szerver oldali feladatok közül kb. csak az adatbázis lekérdezés (vagy az adatok máshonnan összeszedése) maradhat meg (és nem soroltam fel, hogy a többek között a sablonkezelés NEM maradhat, mert felesleges erőforráspocsékolás).
Még valami. A böngészőoldali sablonkezelés pont ugyanolyan kényelmes, mint a szerver oldali, tehát ez szerintem nem szempont, amikor a kényelmességet vetjük össze.
A cikkben is leírtam, hogy mérlegelni kell, ez a véleményem nem változott azóta sem, tehát egyetértünk.
hasonló jellegű problémakör
Vázlatokban annyit kell tudni róla, hogy AJAX alapon nyitvatartott XMLHTTPRequest csatornákon keresztül "comet"-tel frissítgeti az oldalt, így kialakítva egy erősen interaktív adminisztrációs felületet. A terhelés nagy, másodpercenként több firssítési csomag is érkezik több felhasználóhoz kvázi szimultán módon, ráadásul a felhasználók által kiváltott események párhuzamosan megnyitott csatornákon kerülnek a szerverhez lekezelésre.
A cikkedhez Felhő vezetett, mert a terhelés miatt felmerült az igény, hogy az eddigi xajax-os .innerHTML = 'hosszú HTML string amit egy jó nagy smarty template generált' megoldást lecseréljem egy kliens oldali temlate-ező cuccosra. Egyrészt a terhelést szeretném levenni a szerver oldalról, másrészt sokszor kell apró adatrészleteket manipulálnom az oldalon anélkül hogy az említett 'focus' problémába vagy nagyon hasonlóba ütköznék. Sokszor kell a felhasználónak egy-egy adott műveletet megismételnie mert közben "kihúztam" alóla az adott HTML részt.
Meg fogom vizsgálni a direkt DOM manipuláció lehetőségét, bár nulladik megközelítésre is azt tippelem, hogy mindenképp egy hibrid .innerHTML/.createElement()+.addChild() megoldást kéne alkalmaznom. Valójában most is ilyen alapon működik a rendszer, a kérdés most csak a template-ező kliens oldalra helyezése körül forog.
Rövid google-zás után is talál az ember tucatni js alapú template-zőt. Meg fogom őket vizsgálni, hogy a generálás után konkrétan milyen beszúrási technikát használnak és a tapasztalataimat mindenképp megosztom. Szeretnék egy Prototype alapút találni, mivel az már eleve szolgáltatja nekem az Insertion osztályt.
Az is felmerült bennem, hogy ha találok használható készletet, vagy írok egyet, akkor azt igyekszem úgy csinálni, hogy modulként kiegészíthesse Amon protojax készletét. így azt hiszem elég póverful kliens oldali motort kapnánk.
Amon: beleépítetted már a protojax-ba a scriptaculous támogatást? :)
Arra várok már egy ideje. Tudom rakjam bele és szóljak ha megvan...
rage
comet
A "comet" jól hangzik, viszont a nyitva tartott socket elég nagy árnak tűnik. Tudom, hogy nem szorosan web téma (bár szerintem az), de tud valaki valami hack-ről, ahogy a 64ezer helyett sokkal több (TCP) soketet lehet kezelni (lehetőleg FreeBSDhez)?