Dynamické načítání obsahu do HTML z aplikace přes JavaScript

18.02.2005 22:23

Obecné zamyšlení

V dnešní době je moderní měnit obsah stránky pomocí JavaScriptu. Skutečně první významná vlaštovka v tomto moderním trendu byl Google v podobě svého Gmailu. U nás se Gmailem se inspiroval portál Centrum, který v současné době vyvíjí svůj nový email, který je celý založen na JavaScriptu. Dále např. portál Seznam v lednu nasadil své Poznámky, což je velmi jednoduchá “JavaScript only” aplikace.

Ze své vlastní zkušenosti se domnívám, že vytvářet aplikaci, která celá stojí na JavaScriptu je cesta do pekel. JavaScript jako takový vyžaduje velkou režii na vývoj i následnou správu a údržbu. Navíc prohlížeče jsou v současné době značně nepředvídatelné a nikdy si webmaster (programátor) nemůže být jist, že daná funkce bude u klienta fungovat.

U JavaScriptových webovek (aplikací) existuje ještě jedno veliké nebezpečí - nefunkční tlačítko Zpět a Vpřed. Pro profesionála to je pochopitelné, jste pořád na jedné stránce, ale běžný uživatel neví co je to JavaScript, aplikace, stránka, ba ani prohlížeč. Tudíž když klikne na nějaký odkaz, na kterém nenajde co hledal, okamžitě a zcela automaticky klikne na tlačíko Zpět. A zde nastává zásadní problém - uživatel je úplně někde jinde, než si myslel. Zde myslím stojí za zamyšlení, jak se takový uživatel asi bude chovat např. v GigaCentru.

Proto se domnívám, že je dobré jít zlatou střední cestou, kdy JavaScript doplňuje plně funkční webovou aplikaci a přiměřeně urychluje nebo usnadňuje práci s ní. Slovy klasika: “JavaScript je dobrý sluha, ale zlý pán“.

Dynamicky vytvořený <script> tag

Rychle zpět k tématu ukázat jednoduchý způsob, jak lze do stránky dostat data z aplikace bez nutnosti znovu načtení stránky.

Máme několik řešení, pro představu:

  1. Načítat data do stránky v XML pomocí interních metod prohlížeče
    Naráží na nutnou podporu práce s XML v prohlížeči a na velký přenos “zbytečných” dat (XML formát). Toto řešení je poměrně omezující a nejisté. Mimochodem přenos dat v XML s oblibou používá Google, čímž automaticky eliminuje některé prohlížeče.
  2. Načítat JavaScriptový kód přes iframe
    Toto řešení je velmi snadné, funkční a podpora iframe v prohlížeči není problém. Jediná nevýhoda spočívá v přenosu zbytečných dat v podobě definice HTML stránky.
  3. Načítat JavaScriptový kód přes dynamicky vytvořený script tag
    Ideální způsob, odstraňuje všechny nevýhody předchozích dvou řešení.

XML-RPC bez XML v JavaScriptu

Pomocí dynamicky vytvořeného script tagu vlastně vytvoříme vlastní XML-RPC (Remote Procedure Call), bez XML.
Budeme volat skript na serveru, který bude “odpovídat” pouze JavaScriptovým kódem, který zpracují funkce implementované ve stránce.

Ukázka

Chcete vidět, jak to vlastně funguje? Klikněte a uvidíte :)

Kompletní zdroj jednoduchého příkladu:


<script type="text/javascript">
var vystup = document.getElementById('vystup');
	
function stahniData(akce) {
	
	// odmazeme stary skript, pokud existoval
	var hlavicka = document.getElementsByTagName('head')[0];
	var dataLoader = document.getElementById('scriptLoader');
	if(dataLoader) hlavicka.removeChild(dataLoader);
	
	// vytvorime novy element script
	script = document.createElement('script');
	script.id = 'scriptLoader';
	script.src = 'javascript-rpc.php?akce='+akce+'&r='+Math.random();
	
	// POZOR, DULEZITE!! Skript musime vlozit do stranky
	// pomoci DOM - appendChild()
	// Ihned po vlozeni prohlizec stahne skript a spusti jej
	x = document.getElementsByTagName('head')[0];
	x.appendChild(script);
	
	return false;
}
	
