Hujjat va resurs yuklanishi

Sahifa: DOMContentLoaded, load, beforeunload, unload

HTML sahifaning hayot sikli (lifecycle) uchta muhim hodisaga ega: DOMContentLoaded โ€” brauzer HTML'ni to'liq yuklab, DOM daraxtini qurganida; load โ€” nafaqat HTML, balki barcha tashqi resurslar (rasm, stil, skript) yuklanganida; beforeunload va unload โ€” foydalanuvchi sahifani tark etayotganida. Har bir hodisa foydali bo'lishi mumkin: DOMContentLoaded โ€” DOM tayyor bo'lgach initsializatsiya qilish uchun, load โ€” barcha resurslar yuklanganini tekshirish uchun, beforeunload/unload โ€” foydalanuvchi ketayotganda o'zgarishlarni saqlash uchun. Bu darsda ularni chuqur o'rganamiz.

Bu bo'limdagi barcha misollar brauzer muhitiga (DOM, document, window) tegishli. Kurs sahifasidagi interaktiv maydoncha Node.js muhitida ishlaydi va DOM'ga ega emas โ€” shu sabab bu darsdagi kod misollari statik bo'lib, faqat o'qib o'rganish uchun keltiriladi. Ularni ishga tushirib bo'lmaydi.

DOMContentLoaded

DOMContentLoaded hodisasi document obyektida yuz beradi. Uni kuzatish uchun albatta addEventListenerdan foydalanish kerak:

document.addEventListener('DOMContentLoaded', ready);
// funksiya sifatida yozamiz, chaqirmaymiz: ready() emas, balki ready

Masalan:

<script>
  function ready() {
    alert('DOM tayyor');

    // rasm hali yuklanmagan bo'lishi mumkin (agar keshda bo'lmasa),
    // shu sabab o'lchami 0x0 bo'lishi mumkin
    alert(`Rasm o'lchami: ${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">

Bu misolda DOMContentLoaded ishlaganida hujjat butunlay yuklangan โ€” brauzer barcha HTML elementlarni ko'rgan va ularga qo'l uzata oladi. Lekin rasm (<img>) hali yuklanib ulgurmagan bo'lishi mumkin, shuning uchun uning o'lchami ko'pincha 0x0 chiqadi.

DOMContentLoaded โ€” bu DOM daraxti tayyor degani, resurslar tayyor emas. Rasm va boshqa tashqi fayllar hali yuklanayotgan bo'lishi mumkin. Agar sizga faqat DOM elementlari kerak bo'lsa (masalan, tugmaga hodisa biriktirish), bu hodisadan foydalanish eng to'g'ri yechim.

DOMContentLoaded va skriptlar

Brauzer HTML'ni qayta ishlaganda, agar <script> tegiga duch kelsa, DOM qurishni to'xtatib, uni bajarishi shart. Buning sababi bor: skript DOM'ni o'zgartirishi yoki hatto document.write orqali yangi HTML qo'shishi mumkin, shuning uchun brauzer skript tugaguncha kutadi.

Demak, DOMContentLoaded hodisasi skriptlar bajarilib bo'lgach sodir bo'ladi:

<script>
  document.addEventListener('DOMContentLoaded', () => {
    alert('DOM tayyor!');
  });
</script>

<script src="https://cdn.jsdelivr.net/npm/lodash@4.17.21/lodash.min.js"></script>

<script>
  alert('Kutubxona yuklandi, kod ishga tayyor');
</script>

Bu misolda avval "Kutubxona yuklandi", so'ng "DOM tayyor!" ko'rsatiladi. Ya'ni DOMContentLoaded barcha skriptlar bajarilishini kutadi.

Yuqoridagi qoidaning ikkita istisnosi bor. Bular DOMContentLoadedni bloklamaydi: async atributiga ega skriptlar va document.createElement('script') orqali dinamik qo'shilgan skriptlar. Ular mustaqil yuklanadi va DOM qurilishini kutmaydi. Bularni keyingi darslarda batafsil ko'rib chiqamiz.

DOMContentLoaded va stillar

Tashqi stil jadvallari (<link rel="stylesheet">) DOM'ga ta'sir qilmaydi, shuning uchun DOMContentLoaded ularni kutmaydi.

Lekin bir nozik holat bor: agar stildan keyin skript kelsa, u skript stil yuklanguncha kutadi:

<link type="text/css" rel="stylesheet" href="style.css">
<script>
  // skript style.css yuklanmaguncha bajarilmaydi
  alert(getComputedStyle(document.body).marginTop);
</script>

Sababi shundaki: skript elementlarning haqiqiy koordinatalari va boshqa stilga bog'liq xossalarini o'qishga muhtoj bo'lishi mumkin. Shu sababdan skript oldidagi stil to'liq yuklanishini kutadi. Bu esa o'z navbatida DOMContentLoadedni ham kechiktiradi.

Brauzerning avto-to'ldirishi (autofill)

Firefox, Chrome va Opera DOMContentLoaded hodisasida formalarni avtomatik to'ldiradi (autofill). Masalan, agar sahifada login/parol formasi bo'lsa va foydalanuvchi qiymatlarini saqlagan bo'lsa, brauzer DOMContentLoadedda ularni qo'yib chiqadi.

Shuning uchun agar sahifada ko'p yuklanadigan skriptlar tufayli DOMContentLoaded kechiksa, avto-to'ldirish ham kechikadi. Buni foydalanuvchilar sahifa "sekin" ochilayotgandek his qilishi mumkin.

load, unload va readyState

Endi qolgan hodisalarni ko'rib chiqamiz.

window.onload

load hodisasi window obyektida yuz beradi va u barcha resurslar โ€” rasm, stil, skriptlar โ€” yuklanib bo'lgach ishga tushadi:

<script>
  window.onload = function() {
    alert('Sahifa to'liq yuklandi');

    // endi rasm haqiqiy o'lchamiga ega
    alert(`Rasm o'lchami: ${img.offsetWidth}x${img.offsetHeight}`);
  };
</script>

<img id="img" src="https://en.js.cx/clipart/train.gif?speed=1&cache=0">

Bu yerda DOMContentLoadeddan farqli o'laroq, rasm yuklangan va uning to'g'ri o'lchamlari ko'rsatiladi.

window.onunload

Foydalanuvchi sahifadan ketganda window obyektida unload hodisasi ishga tushadi. Bu yerda pop-up oynalar ochish kabi kechiktirishga imkon bermaydigan ishlar mumkin emas, lekin analitika ma'lumotlarini yuborish uchun ishlatiladi:

window.addEventListener('unload', function() {
  // ma'lumotni ketishdan oldin yuborish
});

Amaliyotda unloadda tarmoq so'rovlarini yuborish qiyin, chunki sahifa yopilib qolishi mumkin. Buning uchun maxsus navigator.sendBeacon metodi mavjud (quyida ko'ramiz).

