Lekce 6 - Datová úložiště v JavaScriptu
V předešlém cvičení, Řešené úlohy k 4.-5. lekci OOP v JavaScriptu, jsme si procvičili nabyté zkušenosti z předchozích lekcí.
V dnešním tutoriálu objektově orientovaného programování v JavaScriptu si představíme způsoby, jakými můžeme v JavaScriptu ukládat data tak, abychom je při obnovení stránky hned zas neztratili. Sice tím od samotného OOP lehce odbočíme, je však důležité se i v této problematice orientovat. Znalosti z dnešní lekce se nám mimo jiné budou hodit při tvorbě našeho diáře.

Příprava projektu
Pro účely zkoušení probírané látky si založíme jednoduchou HTML
stránku index.html
a ve složce js/
jeden skript
obsluha.js
, do kterého budeme psát náš kód. V souboru
index.html
budeme mít pouze základní HTML strukturu a
nalinkovaný skript obsluha.js
:
<!DOCTYPE html> <html lang="cs-cz"> <head> <meta charset="UTF-8"> <title>Úložiště</title> </head> <body> <script src="js/obsluha.js"></script> </body> </html>
Web storage
Web storage, také známý jako DOM Storage, představuje dva hlavní
mechanismy pro ukládání dat v prohlížeči: localStorage
a
sessionStorage
. Tyto mechanismy poskytují možnost uchovávat data
tak, aby byla dostupná i po obnovení stránky nebo případně i jejím
úplném zavření.
localStorage
localStorage
je úložiště v internetovém
prohlížeči uživatele (proto local), do něhož se mohou ukládat
data v textovém řetězci, ke kterým můžeme později
přistupovat. K dispozici máme pro naši aplikaci typicky 10 MB prostoru (tento
limit se může lišit v závislosti na konkrétním prohlížeči a
platformě), což je pro texty opravdu hodně Možnost ukládat jen text je
poměrně omezující, ale stejně je
localStorage
kvůli své
jednoduchosti nejoblíbenějším úložištěm dat, o která nechceme po
obnovení nebo zavření stránky přijít.
Práce s localStorage
S localStorage
se pracuje velmi jednoduše voláním metod na
objektu localStorage
, který máme přístupný odkudkoliv.
Postupně si představíme ty nejdůležitější.
Metoda setItem()
K uložení nějakého řetězce pod textový klíč
použijeme metodu setItem()
. Zkusme si pod klíč
"jmeno"
uložit řetězec "Karel"
:
localStorage.setItem("jmeno", "Karel");
Stránku se skriptem nyní otevřeme v prohlížeči, kód následně smažeme a stránku zas obnovíme. Tím jsme si jistí, že Karla již nikde v kódu nemáme, ale je uložený v úložišti. To si ihned ověříme.
Metoda getItem()
Analogicky pro přečtení hodnoty pod klíčem použijeme metodu
getItem()
. Vypišme si, co je uloženo pod dříve použitým
klíčem "jmeno"
:
document.write(localStorage.getItem("jmeno"));
Výsledek:
Vidíme, že se Karel z localStorage
opravdu načetl.
Přestože localStorage
v našem příkladu
obsahuje položky, které jsme si do něj uložili, neměli bychom na to vždy
spoléhat. V praxi může jakýkoli uživatel k úložišti přistoupit a měnit
jej pomocí nástrojů pro vývojáře.
Metoda removeItem()
Asi nás nepřekvapí, že řetězec pod nějakým klíčem můžeme z
úložiště i vymazat. Uděláme to metodou removeItem()
:
localStorage.removeItem("jmeno");
Metoda clear()
Úložiště můžeme kompletně vyprázdnit od všech klíčů a dat pod
nimi metodou clear()
:
localStorage.clear();
Vlastnost length
Počet položek v localStorage
získáme pomocí vlastnosti
length
:
document.write("Uloženo " + localStorage.length + " položek");
Výsledek:
Metoda key()
Možná nás napadlo, jak zjistíme, co vše je v localStorage
uloženo, nebo jak s položkami v úložišti pracovat hromadně. Poslední
chybějící metoda je proto key(n)
, kde n
je index
– pořadové číslo klíče, který chceme vrátit. V kombinaci s vlastností
length
tak dokážeme například vypsat všechny klíče v
úložišti a hodnoty pod nimi uložené.
Udělejme si poslední ukázku, čímž opravdu vyčerpáme možnosti, které
localStorage
nabízí:
for (let i = 0; i < localStorage.length; i++) { const klic = localStorage.key(i); document.write(klic + ": " + localStorage.getItem(klic) + "<br>"); }
Pokud jsme si Karla z localStorage
ještě nesmazali, vypíše
se nám spolu s jeho klíčem:
Pořadí klíčů může být v každém prohlížeči jiné,
a proto by na něm naše aplikace neměla záviset. Neměli bychom tedy
například předpokládat, že prvně přidaná hodnota bude vždy pod klíčem
0
.
Uložení více hodnot pod jeden klíč
Naskýtá se otázka, jakým způsobem je možné uložit například pole hodnot nebo objekt pod jeden klíč úložiště. Pokud bychom související hodnoty neseskupovali pod jeden klíč, byl by v úložišti opravdu nepořádek. Jen si zkusme představit, jak bychom tam uložili dva uživatele. V takovém případě si proto celý objekt převedeme na textový řetězec a uložíme tento řetězec. Převod objektu na textový řetězec se v JavaScriptu řeší pomocí formátu JSON. To se naučíme v lekci Formát JSON a potom si to i prakticky vyzkoušíme na našem OOP diáři.
sessionStorage
Kromě localStorage
existuje také úložiště
sessionStorage
. To funguje úplně stejně, ale liší se v
kontextu, v kterém jsou data uložena. Rozdíly jsou tři:
- Data v
sessionStorage
jsou unikátní pro každou záložku prohlížeče. Když si naopak otevřeme tu samou stránku používajícílocalStorage
ve více záložkách, budou všechny záložky používat to samé úložiště s těmi stejnými daty. - Data budou ztracena ve chvíli zavření záložky. Naopak
data v
localStorage
v prohlížeči zůstávají. - Proč tedy
sessionStorage
používat, když se se zavřením stránky stejně smaže? To už bychom přeci mohli data uložit jen do proměnných jako doposud...sessionStorage
podobně jako cookies zůstává netknuté při obnovení stránky nebo při přechodu na jinou stránku v té samé doméně.
Cookies
O "sušenkách" jistě každý slyšel, minimálně v souvislosti s Evropskou Unií a její nechvalně proslulou cookie hláškou:

Cookies mají hned několik využití, nejdůležitější jsou tyto tři:
- Uložení dat v prohlížeči – Cookies byly dlouhou dobu
jediným způsobem, jak si v prohlížeči něco uložit, aniž by se to při
obnovení stránky ztratilo. Takto jsme si mohli uložit například, že jsme
na stránce již vykřížkovali cookie hlášku
K tomuto účelu se v dnešní době používá zmiňované úložiště
localStorage
. Tam se hodnoty z úložiště jednodušeji získávají a navíc nezpomalují komunikaci se serverem, protože se neposílají s každým požadavkem. - Přihlašování uživatelů – Jelikož prohlížeč spolu s požadavkem na server odesílá serveru i všechny cookies v dané relaci, realizuje se přes ně přihlášení uživatele na serveru. Jinými slovy, hodnotu cookies uživatele můžeme číst na serveru v jazycích jako je PHP nebo C# .NET. Toto využití je potom popsáno v příslušných serverových kurzech.
- Získání informací o uživateli – Do cookies si Google například uloží, že jsme na něm hledali boty. Google reklamy na jiných stránkách si poté načtou z cookies to, co nás zajímá, a dané zboží nám nabízí. Podobně funguje i Google Analytics, jejichž cookie je snad na každé internetové stránce.
V klientském JavaScriptu tedy již pro používání cookies není příliš dobrý důvod. Pro úplnost si práci s nimi ale stejně ukážeme, učit se ji ale nemusíte.
Vytvoření nové cookie
Cookies se v JavaScriptu nastavují pomocí vlastnosti
document.cookie
, která obsahuje všechny cookies. Při
přiřazení nové hodnoty se nepřepíšou všechny existující cookies, ale
přidá se pouze jedna nová.
Cookie se zapisuje jako řetězec s několika částmi oddělenými středníky:
document.cookie = "eu_hlaska_zavrena=1; expires=Sun, 29 Mar 2020 22:00:00 GMT; path=/";
Cookie zadáváme:
- Název a hodnotu.
- Datum expirace – Pokud vynecháme, cookie platí do zavření záložky prohlížeče. Pokud nastavíme do minulosti, cookie se ihned odstraní.
- Cestu – Určuje, kde všude na webu bude cookie platit.
Nejčastěji se nastavuje na celou doménu pomocí hodnoty
/
.
Krkolomné vytváření cookie přímo svádí k napsání funkce. Její kód by byl následující:
function vytvorCookie(nazev, hodnota, expirace = null) { const casExpirace = expirace ? `expires=${expirace.toGMTString()};` : ''; document.cookie = `${nazev}=${hodnota}; ${casExpirace} path=/`; } vytvorCookie('eu_hlaska_zavrena', 1, new Date(2030, 2, 30));
Načtení hodnoty cookie
Ani načtení cookie není příliš intuitivní. Musíme si totiž nejprve načíst všechny cookies v daném dokumentu, což je dlouhý textový řetězec, ve kterém jsou všechny cookies dané stránky oddělené středníky. Je tam pouze název a hodnota, datum expirace si vnitřně řeší prohlížeč.
Řetězec document.cookie
vypadá například takto:
_ga=GA1.2.1621812384.1525606041; __gads=ID=fdbec79e49891ee3:T=1525606041:S=ALNI_MYNHHGQJ65wZIjdzOc60pQanykM8w; __qca=P0-718587983-1559900791988; _gid=GA1.2.2042156113.1583272955; _gat=1; eu_hlaska_zavrena=1"
Nyní je na nás, abychom si z něj hodnotu eu_hlaska_zavrena
vypreparovali. Samozřejmě musíme počítat i s tím, že daná cookie vůbec
neexistuje! Funkce pro čtení cookies by vypadala takto:
function najdiCookie(nazev) { const cookies = document.cookie.split(';') for (const cookie of cookies) { hodnoty = cookie.split('='); if (nazev == hodnoty[0].trim()) return hodnoty[1].trim(); } return null; } document.write(najdiCookie('eu_hlaska_zavrena'));
Zkusme si v prohlížeči, že to opravdu funguje:
Jak již bylo řečeno, všechny hodnoty všech cookie se odesílají
na server v hlavičce HTTP požadavku při každém načtení každé
stránky. Proto je používejme jen pokud nemůžeme použít
localStorage
.
Cookie nastavená ze serveru může mít nastavenou vlastnost
HttpOnly
, čímž ji z klientského JavaScriptu nemůžeme
načíst. Toto je základní bezpečnostní opatření, které by mělo být
nastaveno při používání cookies k autentizaci uživatelů na serveru. Z
klientského JavaScriptu tato vlastnost pochopitelně cookies nastavit nelze
Odstranění cookie
Odstranění cookie je stejné jako její vytvoření. Pouze nastavíme její expiraci do minulosti, což povede k jejímu odstranění. Jako hodnotu můžeme uvést cokoli.
IndexedDb
Posledním standardním úložištěm, které můžeme v JavaScriptu
používat, je IndexedDB. Jak název napovídá, nejedná se
již o pouhé úložiště, kam ukládáme data pod nějakými klíči. Jde o
plnohodnotnou databázi s tabulkami a indexy, z čehož také
vznikl její název. Ukládáme tedy strukturovaná data.
Databázové indexy nám umožňují v databázi vyhledávat řádově rychleji
a jsme schopní s daty přímo pracovat, aniž bychom je museli převádět z/do
textových řetězců, jako je tomu u localStorage
. IndexedDB
navíc podporuje transakce a další standardní databázové
mechanismy, které nám zajistí datovou integritu (že data
nejsou uložena špatně, třeba proto, že v aplikaci došlo k chybě,
jednoduše se uloží buď vše, nebo nic). V neposlední řadě podporuje
rovněž ukládání souborů.
Databáze je takzvaná NoSQL. To znamená, že s ní nekomunikujeme jazykem SQL, jako to u databází bývá zvykem, ale voláním metod na databázových objektech. To je někdy trochu krkolomné, a proto pro ni vzniklo hned několik nástaveb.
IndexedDB je vhodná pro větší aplikace se složitější datovou strukturou a větším množstvím dat. Její používání převyšuje úroveň OOP kurzu a vydalo by na samostatný kurz, proto se s ní zde zabývat nebudeme.
IndexedDB bychom neměli zaměňovat s WebQL, což je jiná nestandardní databáze, která není podporována všemi prohlížeči.
V následujícím kvízu, Kvíz - Datové typy a datová úložiště v JavaScriptu, si vyzkoušíme nabyté zkušenosti z předchozích lekcí.