function vypisText(txt) {
	// nejsnazsi cesta, vlozime novy kod ke staremu na konec
	vystup.innerHTML += txt;
}
	
function chyba(txt) {
	// obycejny alert s hlaskou
	alert(txt);
	return true;
}
	
function vymazData() {
	// v jednoduchem cyklu smazeme vsechny elementy v prvku vystup
	while(vystup.hasChildNodes()) vystup.removeChild(vystup.childNodes[0]);
	return false;
}
	
stahniData('dolly');
</script>

Celé kouzlo je pouze ve správném využití DOM a především funkce createElement(). Doporučuji prostudovat příklad na samotatné stránce.

Tento způsob načítání dat funguje spolehlivě ve všech verzích IE, Gecko a Opeře. Jediné problémy jsou v prohlížečích na jádře KHTML (Konqueror, Safari), kde nedojde ke stažení dynamicky vytvořeného elementu script.

Komentáře k příspěvku 'Dynamické načítání obsahu do HTML z aplikace přes JavaScript'

  1. Leo:

    Hezky, pouzivam to uz nejakou dobu pro chat. Skoda tech KHTML, ale ted me napadlo / zkousel jste to se skriptem v body misto v head? Leos Ondra

  2. Chose:

    2 leo:
    Chova se to uplne stejne … bezne to pouzivam prave v body, ted jsem to dal pro jednoduchost do headu.

  3. Leo:

    Tak to bude asi neprustrelny, skoda. Leo

  4. Daniel.Peder@Infoset.cz:

    a zkousel jste document.write namísto createElement() ?
    svého času jsem také experimentoval s FRAME (nikoliv IFRAME) kde výsledná stránka byla rozdělena na viditelný a neviditelný FRAME. Neviditelný FRAME měl nastaveno čimž byla naharazeno to dunamické generováni …. Další variace je možno vymyslet i IFRAME …

  5. Daniel.Peder@Infoset.cz:

    a zkousel jste document.write namísto createElement() ?
    svého času jsem také experimentoval s FRAME (nikoliv IFRAME) kde výsledná stránka byla rozdělena na viditelný a neviditelný FRAME. Neviditelný FRAME měl nastaveno <meta http-equiv=”Refresh” content=”2;url=http://www.novyden.cz/?dnes.cz”> čimž byla naharazeno to dunamické generováni …. Další variace je možno vymyslet i IFRAME …

  6. Chose:

    2 Daniel.Peder@Infoset.cz:
    Document.write nelze pouzit, protoze dana stranka uz je nactena a doslo by k jejimu “prepsani”.

    IFRAME je samozrejme mozne pouzit, ale iframe musi obsahovat HTML stranku a to jsem nechtel. Chtel jsem vytvorit vlastni RPC “protokol”.

    Rozhodne se neda ale pouzivat meta refresh. Stranka se zmeni na pozadavek uzivatele, ne pravidelne automaticky.

  7. Yuhů:

    Napsal jsem ti sem dvoustránkovej komentář, ale nepochopil jsem, že hvězdička znamená povinnou položku. Takže jestli to šlo getem, tak můj komentář máš v logu. A jestli to šlo postem, tak smůla.

  8. Daniel.Peder@Infoset.cz:

    2 Chose [21.02.2005 v 15:35]:

    IFRAME, FRAME a META-REFRESH - asi jsem se špatně vyjádřil - tyto metody nijak nekolidují s vytvořením vlastního RPC, protože uvnitř IFRAME není generován vlastní obsah! IFRAM je použito pouze jako jakási mezivrstva pro předání RPC dat.

    V IFRAME - nejlépe neviditelném - si např. vytvoříte formulář, do kterého zvenku cpete javascriptem obsah a pk ho javascriptem - na požádání uživatele odešlete. Jakmile se v IFRAME objeví výsledek (toho odeslání formuláře) tak to opět javascriptem přečtete a výsledky naservírujee do viditelné části uživatelova okna.

    Když vám nevyhovuje použití formuláře, použijte třeba &ltscript&gt kontejner, jak píšete ve svém článku.

    ale to nejlepší v dalším příspěvku ;)

  9. Daniel.Peder@Infoset.cz:

    za nejelegantnější řešení které jsem v tomto směru vymyslel je využití cookies pro RPC.

    postup je triviální (píšu to z halvy a zjednodušeně, takže sledujte schema a nebijte mě za syntaxi apod..):

    while(1) // smyčka RPC komunikace

    var RPC_IMG = new Image( url-of-serverside-handler?param1=val1&param2=val2&….)

    /*
    na straně serveru se parsují parametry požadavku na obrázek, a výsledná odpověď se vloží do cookie společně s obrázkem ( obrázek může být 0bytes, to nevadí, protože hlavičky budou předány a zpracovány - testováno na IE OPERA Moz)
    */

    var RPC_RESPONSE = RPC_PARSE_RESPONSE(document.cookies)

    endwhile

    /*

    geniální, ne?

    (C) Daniel Péder ;) )

    */

  10. Daniel.Peder@Infoset.cz:

    užití cookies má sice drobnou nevýhodu v limitovaném objemu dat, ale to se v případě nutnosti dá řešit chunkováním.

    PS: v předcházejícím příspěvku došlo ke zkomolení řádku protže si váš skript usmyslel, že text mezi potržítky bude považovat za italic :-(

  11. Tomáš Hojgr:

    Zajímal by mě Yuhův komentář, který se mu ztratil při odesílání tohoto formuláře. Zkuste to z něj nějak vydolovat, určitě má k tématu mnoho co říct.

    Jinak poslední dobou často řešíme požadavky klientů na desktop aplikaci a pracně jim vysvětlujeme, že aplikace na webu je lepší proto a proto, ale že bohužel musejí počítat s reloady. A postupem v tomhle článku bvychom se reloadů zbavili, takže super. A většina našich DB aplikací může jít výhradně na web. To by byla parááááda.

    Máte někdo praktickou zkušenost s použitím pro aplikace, kde je potřeba najednou zobrazit třeba 1MB dat ? Nepadá to ? Jak se udržuje taková aplikace ve srovnání s klasickým PHP/HTML/MySQL(s JS jen jako doplnkem) ???

  12. Chose:

    2 Yuhu:
    Bohuzel to jde postem :( Navic ukladani komentaru ma WordPress pomerne spatne udelane - zahodi cely napsany text. Asi sem dopnim kontrolu javascriptem, v PHPku tohoto systemu bych se nerad hrabal.

    2 Daniel.Peder@Infoset.cz:
    IFRAME - aha, uz chapu … to je pomerne zajimave reseni …

    RPC V COOKIES - to se mi zda trosku omezujici a jako zneuziti technologie k jinemu ucelu, nez ke kteremu je navrzena :) Uz kvuli max velikosti (tusim 4 KB) a kvuli nutne podpore na strane klienta je to myslim slepa cesta. To IFRAME reseni je ale pouzitelne bez problemu.

  13. Chose:

    2 Tomáš Hojgr:
    Toto reseni by melo byt o rozumnych velikostech. Tahat v javascriptu stovky KB neni efektivni (pokud neni rychla linka), protoze prohlizec zacne vykonavat skript az kdyz ho ma kompletni!

    Pokud by se stahovalo 1 MB dat, je vyhodnejsi to vypsat primo do stranky, ktera se zobrazuje postupne, nez to tahat javascriptem.

    Ja tento princip vyuzivam napr. v “kalendari”, kde po najeti kurzoru nad policko se ukaze pozadova informace, ktera se stahne pres javascript.

    Udrzovani neni vyrazny problem, pokud je dany skript napsany prehledne (vyuziti strukturovani v javascriptu) a dostatecne univerzalne. V kazdem pripade to je dalsi vrstva za aplikaci, html a css, coz samozrejme praci zkomplikuje.

  14. Daniel.Peder@Infoset.cz:

    [Chose 22.02.2005 v 20:07 ]

    zneužití technologie? :-) no já myslím, že ani IFRAME nebylo zrovna určeno pro budování RPC.

    A podpora cookies v klientovi? No řekl bych, že cookies jsou podporovány více klienty než IFRAME ;-) - Dokonce bych se nebál říci, že v případě kombinace s javascriptem je to 100%. Tj. klient podporující javasript podporuje i cookies.

    Nepotřebujete permanentní cookies, úplně na to stačí běžné temp cookies. Ohromnou výhodu navíc vidím v minimalizaci přenosu dat a nulové potřebě renderování HTML.

  15. Chose:

    [Daniel.Peder@Infoset.cz]
    To mate pravdu, ale rozhodne ma k tomu bliz, nez cookies.

    Zde si dovolim oponovat - nemyslim, ze kdyz je u klienta javascript, tak ma automaticky i cookies. Z osobni zkusenosti (i z praxe) se setkavam s pripady, kdy uzivatel je schovan napr. za firemni proxy, ktera z pozadavku automaticky zahazuje cookies. Ovsem javascript normalne funguje, navic se tvari, ze i cookies uklada. Overeno z praxe :)

  16. Daniel.Peder@Infoset.cz:

    [2 Chose]

    ještě jsem si vzpomněl na jedno fintu (už je to doba co jsem se tím zabýval) která by se vám mohla hodit do varianty transport dat v kontejneru javascript:

    pokud elementu script s atributem src změníte pouze atribut src, dojde k reloadu celého skriptu - tj. nemusíte ho pracně odmazávat a znovu vytvářet a snižuje se riziko kolize browseru.

    to jsem asi měl na mysli když jsem psal o document.write namísto getElementBy…etc…

    ono totiž stačí při prvním průběhu ještě během načítání stránky za pomoci document.write vygenerovat “personalizovaný” script tag — nezapomeňte mu přidělit ID — a potom mu už jen měnit atribut src=

  17. Chose:

    [2 Daniel.Peder@Infoset.cz ]
    Rozumim … otazka je, zda v tomto pripade dojde opravdu ke stazeni skriptu. To se da ovsem pomerne snadno zkusit :)

  18. Daniel.Peder@Infoset.cz:

    každopádně používat JS pro RPC je stejně vhodné spíše pro aplikace, u kterým si předem můžete zajistit, že uživatelé budou mít ten správný browser. Nejlépe tedy na intranetech, neveřejných částech CMS nebo u služeb s placeným přístupem (kde předem uvedete co uživatel musí mít pro přístup). Ale rozhodně ne na veřejné web pezentaci.

  19. Chose:

    Ja bych rekl, ze toto lze pouzivat bezproblemu kdekoliv, kde se to hodi, tzn i na verejnych webech.

    No a v CMS, intranetech atd. lze tato “featura” pouzivat ve vetsim nasazeni nez na webu, ale neomezoval bych se jen na tento segment.

  20. Daniel.Peder@Infoset.cz:

    [2 Chose:25.02.2005 v 08:44 ]

    v tom případě doporučuji přečíst nasledující spot a související odkazy i diskuzi
    Metodika měření Javascriptu na Navrcholu stále neznámá [Yuhůů]

  21. Patrik Dvořák:

    Vámi popisovaná metoda je opravdu příjemně jednoduchá. Dynamicky generovaný script tag je elegantním řešením pro načínání obsahu do stránek. Její nevýhodu však vidím v opačném směru komunikace, a to v posílání dat od uživatele na server. Tato metoda umožní posílání dat pouze ve formě parametrů v adresním řádku. Rozsáhlejší data či http upload se nám touto metodou na server dostat nepodaří.

    Před časem jsem se rozhodoval jakou metodu zvolit pro svou aplikaci a mým nástrojem se stal IFRAME. Ten mi umožní na server směrovat formuláře metodou POST a vyhnout se tak i ošetření uživatelských vstupů před vložením do URL.

    Postupoval jsem následujícím způsobem: V zobrazovací části jsem pomocí innerHTML vygeneroval formulář v požadované podobě, který měl target nastaven do IFRAME. Cílem formuláře byl PHP script, který vstup zpracoval a vygeneroval jednoduchou (nevalidní) HTML stránku (stačí html a body), která obsahovala data v javascriptu a volání funkce parent.mojeFunkce(data). Tato funkce, která se nacházi již zpět v hlavní stránce, zajistí strávné zobrazení načtených dat.

    Jediné omezení pro webdesignera je v tom, že je třeba použít XHTML Transitional, jelikož v striktním je IFRAME ze specifikace odstraněn.

    Problém s tlačítkem zpět v prohlížeči mě také trápí, ale i přes to předpokládám, že podobných aplikací bude přibývat.

  22. Chose:

    [2 Patrik Dvořák]
    Diky za hodnotny prispevek. Mate pravdu, IFRAME je zrejme nejuniverzalnejsi a nejspolehlivejsi zpusob prenosu dat v obou smerech.

    HTML a BODY se urcite prezit da a navic je to spolehlive ve velke vetsine browseru, takze je to myslim jasne.

    Take predpokladam, ze podobne aplikace budou pribyvat, ale jsem zastance, aby podobne stranky nevypadaly jako web, nybr jako aplikace.

  23. llook:

    prohlížeče jsou v současné době značně nepředvídatelné

    Nejde jenom o prohlížeče. Nedávno jsem se z jednoho počítače nemohl dostat na Gmail ani z IE6 ani z O8. Pak jsem přišel na to, že když v KPF zruším blokování pop-upů, tak to jde. Ale kdybych byl za nějakým podobně chytrým síťovým firewallem…

  24. Martin Straka:

    Trochu bych se přel o prvenství GMailu, třeba inet bankovnictví eBanky je javascript aplikace a je zde o hodně let déle:)

  25. Jiri Maly:

    Zrovna pred casem jsem psal o teto stale aktivneji pouzivane technice jeden kratky prispevek. Ono se z funkcniho i interakcniho hlediska s vyuzitim asychronniho javascriptu a XML nabizi relativne hodne moznosti a skutecne smysluplnych reseni se zcela urcite jeste objevi hodne. Jenom je dulezite, aby autori nezapominali na elementarni principy webu. V tomto ohledu treba takove mapy na atlasu jsou aplikaci skutecne inteligentni (cti efektivni), kdezto gmail spise efektni.

  26. Petr Krontorad:

    Jen update (kdyz uz je nove odkaz z Rootu;). Safari 2.0 uz appendovani script elementu umi, takze lze pouzit. httprequest objekt byl implementovan uz mnohem drive, hodne podobne gecko api (v podstate projdou mozilla testy).

  27. Chose:

    2 Petr Krontorad:
    Aha, diky za info

  28. -lukas-:

    Diky za skvely clanek! Aplikoval jsem ho v jednom projektu, ale objevil se pomerne velky problem.
    Stranka se nacte a pote se periodicky (5 vteřin) v pohode odesila pozadavek na stazeni aktualnich dat (prez setInterval()). Jde o data tak cca 1kB.
    A v tom opakovanem odesilani je prave ten problem. Dlouhodobě bez problemu to jede pouze ve FireFoxu. V IE a obcas i v Opeře se přestane požadavek z ničeho nic odesílat. Vypadá to, že funkce, ktera odesila pozadavek se vzdy vykona, ale pozadavek se proste nepošle (zjištěno i programem Ethereal na sledování aktivity na TCP/IP).
    Přitom “odpovědi” přicházejí, ale zjistil jsem, že to jsou jen (náhodné?) hodnoty načtené dříve do cache IE. Stránka se totiž kupodivu bez problémů “obnovuje” i po odpojení PC od počítačové sítě - obnovuje se právě náhodnými historickými daty z cache.
    Mate nekdo napad, v cem by mohl byt problem?

  29. Josef:

    Ovšem takový vložený obsah bude nepřístupný pro roboty vyhledávačů, nemůže to být z jejich strany nějak penalizováno?

  30. Chose:

    2 Josef:
    To je pravda, ale penalizace bych se nebal. To by z principu musely byt penalizovany vsechny AJAXove weby.

    2 -lukas-:
    To je podivne … zkusil bych snad jedine pridat nejake random cislo do parametru nebo casovou znamku, aby jsi zajistil, ze se nebudou dotazy cachovat. Jinak by to melo fungovat porad bez problemu.

  31. -lukas-:

    To jsem zkoušel. Dospěl jsem dokonce do stadia, kdy jsem vkladal randomicke cislo (0-999) i aktualni datum a cas :-)
    Dobu, po jakou skript funguje to sice prodlouží, ale po nějaké (ne vždy stejné době) to stejně přestane fungovat.

  32. tomask:

    Děkuji za výborný článek a publikované knoh-how. Jenom jsem se chtěl zeptat, zda-li byste nemohl nastínit řešení, pokud je potřeba pracovat s daty od uživatele. Pokud takto dynamicky vložím nějaký INPUT. A formulář s takto vloženým inputem odešlu, tak jak se nejjednodušeji dostat k hodnotě tohoto inputu. Předem díky za jakékoliv našťouchnutí, vlastní důvtip bohužel nestačil v tomto případě :-(

  33. Tomáš Hráský:

    Výborný příspěvěk, srozumitelný a inspirující (tedy alespoň mě inspiroval).
    Setkal jsem se s jediným problémem, ne snad tak ani skriptu, jako spíš prohlížečů.

    Chci použít tuto metodu pro podmíněné načtení javascriptu a okamžité zavolání funkce v něm definované. Prohlížeče (FF a IE) mi ale tvrdí, že fce nebyla definována.

    Existuje způsob jak pozdržet zavolání oné funkce než se načtě celý JS a tím pádem bude fce definovaná?

    Děkuji

  34. pivko:

    pozvu na pivko toho, kdo mi napise kod, ktery kdyz napisu napr. www.neco.html?kw=klicove+slovo mi vrati html stranku jejimz obsahem budou vysledky naseptavace seznamu :o )

  35. RaĎim:

    To o čem tu píšeš jsem hledal asi týden. Takže je super, že to jde tak jednoduše. Chtěl jsem si to zkusit u sebe a tak jsem prostě zkopíroval ukázkový soubor na svůj hdd a nic. Nefunguje to. A nevím proč. Samozřejmě jsem se postaral o to, aby ten PHP soubor fungoval. Pomozte pls.

  36. spaze:

    Josef, 19.01.2006 v 23:08:
    Jak by mohl vyhledávač penalizovat něco, k čemu s nedostane? ;) Vždyť už samo o sobě to, že se k tomu obsahu nedostane a že jej tudíž nemůže zařadit do indexu a tudíž jej nemůže prohledávat a tudíž jej nemůže nabídnout v SERP je dostačující.

  37. spaze:

    pivko, 03.03.2006 v 19:48:

    http://www.seznam.cz/jsSuggestFulltext?dict=fulltext&phrase=foo&encoding=utf-8&response_encoding=utf-8

    jak jednoduché ;)

  38. JaffaMaster:

    super…tohle sem potřeboval..dělám webovou hru a tohle se mi přesně hodí… Tu funkci sem si upravil tak jak jsem potřeboval a nemůžu si to vynachválit…
    :)

  39. Petr:

    Myslite si že by zde popsaný příklad mohl být použit pro dynamické vložení HTML kódu do prvku jako je například DIV ??

  40. Chose:

    2 Petr:
    Jiste, k tomuto ucelu to lze bez problemu vyuzit. Jen je treba si vhodne upravit javascriptovy kod.

  41. Richard Daněk:

    Toto reseni je bezva jen pri pouziti setTimeout a opetovneho volani v tomto pripade stahniData() jiz IE vubec nezavola volany source tedy php script, nevite nekdo reseni?

  42. Richard Daněk:

    Uz jsem na to prisel resenim je dat do volaneho scriptu:

    header(”Expires: Mon, 26 Jul 1997 05:00:00 GMT”);
    header(”Last-Modified: ” . gmdate(”D, d M Y H:i:s”) . ” GMT”);
    header(”Content-type: application/x-javascript; charset=utf-8″);
    header(”Cache-Control: no-store, no-cache, must-revalidate”);
    header(”Cache-Control: post-check=0, pre-check=0″, false);
    header(”Pragma: no-cache”);

  43. Nikolay:

    Dalo by se toto reseni pouzit jestli namiste textu budu nahravat obrazky. Napriklad jestli mam webkameru ktera nahrava soubory FTPkem na serveru a abych nedelal pokazde refresh pri kterym se cela stranka nacte znovu. Jestli se da obdobnim zpusobem udelat preload obrazku a za nekolik vterin se na strance objevi plinule ne jako u refresh - skakat.

  44. ano:

    ano ?

  45. Chose:

    2 Nikolay:
    Ano, to jiste lze vyuzit pro aktualizaci obrazku. Staci pouze vzdy vymenit .src u obrazku :)

  46. Baklanka:

    Super tits:
    big natural tits . black tits . perfect tits . titty fucking . gigantic tits . big titties . tit torture . tits and ass . free big tits . massive tits . little tits . small tits . super huge tits . saggy tits . asian tits .