document.readyState

document.readyState xossasi hujjatning joriy yuklanish holatini bildiradi. Uning uchta qiymati bor:

Uni tekshirib, holatga qarab ish yuritishimiz mumkin. Ba'zan skript kechroq ishga tushadi va DOMContentLoaded allaqachon o'tib ketgan bo'ladi. Bunday holda hodisani kutish behuda bo'ladi, shuning uchun readyStateni tekshirish kerak:

function work() { /* ... */ }

if (document.readyState == 'loading') {
  // hali yuklanmoqda โ€” hodisani kutamiz
  document.addEventListener('DOMContentLoaded', work);
} else {
  // DOM allaqachon tayyor!
  work();
}

Holatning o'zgarishini kuzatish uchun readystatechange hodisasi ham bor, lekin bu bugungi kunda kam ishlatiladi โ€” DOMContentLoaded va load yetarli:

// joriy holat
console.log(document.readyState);

// har o'zgarishda
document.addEventListener('readystatechange', () => {
  console.log(document.readyState);
});
Hodisalarning odatiy ketma-ketligi: readyState:loading โ†’ readyState:interactive โ†’ DOMContentLoaded โ†’ (rasm/resurslar yuklanadi) โ†’ readyState:complete โ†’ window.onload. Ya'ni interactive DOMContentLoadeddan biroz oldin, complete esa loaddan biroz oldin belgilanadi.

beforeunload bilan ogohlantirish

Agar foydalanuvchi sahifadan ketmoqchi bo'lsa yoki oynani yopmoqchi bo'lsa, beforeunload ishlovchisi qo'shimcha tasdiqni so'rashi mumkin. Bu ayniqsa foydalanuvchi to'ldirilmagan formani tashlab ketayotganda foydali โ€” "O'zgarishlar saqlanmagan, chindan ham ketmoqchimisiz?" degan savol chiqarish uchun.

Agar biz beforeunload hodisasini bekor qilsak, brauzer foydalanuvchidan tasdiq so'raydi:

window.addEventListener('beforeunload', function(event) {
  event.preventDefault();

  // eski brauzerlar uchun returnValue kerak bo'lishi mumkin
  event.returnValue = '';
});
Zamonaviy brauzerlarda event.returnValuega qo'ygan matningizni ko'rsatib bo'lmaydi โ€” brauzer o'zining standart xabarini chiqaradi (masalan, "Ushbu sahifani tark etmoqchimisiz?"). Bu foydalanuvchini yolg'on xabarlar bilan aldashning oldini olish uchun qilingan. Shu sabab matnni maxsuslashtirishga urinmang โ€” u baribir ko'rinmaydi.

Muhim nozik jihat: bu ishlovchi faqat foydalanuvchi sahifa bilan o'zaro aloqada bo'lgan (bosgan, matn kiritgan) holatda ishlaydi. Agar foydalanuvchi sahifa bilan hech qanday muloqot qilmasa, ba'zi brauzerlar ogohlantirishni ko'rsatmaydi.

navigator.sendBeacon

Foydalanuvchi sahifadan ketayotganda ko'pincha serverga analitika yoki statistika ma'lumotlarini yuborishimiz kerak bo'ladi. Muammo shundaki, unloadda oddiy fetch yoki XMLHttpRequest so'rovi ko'pincha bekor qilinadi, chunki sahifa yopilib qolyapti.

Bu vazifa uchun maxsus navigator.sendBeacon(url, data) metodi mavjud. U ma'lumotni fon rejimida yuboradi va sahifa yopilsa ham so'rovni yakunlaydi:

let analyticsData = { /* ma'lumotlar obyekti */ };

window.addEventListener('unload', function() {
  navigator.sendBeacon('/analytics', JSON.stringify(analyticsData));
});
sendBeacon yuborilgach, brauzer sahifa yopilgan taqdirda ham so'rovni yakunlashga harakat qiladi. Shu sababdan ketish paytida analitika yuborishning eng ishonchli usuli aynan shu. Zamonaviy alternativa: fetch(url, { keepalive: true }) ham xuddi shunday vazifani bajaradi.

Xulosa

Sahifa hayot sikli hodisalari:

document.readyState orqali istalgan paytda holatni tekshirish mumkin: loading, interactive yoki complete. Bu skript kech ishga tushganda DOMContentLoaded o'tib ketgan-ketmaganini bilishga yordam beradi.