
SPA přístup pro obsahové weby
Jak vytvořit obsahový web s dynamickým načítání obsahu bez obnovení celé stránky.
Pro zlepšení uživatelského dojmu z webu může posloužit moderní přístup k načítání obsahu bez obnovení celé stránky.
Díky dynamickému načítání obsahu může být přechod mezi stránkami rychlejší a plynulejší.
- Stačí přenášet jen obsah, který se mění.
- Překreslovat se bude jen část stránky, která se změnila. Namísto kompletního vykreslení stránky od nuly.
Na prvním bodu většinou ani moc nezáleží – velikost výsledných HTML kódů se obvykle pohybují v desítkách kilobytů, což příliš velký prostor pro úsporu nepřináší. Překreslování jen změněné části ale většinou velmi znatelně přechod po webu zrychlí.
Jak na to
Dříve se tato metoda nazývala jako AJAX (ajaxová stránka apod.), někdy také jako PJAX (pushState + AJAX). Dnes se tomu říká nejčastěji SPA – single page aplikace.
Nicméně nic nebrání udělat celý obsahový web ve stylu SPA.
Fetch API
Nativní JavaScript API, které nahradilo starý XMLHttpRequest
. Umožňuje čistou práci s HTTP požadavky pomocí Promise
objektů a async/await
syntaxe.
History API
Zásadní věc je podpora změny URL JavaScriptem pomocí history.pushState
– dnes je to podporováno všemi moderními prohlížeči včetně mobilních.
To umožní i při obsloužení změny obsahu dynamicky změnit URL do podoby, která se může načíst i bez JS. Bez této funkce bych se do obsahového webu s dynamickým načítáním nepouštěl.
SPA vs. SSR přístup
Dnešní weby často používají hybridní přístup - Server-Side Rendering pro první načtení a Client-Side Routing pro další navigaci. To kombinuje výhody rychlého prvního načtení s plynulou navigací.
Signalisace načítání
Je dobré znázornit, že se po kliknutí na odkaz něco děje. Při klasickém přecházení mezi stránkami to automaticky řeší prohlížeč. Při dynamické změně obsahu to musíme řešit vlastní signalisací.
Znázorňovat načítání nemusí být dobré ihned po kliknutí. Pokud web funguje normálně, mělo by načtení obsahu být tak rychlé (desítky až nízké stovky milisekund), že by znázornění načítání stejně jen probliklo.
Uživatelé bývají zvyklí, že se po kliknutí na odkaz chvíli nic neděje, nabízí se tedy načítání znázorňovat třeba až 0,5 vteřiny od vyvolání akce.
Moderní přístupy používají skeleton loadery nebo progressive enhancement pro lepší UX.
Přednačítání a optimalisace
Zrychlit dojem z webu může ještě přednačítání. Celý proces změny obsahu rozložíme do dvou kroků.
- stažení/zpracování obsahu,
- zobrazení obsahu
Má to svoje výhody i nevýhody. Umožní to rychlejší změnu obsahu, ale může plýtvat daty (načte se něco, co uživatel nakonec nebude chtít zobrazit).
Rozumné může být přednačtení při najetí myší (onmouseover
) nebo stisknutí tlačítka (onmousedown
). Zvlášť při stisknutí tlačítka myši nad odkazem je vysoká šance, že uživatel dokončí kliknutí (neodjede myší pryč) a můžeme tak získat třeba 100 milisekund, které běžné kliknutí trvá (stisknutí a uvolnění tlačítka). Tedy v době dokončení kliknutí už mít načteno.
Moderní prohlížeče také podporují Intersection Observer API
, které umožňuje efektivněji detekovat, kdy se odkazy dostanou do viewportu a případně je přednačítat.
Hotová řešení
Dnešní populární JS frameworky toto chování podporují, aniž by se autor webu musel nějak snažit.
- Next.js – React framework s hybridním SSR/SPA přístupem
- Nuxt.js – Vue.js framework s podobnými možnostmi
- SvelteKit – moderní Svelte framework s univerzálním renderingem
Implementace v JavaScriptu
Zde je příklad, jak implementovat dynamické načítání obsahu pomocí Fetch API.
Minimální implementace v čistém JS může vypadat následovně:
async function loadContent(url) {
try {
// Načtení HTML obsahu z URL pomocí Fetch API
const response = await fetch(url);
// Extrakce textového obsahu z response
const html = await response.text();
// Vložení načteného HTML do elementu s ID 'content'
document.getElementById('content').innerHTML = html;
// Aktualizace URL v prohlížeči bez obnovení stránky
history.pushState({}, '', url);
} catch (error) {
// Ošetření chyby při načítání
console.error('Chyba při načítání obsahu:', error);
}
}
// Poslouchání kliknutí na všechny odkazy
document.addEventListener('click', (e) => {
// Kontrola, zda kliknutý element je odkaz začínající na "/"
if (e.target.matches('a[href^="/"]')) {
// Zabránění výchozímu chování (obnovení stránky)
e.preventDefault();
// Volání funkce pro načtení obsahu
loadContent(e.target.href);
}
});
Co je potřeba vyřešit pro reálnou implementaci
Výše uvedený kód je pouze základní ukázkou. Pro produkční nasazení je potřeba vyřešit:
- Titulek stránky – měnit
<title>
webu, mít správné meta tagy, Open Graph, strukturovaná data - Tlačítka vpřed/zpět – obsluha navigace v prohlížeči
- Připojování/odpojování JS – pokud stránky používají JS, musí se řešit init a destroy po změně navigace
- Odesílání formulářů – další věc je odesílání formulářů JavaScriptem
- Loading stavy – indikátory načítání, skeleton loadery
- Error handling – mít nějaký fallback při selhání načítání
- Analytické nástroje – sledování page views a uživatelského chování funguje u SPA trochu jinak
Proto se v praxi často používají osvědčené frameworky jako Next.js, Nuxt.js nebo SvelteKit, které tyto problémy řeší out-of-the-box.
Nevýhody SPA přístupu
I když má SPA přístup mnoho výhod, má také své nevýhody, které je potřeba zvážit:
Zvýšená komplexita
Implementace SPA funkcionality vyžaduje řešení mnoha problémů, které klasické weby řeší automaticky:
- State management – správa stavu aplikace mezi navigacemi
- Memory leaks – risiko úniku paměti při nesprávném odstraňování event listenerů
- Event handling – správné připojování/odpojování JavaScript funkcí
- Form handling – odesílání formulářů bez obnovení stránky
Každá chyba může být fatální.
Výhoda klasického přístupu je, že i když web používá JavaScript, každá změna navigace reálně znamená restart aplikace. SPA může hnít v prohlížeči klidně měsíce.
Výkon
SPA přístup má své nevýhody i v oblasti výkonu – zejména první načtení, které je pomalejší kvůli stahování obslužného JS.
Přístupnost
Docílit dobré přístupnosti může být v SPA složitější:
- Čtečky obrazovky – mohou mít problémy s dynamicky měnícím se obsahem
- Ovládání klávesnicí – focus management je složitější
Složitější debugging
Vývoj a údržba SPA může být náročnější, protože mohou existovat chyby, které se projeví třeba až při načtení více stránek v určitém pořadí, což se špatně odhaluje, popisuje a reprodukuje.
Závěr
Doporučuji pečlivě zvážit, zda SPA přístup skutečně přináší hodnotu pro konkrétní projekt, nebo zda není lepší zůstat u klasického server-side renderingu.
Zajímavým příkladem je třeba Astro, které schválně používá plnohodnotnou navigaci mezi stránkami. Pokud je web rychlý sám o sobě, těžko jde reálně poznat rozdíl.
Potřebnou JS funkcionalitu jde realizovat přes tzv. Islands architecture, kdy se na potřebná místa na stránce doplňují JS komponenty, které ale neblokují vykreslování hlavního obsahu.
Pro SPA chování na obsahové stránce bych tak hledal opravdu dobrý důvod. Kdy má tedy SPA přístup skutečně smysl? Napadají mě následující případy:
- Real-time funkcionalita – chat aplikace, live updates, notifikace, přehrávání videa nad samotnou stránkou – klasická změna navigace by přerušila zážitek
- Zabudované ve frameworku – pokud SPA chování framework automaticky podporuje, asi není potřeba to za každou cenu vypínat
Klíčové je neřešit technické problémy, které neexistují. Pokud klasický web funguje dobře a uživatelé si nestěžují, není důvod přidávat složitost.
Související články


JavaScript null
a undefined
Rozdíly mezi null
a undefined
v JavaScriptu, kdy je používat a jak se vyhnout běžným chybám.

Sleep v JavaScriptu
Jak implementovat sleep/delay funkcionalitu v JavaScriptu pomocí Promise
a async
/await

JavaScript Battery API
Jak v JS zjistit stav baterie, co dnes funguje a kdy API nepoužívat.