Stránka:DOMContentLoaded, load, beforeunload, unload

Ž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í DOMContentLoaded

Z tohoto pravidla existují dvě výjimky:

  1. Skripty s kódem async atribut, kterému se budeme věnovat o něco později, neblokujte DOMContentLoaded .
  2. 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. [1] počáteční readyState:načítání
  2. [2] readyState:interactive
  3. [2] DOMContentLoaded
  4. [3] načtení prvku iframe
  5. [4] img onload
  6. [4] readyState:complete
  7. 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í na interactive těsně před DOMContentLoaded . Tyto dvě věci ve skutečnosti znamenají totéž.
  • document.readyState se změní na complete když všechny zdroje (iframe a img ) jsou načteny. Zde vidíme, že se to stane přibližně ve stejnou dobu jako img.onload (img je poslední zdroj) a window.onload . Přepínání na complete stav znamená totéž jako window.onload . Rozdíl je v tom, že window.onload vždy funguje po všech ostatních load manipulátory.

Shrnutí

Události načítání stránky:

  • DOMContentLoaded událost se spustí na document 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í.
  • Číslo load událost na window 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 na window 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 na window 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 s navigator.sendBeacon .
  • document.readyState je aktuální stav dokumentu, změny lze sledovat v readystatechange událost:
    • loading – dokument se načítá.
    • interactive – dokument je analyzován, probíhá přibližně ve stejnou dobu jako DOMContentLoaded , ale předtím.
    • complete – dokument a zdroje jsou načteny, což se děje přibližně ve stejnou dobu jako window.onload , ale předtím.