Životní cyklus stránky HTML má tři důležité události:
DOMContentLoaded
– prohlížeč plně načte HTML a je vytvořen strom DOM, ale externí zdroje, jako jsou obrázky<img>
a šablony stylů se možná ještě nenačetly.load
– načte se nejen HTML, ale také všechny externí zdroje:obrázky, styly atd.beforeunload/unload
– uživatel opouští stránku.
Každá událost může být užitečná:
DOMContentLoaded
událost – DOM je připraven, takže obsluha může vyhledat uzly DOM, inicializovat rozhraní.load
událost – načítají se externí zdroje, takže se aplikují styly, jsou známé velikosti obrázků atd.beforeunload
událost – uživatel odchází:můžeme zkontrolovat, zda uživatel změny uložil, a zeptat se, zda opravdu chce odejít.unload
– uživatel téměř odešel, ale stále můžeme zahájit některé operace, jako je odesílání statistik.
Pojďme prozkoumat podrobnosti těchto událostí.
DOMContentLoaded
DOMContentLoaded
událost se stane na document
objekt.
Musíme použít addEventListener
abyste to chytili:
document.addEventListener("DOMContentLoaded", ready);
// not "document.onDOMContentLoaded = ..."
Například:
<script>
function ready() {
alert('DOM is ready');
// image is not yet loaded (unless it was cached), so the size is 0x0
alert(`Image size: ${img.offsetWidth}x${img.offsetHeight}`);
}
document.addEventListener("DOMContentLoaded", ready);
</script>
<img id="img" src="https://en.js.cx/clipart/train.gif?speed=1&cache=0">
V příkladu DOMContentLoaded
handler se spustí, když je dokument načten, takže může vidět všechny prvky, včetně <img>
níže.
Ale nečeká na načtení obrázku. Takže alert
zobrazuje nulové velikosti.
Na první pohled DOMContentLoaded
akce je velmi jednoduchá. Strom DOM je připraven – zde je událost. Existuje však několik zvláštností.
DOMContentLoaded a skripty
Když prohlížeč zpracuje dokument HTML a narazí na <script>
Před pokračováním ve vytváření DOM je třeba jej provést. To je preventivní opatření, protože skripty mohou chtít upravit DOM a dokonce document.write
do něj, takže DOMContentLoaded
musí počkat.
Takže DOMContentLoaded se určitě stane po takových skriptech:
<script>
document.addEventListener("DOMContentLoaded", () => {
alert("DOM ready!");
});
</script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.3.0/lodash.js"></script>
<script>
alert("Library loaded, inline script executed");
</script>
Ve výše uvedeném příkladu nejprve vidíme „Knihovna načtena…“ a poté „DOM připraven!“ (všechny skripty jsou provedeny).
Skripty, které neblokují DOMContentLoadedZ tohoto pravidla existují dvě výjimky:
- Skripty s kódem
async
atribut, kterému se budeme věnovat o něco později, neblokujteDOMContentLoaded
. - Skripty, které jsou generovány dynamicky pomocí
document.createElement('script')
a poté přidány na webovou stránku, také tuto událost neblokují.
DOMContentLoaded a styly
Externí šablony stylů nemají vliv na DOM, takže DOMContentLoaded
nečeká na ně.
Ale je tu úskalí. Pokud máme za stylem skript, pak tento skript musí počkat, dokud se šablona stylů nenačte:
<link type="text/css" rel="stylesheet" href="style.css">
<script>
// the script doesn't execute until the stylesheet is loaded
alert(getComputedStyle(document.body).marginTop);
</script>
Důvodem je, že skript může chtít získat souřadnice a další vlastnosti prvků závislé na stylu, jako v příkladu výše. Přirozeně musí čekat na načtení stylů.
Jako DOMContentLoaded
čeká na skripty, nyní před nimi čeká i na styly.
Vestavěné automatické vyplňování prohlížeče
Automatické vyplňování formulářů Firefox, Chrome a Opera na DOMContentLoaded
.
Pokud má například stránka formulář s přihlašovacím jménem a heslem a prohlížeč si zapamatoval hodnoty, pak na DOMContentLoaded
může se pokusit je automaticky vyplnit (pokud to uživatel schválí).
Pokud tedy DOMContentLoaded
je oddalováno dlouhým načítáním skriptů, pak čeká i automatické vyplňování. Pravděpodobně jste to viděli na některých webech (pokud používáte automatické vyplňování prohlížeče) – pole pro přihlášení/heslo se nevyplní automaticky okamžitě, ale dojde ke zpoždění, než se stránka úplně načte. To je vlastně zpoždění do DOMContentLoaded
událost.
windows.onload
load
událost na window
objekt se spouští při načtení celé stránky včetně stylů, obrázků a dalších zdrojů. Tato událost je dostupná prostřednictvím onload
vlastnost.
Níže uvedený příklad správně ukazuje velikosti obrázků, protože window.onload
čeká na všechny obrázky:
<script>
window.onload = function() { // can also use window.addEventListener('load', (event) => {
alert('Page loaded');
// image is loaded at this time
alert(`Image size: ${img.offsetWidth}x${img.offsetHeight}`);
};
</script>
<img id="img" src="https://en.js.cx/clipart/train.gif?speed=1&cache=0">
window.onunload
Když návštěvník opustí stránku, zobrazí se unload
spouští událost na window
. Můžeme tam udělat něco, co nezahrnuje zpoždění, jako je zavírání souvisejících vyskakovacích oken.
Pozoruhodnou výjimkou je odesílání analýz.
Řekněme, že shromažďujeme data o tom, jak se stránka používá:kliknutí myší, posouvání, zobrazené oblasti stránky atd.
Samozřejmě unload
událost je, když nás uživatel opustí, a my bychom rádi data uložili na náš server.
Existuje speciální navigator.sendBeacon(url, data)
metoda pro tyto potřeby, popsaná ve specifikaci https://w3c.github.io/beacon/.
Odesílá data na pozadí. Přechod na jinou stránku není zpožděn:prohlížeč opustí stránku, ale stále provádí sendBeacon
.
Zde je návod, jak jej používat:
let analyticsData = { /* object with gathered data */ };
window.addEventListener("unload", function() {
navigator.sendBeacon("/analytics", JSON.stringify(analyticsData));
});
- Požadavek je odeslán jako POST.
- Můžeme odeslat nejen řetězec, ale také formuláře a další formáty, jak je popsáno v kapitole Načítání, ale obvykle se jedná o stringifikovaný objekt.
- Data jsou omezena na 64 kb.
Když sendBeacon
požadavek je dokončen, prohlížeč pravděpodobně již opustil dokument, takže neexistuje způsob, jak získat odpověď serveru (která je obvykle pro analýzu prázdná).
K dispozici je také keepalive
příznak pro provádění takových požadavků „after-page-left“ v metodě načítání pro obecné síťové požadavky. Více informací naleznete v kapitole Fetch API.
Pokud chceme zrušit přechod na jinou stránku, nemůžeme to udělat zde. Můžeme ale použít jinou událost – onbeforeunload
.
window.onbeforeunload
Pokud návštěvník zahájil navigaci mimo stránku nebo se pokusil zavřít okno, zobrazí se beforeunload
handler požádá o dodatečné potvrzení.
Pokud událost zrušíme, prohlížeč se může návštěvníka zeptat, zda si je jistý.
Můžete to zkusit spuštěním tohoto kódu a opětovným načtením stránky:
window.onbeforeunload = function() {
return false;
};
Z historických důvodů se za zrušení události počítá i vrácení neprázdného řetězce. Před časem to prohlížeče zobrazovaly jako zprávu, ale jak říká moderní specifikace, neměly by.
Zde je příklad:
window.onbeforeunload = function() {
return "There are unsaved changes. Leave now?";
};
Chování bylo změněno, protože někteří webmasteři zneužívali tuto obsluhu událostí zobrazováním zavádějících a obtěžujících zpráv. Takže právě teď to staré prohlížeče mohou stále zobrazovat jako zprávu, ale kromě toho – neexistuje způsob, jak upravit zprávu zobrazovanou uživateli.
event.preventDefault()
nefunguje z beforeunload
psovod
Může to znít divně, ale většina prohlížečů ignoruje event.preventDefault()
.
Což znamená, že následující kód nemusí fungovat:
window.addEventListener("beforeunload", (event) => {
// doesn't work, so this event handler doesn't do anything
event.preventDefault();
});
Místo toho by se v takových obslužných programech mělo nastavit event.returnValue
na řetězec, abyste získali výsledek podobný výše uvedenému kódu:
window.addEventListener("beforeunload", (event) => {
// works, same as returning from window.onbeforeunload
event.returnValue = "There are unsaved changes. Leave now?";
});
stav připravenosti
Co se stane, když nastavíme DOMContentLoaded
handler po načtení dokumentu?
Přirozeně to nikdy neběží.
Jsou případy, kdy si nejsme jisti, zda je dokument připraven nebo ne. Rádi bychom, aby se naše funkce spustila při načtení DOM, ať už teď nebo později.
document.readyState
vlastnost nám říká o aktuálním stavu načítání.
Existují 3 možné hodnoty:
"loading"
– dokument se načítá."interactive"
– dokument byl zcela přečten."complete"
– dokument byl plně přečten a jsou načteny také všechny zdroje (jako obrázky).
Takže můžeme zkontrolovat document.readyState
a nastavte obslužnou rutinu nebo okamžitě spusťte kód, pokud je připraven.
Takhle:
function work() { /*...*/ }
if (document.readyState == 'loading') {
// still loading, wait for the event
document.addEventListener('DOMContentLoaded', work);
} else {
// DOM is ready!
work();
}
Je zde také readystatechange
událost, která se spustí, když se stav změní, takže můžeme všechny tyto stavy vytisknout takto:
// current state
console.log(document.readyState);
// print state changes
document.addEventListener('readystatechange', () => console.log(document.readyState));
readystatechange
event je alternativní mechanika sledování stavu načítání dokumentu, objevila se již dávno. V současné době se používá zřídka.
Pro úplnost se podívejme na celý průběh událostí.
Zde je dokument s <iframe>
, <img>
a obslužné programy, které zaznamenávají události:
<script>
log('initial readyState:' + document.readyState);
document.addEventListener('readystatechange', () => log('readyState:' + document.readyState));
document.addEventListener('DOMContentLoaded', () => log('DOMContentLoaded'));
window.onload = () => log('window onload');
</script>
<iframe src="iframe.html" onload="log('iframe onload')"></iframe>
<img src="http://en.js.cx/clipart/train.gif" id="img">
<script>
img.onload = () => log('img onload');
</script>
Pracovní příklad je v karanténě.
Typický výstup:
- [1] počáteční readyState:načítání
- [2] readyState:interactive
- [2] DOMContentLoaded
- [3] načtení prvku iframe
- [4] img onload
- [4] readyState:complete
- Načtení okna [4]
Čísla v hranatých závorkách označují přibližný čas, kdy k tomu dojde. Události označené stejnou číslicí probíhají přibližně ve stejnou dobu (± několik ms).
document.readyState
se změní nainteractive
těsně předDOMContentLoaded
. Tyto dvě věci ve skutečnosti znamenají totéž.document.readyState
se změní nacomplete
když všechny zdroje (iframe
aimg
) jsou načteny. Zde vidíme, že se to stane přibližně ve stejnou dobu jakoimg.onload
(img
je poslední zdroj) awindow.onload
. Přepínání nacomplete
stav znamená totéž jakowindow.onload
. Rozdíl je v tom, žewindow.onload
vždy funguje po všech ostatníchload
manipulátory.
Shrnutí
Události načítání stránky:
DOMContentLoaded
událost se spustí nadocument
až bude DOM připraven. V této fázi můžeme na prvky aplikovat JavaScript.- Skript jako
<script>...</script>
nebo<script src="..."></script>
blokovat DOMContentLoaded, prohlížeč čeká na jejich provedení. - Obrázky a další zdroje mohou také pokračovat v načítání.
- Skript jako
- Číslo
load
událost nawindow
spustí se při načtení stránky a všech zdrojů. Používáme to jen zřídka, protože obvykle není potřeba čekat tak dlouho. beforeunload
událost nawindow
spouští, když chce uživatel opustit stránku. Pokud událost zrušíme, prohlížeč se zeptá, zda chce uživatel skutečně odejít (např. máme neuložené změny).- Číslo
unload
událost nawindow
spouští, když uživatel konečně odchází, v handleru můžeme dělat pouze jednoduché věci, které nezahrnují zpoždění nebo dotaz uživatele. Kvůli tomuto omezení se používá zřídka. Můžeme odeslat síťový požadavek snavigator.sendBeacon
. document.readyState
je aktuální stav dokumentu, změny lze sledovat vreadystatechange
událost:loading
– dokument se načítá.interactive
– dokument je analyzován, probíhá přibližně ve stejnou dobu jakoDOMContentLoaded
, ale předtím.complete
– dokument a zdroje jsou načteny, což se děje přibližně ve stejnou dobu jakowindow.onload
, ale předtím.