IlmHamroh
JavaScript Full-stack/13-qism. Brauzer API'lari, performans va xavfsizlik7/32-dars20 daqiqa
Mundarija (38)

JavaScript Permissions, Notifications va Geolocation: ruxsat so'raydigan API'lar

Qisqacha: Joylashuv, bildirishnoma, kamera kabi API'lar foydalanuvchidan ruxsat so'raydi. navigator.permissions.query({ name: "geolocation" }) ruxsat holatini (granted, denied, prompt) so'ramasdan bilib beradi. Notification.requestPermission() va navigator.geolocation.getCurrentPosition() esa ruxsat oynasini chiqaradi. Qoida: ruxsatni sahifa ochilishi bilan emas, foydalanuvchi o'sha imkoniyatni so'raganda va sababini tushuntirib so'rang.

Bu darsda

  • Ruxsat holatlarini (prompt, granted, denied) va ular qanday o'zgarishini tushuntira olasiz.
  • navigator.permissions.query bilan holatni bilib, interfeysni unga moslaysiz.
  • Notification.requestPermission va new Notification bilan bildirishnoma ko'rsatasiz.
  • getCurrentPosition va watchPosition bilan joylashuv olasiz, uch xato kodini to'g'ri ishlaysiz.
  • Ruxsatni "to'g'ri vaqtda, to'g'ri gap bilan" so'rash UX'ini qo'llaysiz.

Oldin bilishingiz kerak: Clipboard va Web Share API, Promise asoslari, async / await, Callback'lar.

1. Nega bu kerak?

O'tgan darsda ko'rdik: buferni o'qish uchun brauzer ruxsat so'raydi. Bunday API'lar ko'p. Telefoningizni eslang: yangi ilova "Joylashuvingizni ko'rishga ruxsat berasizmi?", "Bildirishnoma yuborishga ruxsat berasizmi?" deb so'raydi. Brauzer ham xuddi shunday.

«Bahor»da ikki yangi g'oya bor:

  • "Eng yaqin filial" tugmasi. Mehmonning joylashuvini olib, unga yaqin filialni ko'rsatish — Geolocation API.
  • "Buyurtma tayyor" xabari. Mehmon boshqa tabda o'tirgan bo'lsa ham, taomi tayyor bo'lganda ekran burchagida xabar chiqsin — Notifications API.

Ikkalasi ham shaxsiy narsaga tegadi: qayerda ekaningiz va diqqatingiz. Shuning uchun brauzer ularni ruxsatsiz bermaydi. Bu darsda ruxsat bilan ishlashni o'rganamiz: qanday so'rash, qanday tekshirish va "yo'q" javobini qanday hurmat qilish.

2. Ruxsat qanday ishlaydi

2.1 Uch holat

Har bir ruxsat — har sayt uchun alohida saqlanadigan qaror. Uning uch holati bor:

stateDiagram-v2
  [*] --> prompt: sayt birinchi marta
  prompt --> granted: "Ruxsat berish"
  prompt --> denied: "Bloklash"
  granted --> prompt: sozlamalarda tiklash
  denied --> prompt: sozlamalarda tiklash
  • prompt — hali so'ralmagan. API chaqirilsa, brauzer ruxsat oynasini ko'rsatadi.
  • granted — ruxsat berilgan. Oyna chiqmaydi, API darhol ishlaydi.
  • denied — rad etilgan. Oyna endi chiqmaydi, API darhol xato beradi.

Oxirgi nuqta muhim. Foydalanuvchi bir marta "Bloklash" ni bossa, sayt uni qayta so'ray olmaydi. Faqat foydalanuvchining o'zi manzil satridagi qulf belgisi orqali sozlamani tiklaydi. Shuning uchun birinchi so'rov — yagona imkoniyat. Uni isrof qilmang.

2.2 navigator.permissions.query: so'ramasdan bilish

Ruxsat holatini bilish uchun Permissions API bor. U ruxsat oynasini chiqarmaydi — faqat "hozir qaysi holat?" deb javob beradi:

js
const status = await navigator.permissions.query({
  name: "geolocation",
});

console.log(status.state); // "prompt", "granted" yoki "denied"

Javob — PermissionStatus obyekti. Unda state (holat), name (ruxsat nomi) va change hodisasi bor. Foydalanuvchi sozlamalarda ruxsatni o'zgartirsa, sahifa buni darhol biladi:

js
status.addEventListener("change", () => {
  console.log(`Joylashuv ruxsati endi: ${status.state}`);
});

Biz Live Server sahifasida (Chrome 154) sinadik: ruxsat berilgan sahifada query → granted, keyin ruxsatni olib tashlaganimizda change hodisasi keldi va state prompt ga qaytdi.

2.3 Nomlar va xatolar

name — qat'iy ro'yxatdan: "geolocation", "notifications", "camera", "microphone", "clipboard-read", "clipboard-write", "persistent-storage", "screen-wake-lock" va boshqalar. Ro'yxatda yo'q nom bersangiz:

text
TypeError: Failed to execute 'query' on 'Permissions': Failed to read the 'name' property from 'PermissionDescriptor': The provided value 'clipboard' is not a valid enum value of type PermissionName.

Tarjimasi: "'clipboard' qiymati PermissionName turining ruxsat etilgan qiymatlaridan biri emas". Ya'ni nom xato — "clipboard-read" yoki "clipboard-write" kerak edi. Bu ro'yxat brauzerlarda farq qiladi: bir brauzer taniydigan nomni boshqasi tanimasligi va xuddi shunday TypeError berishi mumkin (MDN'dagi brauzerlar jadvaliga qarang). Shuning uchun query ni har doim try/catch ichida chaqiring.

Diqqat: Permissions API faqat o'qiydi. "Ruxsat so'ra" degan metodi yo'q. Ruxsat oynasini API'ning o'zi chiqaradi: getCurrentPosition, Notification.requestPermission, readText.

2.4 Bu darsdagi jonli oynalarda ruxsatlar yopiq

Jonli oyna — sayt ichidagi alohida, ajratilgan sahifa. Sayt unga ruxsatlar siyosati (permissions policy) bilan imkoniyatlarni cheklaydi: bufer yozish bor, joylashuv, kamera va bildirishnoma — yo'q. Ko'ring:

html
<script type="module">
  const names = [
    "geolocation",
    "notifications",
    "camera",
    "clipboard-write",
  ];
  for (const name of names) {
    const status = await navigator.permissions.query({ name });
    console.log(`${name}: ${status.state}`);
  }
</script>

Konsolda:

text
geolocation: denied
notifications: denied
camera: denied
clipboard-write: granted

Uch API darhol denied — ruxsat oynasi chiqmaydi ham. Bu sizning brauzeringiz sozlamasi emas: oyna siyosat bo'yicha yopiq. Shu sabab joylashuv va bildirishnoma misollarini Live Server'da sinaysiz. Darsdagi ularning natijalari — Live Server sahifasida, Chrome 154 da olingan: ruxsat oldindan berildi, joylashuv esa soxtalashtirildi. O'zingiz ham shunday qila olasiz: DevTools → ⋮ → More tools → Sensors → Location — istalgan koordinatani yoki «Location unavailable» ni tanlang.

Tekshirib ko'ring: Foydalanuvchi joylashuv so'roviga "Bloklash" ni bosdi. Ertasi kuni sayt yana getCurrentPosition ni chaqirdi. Ruxsat oynasi chiqadimi?

Javob

Yo'q. Holat denied — brauzer qarorni eslab qolgan va API darhol xato beradi (code 1). Foydalanuvchi qulf belgisi orqali sozlamani o'zi tiklamaguncha, sayt qayta so'ray olmaydi. Shuning uchun birinchi so'rovni to'g'ri vaqtda qilish muhim.

3. Bildirishnomalar: Notifications API

3.1 Ruxsat so'rash

Bildirishnoma (notification) — operatsion tizimning xabar oynasi: ekran burchagida chiqadi, brauzer boshqa tabda yoki kichraytirilgan bo'lsa ham ko'rinadi. Avval ruxsat:

js
const result = await Notification.requestPermission();
console.log(result); // "granted", "denied" yoki "default"

Notification.requestPermission() — ruxsat oynasini chiqaradi va Promise qaytaradi. Javob — foydalanuvchi tanlovi.

Nomlardagi farqqa e'tibor bering. Bildirishnomaning o'z xususiyati Notification.permission va requestPermission "hali so'ralmagan" holatni "default" deb ataydi. Permissions API esa xuddi shu holatni "prompt" deydi. Ma'nosi bir xil — tarixan ikki API turli vaqtda yozilgan.

Live Server sahifasida sinadik: ruxsat berilmagan headless (oynasiz, avtomatik sinov uchun) Chrome'da so'rov darhol denied bilan tugadi — u yerda ruxsat oynasini bosadigan odam yo'q. Haqiqiy brauzerda ruxsat oynasi chiqadi va javob foydalanuvchi tanloviga bog'liq.

3.2 Bildirishnoma ko'rsatish

js
if (Notification.permission === "granted") {
  const note = new Notification("«Bahor»: buyurtma tayyor", {
    body: "Osh — 2 porsiya. Kassaga yaqinlashing.",
    tag: "buyurtma-12",
  });

  note.addEventListener("click", () => {
    window.focus(); // saytning tabiga qaytish
    note.close();
  });
}
  • Birinchi argument — sarlavha. body — asosiy matn.
  • tag — "bu qaysi xabar". Bir xil tag li yangi bildirishnoma eskisining o'rnini egallaydi. Buyurtma holati uch marta o'zgarsa ("qabul qilindi", "tayyorlanmoqda", "tayyor"), ekranda uchta emas, bitta xabar turadi.
  • click hodisasi — foydalanuvchi xabarni bosdi. Odatda saytga qaytaramiz va xabarni yopamiz.
  • icon — kichik rasm manzili; sayt logotipi uchun.

Ruxsat berib sinadik: show hodisasi keldi va xabarda biz bergan sarlavha, matn va tag bor edi. note.close() dan keyin close hodisasi keldi.

3.3 Cheklovlar

  • Faqat sahifa ochiq turganda. new Notification — sahifa yopilsa, bildirishnoma yuborib bo'lmaydi. Sayt yopiq bo'lsa ham kelsa — bu Push bildirishnoma, uning uchun Service Worker kerak. Service Worker — sahifadan alohida, fonda ishlaydigan skript; uni Service Worker hayot sikli darsida, Push'ni PWA darsida ko'ramiz.
  • Android Chrome'da new Notification ishlamaydi — u yerda faqat Service Worker orqali (registration.showNotification). Shuning uchun Notifications API web-features'da hali "Baseline" emas (2026-oktabr).
  • Xavfsiz kontekst. HTTP sahifada Notification.permission doim "denied" (biz http://bahor.test da tekshirdik).
  • Iframe ichida so'rab bo'lmaydi. Chrome boshqa domendan joylashtirilgan iframe'ga bildirishnoma ruxsatini so'ratmaydi — reklama oynalari foydalanuvchini bezor qilmasin.

Tekshirib ko'ring: Buyurtma holati besh marta o'zgaradi va har safar bildirishnoma yuboriladi. Foydalanuvchi ekranida beshta xabar to'planib qolmasligi uchun nima qilasiz?

Javob

Hamma xabarlarga bir xil tag bering (masalan, "buyurtma-12"). Bir xil tag li yangi bildirishnoma eskisining o'rnini egallaydi — ekranda doim bitta, eng yangi holat turadi.

4. Joylashuv: Geolocation API

4.1 getCurrentPosition

js
navigator.geolocation.getCurrentPosition(
  (position) => {
    const { latitude, longitude, accuracy } = position.coords;
    console.log(latitude, longitude, `±${accuracy} m`);
  },
  (error) => {
    console.log(`Xato ${error.code}: ${error.message}`);
  },
);

Bu API Promise'dan oldin yozilgan, shuning uchun ikki callback oladi: muvaffaqiyat va xato. Callback'lar darsidagi uslub. Birinchi chaqiruvda ruxsat oynasi chiqadi.

Live Server'da, joylashuv (Sensors orqali) Toshkent markaziga qo'yilganda (latitude: 41.3111, longitude: 69.2797, aniqlik 25 m), muvaffaqiyat callback'iga shunday obyekt keldi:

text
{
  timestamp: 1791223235420,
  coords: {
    accuracy: 25,
    latitude: 41.3111,
    longitude: 69.2797,
    altitude: null,
    altitudeAccuracy: null,
    heading: null,
    speed: null
  }
}
  • latitude — kenglik (shimol-janub, −90…90), longitude — uzoqlik (g'arb-sharq, −180…180). Xaritadagi nuqta shu ikki son.
  • accuracy — aniqlik, metrda: haqiqiy joy shu radius ichida. GPS'da 5–20 m, faqat Wi-Fi bo'yicha — yuzlab metr, IP bo'yicha — kilometrlar.
  • altitude (balandlik), speed (tezlik), heading (yo'nalish) — qurilma bilsagina; kompyuterda odatda null.
  • timestamp — o'lchangan vaqt (millisoniya, Date.now() kabi).

4.2 Promise'ga o'rash

await bilan yozish qulayroq. Promise asoslari darsidagi usul — callback'li funksiyani new Promise ga o'rash:

js
function getPosition(options) {
  return new Promise((resolve, reject) => {
    const geo = navigator.geolocation;
    geo.getCurrentPosition(resolve, reject, options);
  });
}

const position = await getPosition({ timeout: 10000 });

Uchinchi argument — sozlamalar:

Sozlama Ma'nosi Sukut
enableHighAccuracy GPS'ni yoqib, aniqroq o'lchash (batareyani ko'proq yeydi) false
timeout ko'pi bilan necha ms kutish cheksiz
maximumAge necha ms gacha eski (keshdagi) javob ham bo'ladi 0

timeout siz chaqiruv cheksiz kutishi mumkin — masalan, binoning ichida GPS signal topolmasa. Doim timeout bering.

4.3 Uch xato kodi

Xato callback'iga GeolocationPositionError keladi. Uning code i uch qiymatdan biri:

code Nomi Qachon
1 PERMISSION_DENIED ruxsat yo'q yoki siyosat yopgan
2 POSITION_UNAVAILABLE qurilma joyni aniqlay olmadi
3 TIMEOUT timeout ichida javob kelmadi

Uchalasini ham Chrome 154 da oldik:

  • Ruxsat rad etilganda: code 1, User denied Geolocation. HTTP sahifada: code 1, Only secure origins are allowed (see: https://goo.gl/Y0ZkNV). — "faqat xavfsiz manbalarga ruxsat".
  • Joy aniqlanmaganda (Sensors'da «Location unavailable»): code 2, message — bo'sh satr.
  • { timeout: 0 } bilan: code 3, Timeout expired.

Ikkinchi holatdagi bo'sh message — muhim saboq: message brauzerga bog'liq, foydalanuvchiga ko'rsatish uchun emas. Matnni code ga qarab o'zingiz yozing.

4.4 Xatolarni to'g'ri ishlash — jonli

Bu jonli oynada joylashuv siyosat bo'yicha yopiq. Bu ham foydali: xato yo'lini ko'rasiz.

html
<style>
  body { font: 1rem/1.5 system-ui, sans-serif; margin: 1rem; }
  button { font: inherit; padding: 0.4rem 0.8rem; }
  #result { min-height: 1.5em; }
</style>
<button id="nearest" type="button">Eng yaqin filial</button>
<p id="result" role="status"></p>
<script>
  const MESSAGES = {
    1: "Joylashuvga ruxsat yo'q. Filialni ro'yxatdan tanlang.",
    2: "Joyingizni aniqlab bo'lmadi. Keyinroq urinib ko'ring.",
    3: "Javob kelmadi. Internet yoki GPS'ni tekshiring.",
  };
  const result = document.getElementById("result");

  document.getElementById("nearest").addEventListener("click", () => {
    result.textContent = "Joylashuv aniqlanmoqda…";
    navigator.geolocation.getCurrentPosition(
      (position) => {
        const { latitude, longitude } = position.coords;
        result.textContent = `Siz: ${latitude}, ${longitude}`;
      },
      (error) => {
        result.textContent = MESSAGES[error.code];
        console.log(`xato kodi: ${error.code}`);
        console.log(error.message);
      },
      { timeout: 10000 },
    );
  });
</script>

«Eng yaqin filial» ni bosing. Konsolda (Live Server'da bosib olingan):

text
xato kodi: 1
Geolocation has been disabled in this document by permissions policy.

Ekranda esa foydalanuvchiga tushunarli matn: Joylashuvga ruxsat yo'q. Filialni ro'yxatdan tanlang. Brauzerning inglizcha message i faqat konsolda — dasturchi uchun.

E'tibor bering: xato bo'lsa ham foydalanuvchiga boshqa yo'l taklif qilinadi — "ro'yxatdan tanlang". Joylashuv — qulaylik, majburiyat emas.

4.5 watchPosition: harakatni kuzatish

Kurer yo'lda, mehmon xaritada uning yaqinlashishini ko'rmoqchi. Bir marta emas, doimiy joylashuv kerak:

js
const watchId = navigator.geolocation.watchPosition(
  (position) => updateMap(position.coords),
  (error) => console.log(`Xato ${error.code}`),
  { enableHighAccuracy: true, maximumAge: 5000 },
);

// kuzatuv kerak bo'lmay qolganda:
navigator.geolocation.clearWatch(watchId);

watchPosition callback'ni har joy o'zgarganda chaqiradi va son qaytaradi — kuzatuv raqami. Uni clearWatch ga berib to'xtatasiz: setInterval va clearInterval juftligi kabi. Biz sinadik: joylashuvni o'zgartirganimizda callback yangi koordinata bilan keldi, clearWatch dan keyingi o'zgarishda esa — kelmadi.

clearWatch ni unutmang. GPS yoqilgan holda qolsa, telefon batareyasi tez tugaydi.

4.6 DevTools'da joylashuvni soxtalashtirish

Har safar Samarqandga borib sinab bo'lmaydi. DevTools → Ctrl + Shift + P → Show Sensors → Location ro'yxatidan shahar tanlang yoki o'z kenglik/uzoqlikni yozing. "Location unavailable" tanlansa — code 2 xatosini sinaysiz.

Tekshirib ko'ring: Sardor joylashuv xatosida foydalanuvchiga error.message ni ko'rsatdi. Ikki sababini ayting, nega bu yomon.

Javob

Birinchidan, matn inglizcha va texnik ("permissions policy") — mehmon Dilshod aka tushunmaydi. Ikkinchidan, u brauzerga bog'liq va bo'sh bo'lishi mumkin (code 2 da Chrome bo'sh satr berdi). Foydalanuvchi matnini code bo'yicha o'zingiz yozing, message — faqat konsolga.

5. Qachon va qanday so'rash

5.1 Ochilishi bilan so'ramang

Eng ko'p uchraydigan xato: sahifa ochilishi bilan "Bildirishnomalarga ruxsat bering" oynasi. Dilshod aka hali sayt nima ekanini bilmaydi — u "Bloklash" ni bosadi. Va bu denied — abadiy. Bu muammo shunchalik keng tarqalganki, brauzerlar choralar ko'rdi:

  • Firefox 72-versiyadan (2020-yil yanvar) bildirishnoma so'rovini faqat foydalanuvchi harakatidan (bosish, tugma) keyin oyna qilib ko'rsatadi. Harakatsiz so'rovda oyna chiqmaydi — manzil satrida faqat kichik belgi paydo bo'ladi.
  • Chrome 80-versiyadan (2020-yil boshi) bildirishnomalar uchun "jimroq" ko'rinishga ega. Oyna o'rniga manzil satrida qisqa "Notifications blocked" yozuvi va ustidan chizilgan qo'ng'iroq belgisi chiqadi. Bu ikki holatda avtomatik yoqiladi: so'rovlarni deyarli hech kim qabul qilmaydigan saytlarda va bildirishnomalarni tez-tez rad etadigan foydalanuvchilarda.

5.2 To'g'ri naqsh: avval tushuntirish

sequenceDiagram
  participant M as Mehmon
  participant S as Sayt
  participant B as Brauzer
  M->>S: "Buyurtma berish" ni bosdi
  S->>M: "Tayyor bo'lganda xabar beraylikmi?" [Ha] [Yo'q]
  M->>S: [Ha]
  S->>B: Notification.requestPermission()
  B->>M: Ruxsat oynasi
  M->>B: Ruxsat berish
  B->>S: "granted"

Ikki qadam:

  1. O'z tushuntirishingiz — sayt ichidagi oddiy tugma yoki oyna: nima uchun kerak va foydalanuvchi nima oladi. "Yo'q" desa — hech narsa qilmaysiz, denied ga tushmaysiz, keyinroq yana taklif qilish mumkin.
  2. Faqat "Ha" dan keyin — brauzer so'rovi. Endi foydalanuvchi nima bo'layotganini biladi va ko'pincha rozi bo'ladi.

5.3 Holatga qarab interfeys

Permissions API shu yerda kerak: tugmani holatga moslash.

js
async function setupLocationButton(button) {
  try {
    const status = await navigator.permissions.query({
      name: "geolocation",
    });
    const update = () => {
      button.hidden = status.state === "denied";
    };
    status.addEventListener("change", update);
    update();
  } catch {
    // query qo'llanmasa — tugma ko'rinib turaveradi
  }
}

denied bo'lsa — tugmani yashiramiz: bosilsa baribir ishlamaydi. O'rniga "Joylashuv o'chirilgan — filialni ro'yxatdan tanlang" kabi matn ko'rsatish ham mumkin. granted bo'lsa — foydalanuvchi allaqachon rozi, joylashuvni bir bosishda olamiz. change tinglovchisi — foydalanuvchi sozlamani o'zgartirsa, tugma ham o'zgaradi.

5.4 Yangi yo'l: <geolocation> elementi

Chrome 144 dan <geolocation> HTML elementi bor — joylashuvni so'raydigan tayyor tugma. Brauzer uni o'zi chizadi va bosish haqiqatan foydalanuvchi niyati ekaniga ishonadi. Shuning uchun denied holatidan ham qayta so'ray oladi. Hozircha faqat Chromium brauzerlarida (web-features, 2026-oktabr) — oddiy tugma bilan zaxira kerak. Bu darsda batafsil to'xtalmaymiz.

6. Hujumchi nigohi

Joylashuv — eng nozik shaxsiy ma'lumotlardan biri. U uyingiz, ishxonangiz va kun tartibingizni ochib beradi.

  • Ortiqcha so'rash. Sayt "eng yaqin filial" uchun ruxsat oladi, keyin watchPosition bilan har 5 soniyada joyni serverga yuboradi — reklama kompaniyasi uchun. Himoya dasturchi tomonida: kerakli paytda, kerakli aniqlikda, bir marta. "Eng yaqin filial" uchun getCurrentPosition yetadi, enableHighAccuracy shart emas. Serverga yuborish shart bo'lsa — koordinatani yaxlitlang (latitude.toFixed(2) — taxminan 1 km).
  • Begona skript. Sahifaga ulangan har skript (analitika, vidjet) sizning ruxsatingiz bilan ishlaydi: siz ruxsat olgan bo'lsangiz, u ham getCurrentPosition ni chaqira oladi. Shuning uchun ruxsat — butun sayt uchun mas'uliyat. Skriptlarni cheklash — Brauzer xavfsizlik modeli darsida.
  • Iframe'lar. Joylashtirilgan begona oynaga sukut bo'yicha ruxsatlar berilmaydi — biz buni jonli oynalarda ko'rdik (denied). Ruxsat kerak bo'lsa, ota sahifa allow="geolocation" bilan aniq beradi. Kerakmasini bermang.
  • Spam bildirishnomalar. "Videoni ko'rish uchun Ruxsat berish ni bosing" — firibgarlarning eski usuli: keyin reklama xabarlari yog'iladi. Foydalanuvchi sifatida: sababi tushunarsiz so'rovga "Bloklash".

7. Ko'p uchraydigan xatolar

7.1 Sahifa ochilishi bilan so'rash

Yuqorida aytdik: rad etish ehtimoli yuqori va denied — qaytarib bo'lmaydi. Tuzatish: foydalanuvchi o'sha imkoniyatni so'raganda, avval tushuntirib.

7.2 Xato callback'isiz getCurrentPosition

js
// ❌ ruxsat rad etilsa — hech narsa bo'lmaydi
navigator.geolocation.getCurrentPosition(showOnMap);

Ikkinchi argument yo'q — xato jim yutiladi. Foydalanuvchi tugmani bosadi, hech narsa o'zgarmaydi. Tuzatish: doim xato callback'i va foydalanuvchiga matn.

7.3 timeout siz

GPS signal topolmasa — "aniqlanmoqda…" abadiy turadi. Tuzatish: { timeout: 10000 } va code 3 uchun matn.

7.4 "default" va "prompt" ni aralashtirish

Notification.permission === "prompt" — hech qachon true bo'lmaydi: u "default" qaytaradi. Tuzatish: Notification.permission uchun "default", permissions.query uchun "prompt".

7.5 clearWatch siz

Sahifadan ketildi yoki kuzatuv kerak emas — GPS ishlab turibdi. Tuzatish: watchId ni saqlang va kerak bo'lmaganda clearWatch(watchId).

8. Mashqlar

1-mashq (oson): Bildirishnoma holati

Notification.permission ga qarab #note-status ga matn yozing: default — "Bildirishnomalar hali so'ralmagan", granted — "Bildirishnomalar yoqilgan", denied — "Bildirishnomalar bloklangan (brauzer sozlamalaridan yoqing)". Bu jonli oynada uchinchisi chiqishi kerak (Notification.permission siyosat tufayli denied).

Yechim
html
<p id="note-status"></p>
<script>
  const TEXTS = {
    default: "Bildirishnomalar hali so'ralmagan",
    granted: "Bildirishnomalar yoqilgan",
    denied:
      "Bildirishnomalar bloklangan (brauzer sozlamalaridan yoqing)",
  };
  const state = Notification.permission;
  document.getElementById("note-status").textContent = TEXTS[state];
  console.log(state);
</script>

Konsolda:

text
denied

Obyekt kalitlari aynan holat nomlari — if/else zanjiri kerak emas. Live Server'da ochsangiz default (hali so'ralmagan) chiqadi.

2-mashq (o'rta): Ruxsatdan oldin tushuntirish

Live Server'da sahifa yozing: «Buyurtma tayyor bo'lganda xabar beraylikmi?» degan matn va ikki tugma — «Ha» va «Keyinroq». «Ha» bosilganda Notification.requestPermission() chaqirilsin; granted bo'lsa — 3 soniyadan keyin «Bahor»: buyurtma tayyor bildirishnomasi chiqsin (tag: "buyurtma"). «Keyinroq» — taklifni yashirsin, ruxsat so'ramasin. Agar Notification.permission allaqachon denied bo'lsa, taklif umuman ko'rinmasin.

Yechim
html
<div id="offer" hidden>
  <p>Buyurtma tayyor bo'lganda xabar beraylikmi?</p>
  <button id="yes" type="button">Ha</button>
  <button id="later" type="button">Keyinroq</button>
</div>
<script>
  const offer = document.getElementById("offer");
  offer.hidden = Notification.permission !== "default";

  document.getElementById("later").addEventListener("click", () => {
    offer.hidden = true; // ruxsat so'ralmadi, keyin yana taklif
  });

  const yesButton = document.getElementById("yes");
  yesButton.addEventListener("click", async () => {
    offer.hidden = true;
    const result = await Notification.requestPermission();
    console.log(`ruxsat: ${result}`);
    if (result !== "granted") return;
    setTimeout(() => {
      new Notification("«Bahor»: buyurtma tayyor", {
        body: "Osh — 2 porsiya",
        tag: "buyurtma",
      });
    }, 3000);
  });
</script>

Ruxsat berib sinadik: «Ha» → konsolda ruxsat: granted, 3 soniyadan keyin bildirishnomaning show hodisasi keldi. Ruxsat bloklangan bo'lsa — ruxsat: denied va bildirishnoma yo'q. Haqiqiy brauzerda «Ha» dan keyin ruxsat oynasi chiqadi. granted bo'lsa ham taklif ko'rinmaydi — so'raydigan narsa qolmagan.

3-mashq (qiyin): «Bahor»gacha masofa

Mehmon joylashuvini olib, «Bahor»gacha masofani hisoblang. «Bahor» koordinatasi (mashq uchun): 41.3111, 69.2797. Ikki nuqta orasidagi masofa — Haversine formulasi (Yer — radiusi 6 371 km li shar):

js
const R = 6371000; // Yer radiusi, metr
const toRad = (deg) => (deg * Math.PI) / 180;
const dLat = toRad(lat2 - lat1);
const dLon = toRad(lon2 - lon1);
const a =
  Math.sin(dLat / 2) ** 2 +
  Math.cos(toRad(lat1)) * Math.cos(toRad(lat2)) *
    Math.sin(dLon / 2) ** 2;
const meters = 2 * R * Math.asin(Math.sqrt(a));

Formulani distanceMeters(lat1, lon1, lat2, lon2) funksiyasiga o'rang. Keyin watchPosition bilan kuzating: har yangilanishda #distance ga «Bahor»gacha: 1.2 km (1 000 m dan kam bo'lsa — 850 m) yozilsin. Masofa 100 m dan kam bo'lsa — Yetib keldingiz! va kuzatuv to'xtasin. Xatolar — «Xatolarni to'g'ri ishlash — jonli» bo'limidagi MESSAGES bilan. DevTools Sensors bilan sinang.

Yechim
js
const BAHOR = { lat: 41.3111, lon: 69.2797 };
const output = document.getElementById("distance");

function distanceMeters(lat1, lon1, lat2, lon2) {
  const R = 6371000;
  const toRad = (deg) => (deg * Math.PI) / 180;
  const dLat = toRad(lat2 - lat1);
  const dLon = toRad(lon2 - lon1);
  const a =
    Math.sin(dLat / 2) ** 2 +
    Math.cos(toRad(lat1)) * Math.cos(toRad(lat2)) *
      Math.sin(dLon / 2) ** 2;
  return 2 * R * Math.asin(Math.sqrt(a));
}

function formatDistance(m) {
  if (m < 1000) return `${Math.round(m)} m`;
  return `${(m / 1000).toFixed(1)} km`;
}

const watchId = navigator.geolocation.watchPosition(
  ({ coords }) => {
    const m = distanceMeters(
      coords.latitude, coords.longitude, BAHOR.lat, BAHOR.lon,
    );
    if (m < 100) {
      output.textContent = "Yetib keldingiz!";
      navigator.geolocation.clearWatch(watchId);
      return;
    }
    output.textContent = `«Bahor»gacha: ${formatDistance(m)}`;
  },
  (error) => (output.textContent = MESSAGES[error.code]),
  { timeout: 10000 },
);

Yechim sahifada <p id="distance" role="status"></p> borligini va MESSAGES obyekti jonli misoldan shu faylga ko'chirilganini nazarda tutadi. Ularsiz kod ishlamaydi: output — null, MESSAGES esa ReferenceError beradi.

Joylashuvni uch marta o'zgartirib sinadik: 41.3200, 69.2500 da — «Bahor»gacha: 2.7 km, 41.3150, 69.2797 da — «Bahor»gacha: 434 m, 41.3115, 69.2797 da — Yetib keldingiz!, undan keyingi o'zgarish esa matnni o'zgartirmadi (kuzatuv to'xtagan).

Nega aynan Haversine? Yer yassi emas: kenglik va uzoqlik graduslari turli joyda turli metrga teng (Toshkentda 1° uzoqlik ≈ 84 km, ekvatorda ≈ 111 km). Shahar ichidagi masofa uchun bu formula yetarlicha aniq. Yo'l bo'ylab masofa (ko'chalar) — xarita xizmatlari (Yandex, Google) ishi.

4-mashq: Amaliy tajriba — vazifalar eslatmasi

Kanon kodga tegmasdan tajriba: kurs/mashqlar/13/07-eslatma/index.html — Live Server'da ochiladigan kichik sahifa. Maydon va «Eslatma qo'yish» tugmasi: vazifa matni va soniyalar soni (5–60). Bosilganda:

  1. Notification.permission default bo'lsa — tugma ostida bir gap bilan sababini yozing va requestPermission() chaqiring.
  2. granted bo'lsa — belgilangan soniyadan keyin Eslatma: <matn> bildirishnomasi, tag — vazifa matni (bir vazifaga ikki eslatma bir-birini almashtirsin).
  3. denied bo'lsa — bildirishnoma o'rniga sahifadagi role="status" maydonida eslatma chiqsin.
  4. Bildirishnoma bosilsa — window.focus().

Permissions API bilan sahifa ochilganda holatni ko'rsating va change da yangilang.

Yechim
html
<label for="task">Vazifa:</label>
<input id="task" value="Bron ro'yxatini tekshirish">
<label for="seconds">Necha soniyadan keyin:</label>
<input id="seconds" type="number" min="5" max="60" value="5">
<button id="remind" type="button">Eslatma qo'yish</button>
<p id="state"></p>
<p id="fallback" role="status"></p>
<script type="module">
  const stateText = document.getElementById("state");
  const fallback = document.getElementById("fallback");

  try {
    const status = await navigator.permissions.query({
      name: "notifications",
    });
    const show = () => {
      stateText.textContent = `Ruxsat: ${status.state}`;
    };
    status.addEventListener("change", show);
    show();
  } catch {
    stateText.textContent = "Ruxsat holatini bilib bo'lmadi";
  }

  function remind(text) {
    if (Notification.permission === "granted") {
      const title = `Eslatma: ${text}`;
      const note = new Notification(title, { tag: text });
      note.addEventListener("click", () => window.focus());
    } else {
      fallback.textContent = `Eslatma: ${text}`;
    }
  }

  const remindButton = document.getElementById("remind");
  remindButton.addEventListener("click", async () => {
    const text = document.getElementById("task").value.trim();
    const seconds = Number(document.getElementById("seconds").value);
    if (Notification.permission === "default") {
      fallback.textContent = "Eslatmani ekran burchagida ko'rsatish "
        + "uchun brauzer ruxsat so'raydi.";
      await Notification.requestPermission();
    }
    setTimeout(() => remind(text), seconds * 1000);
  });
</script>

Ruxsat berib va bermay sinab ko'ring. denied holatida ham eslatma yo'qolmaydi — sahifaning o'zida chiqadi. Sinash uchun ruxsatni manzil satridagi belgi → "Reset permission" bilan tiklang — Ruxsat: yozuvi change orqali o'zi yangilanadi.

bash
git add 13/07-eslatma/index.html
git commit -m "13/07: bildirishnoma eslatmasi tajribasi"

9. Real ishda

  • Xarita va yetkazib berish — Yandex Go, Uzum Tezkor, Express24: veb versiyalarda "Joylashuvimni aniqlash" tugmasi getCurrentPosition, kurer kuzatuvi — watchPosition yoki serverdan keladigan koordinatalar.
  • Bildirishnomalar — Gmail, Telegram Web, Slack: yangi xabar kelganda. Ko'pchiligi Push (Service Worker) ishlatadi — tab yopiq bo'lsa ham keladi.
  • Ruxsat UX'i — katta kompaniyalar ruxsat so'rovidan oldin o'z tushuntirish oynasini ko'rsatadi ("soft ask"). Bu rozilik foizini sezilarli oshiradi.
  • Huquqiy tomon — joylashuv shaxsiy ma'lumot. Ko'p mamlakatlarda (Yevropada GDPR, O'zbekistonda "Shaxsga doir ma'lumotlar to'g'risida"gi qonun) uni yig'ish va saqlash qoidalari bor.
  • Intervyu. "Permissions API nima qila oladi va nima qila olmaydi?", "Geolocation xato kodlari?", "Bildirishnoma ruxsatini qachon so'raysiz?" — frontend intervyularida uchraydi.

Xulosa

  • Ruxsat — har sayt uchun saqlanadigan qaror: prompt → granted / denied; denied dan sayt o'zi qaytara olmaydi.
  • navigator.permissions.query({ name }) — holatni o'qiydi va change hodisasini beradi; so'ramaydi.
  • Notification.requestPermission() → "granted" | "denied" | "default"; new Notification(sarlavha, { body, tag }) — sahifa ochiq turganda.
  • getCurrentPosition(ok, xato, { timeout }) va watchPosition / clearWatch; xato kodlari 1, 2, 3 — foydalanuvchi matnini code bo'yicha yozing.
  • So'rovni foydalanuvchi harakatida va o'z tushuntirishingizdan keyin qiling; har doim ruxsatsiz zaxira yo'li bo'lsin.

Keyingi dars: navigator va qurilma API'lari — tarmoq holati, til, to'liq ekran, ekran qulfi va "qaysi brauzer?" o'rniga "bu imkoniyat bormi?" deb so'rash.

Manbalar

  • W3C: "Permissions" — w3c.github.io/permissions
  • WHATWG: "Notifications API Standard" — notifications.spec.whatwg.org
  • W3C: "Geolocation" — w3c.github.io/geolocation
  • MDN: Permissions.query(), Notification, Geolocation — developer.mozilla.org
  • web.dev: "Permission UX" va "Best practices for notifications" — web.dev
  • Mozilla: "Restricting Notification Permission Prompts in Firefox" (2019-yil 4-noyabr, Firefox 72) — blog.mozilla.org
  • Chromium Blog: "Introducing quieter permission UI for notifications" (2020-yil 7-yanvar, Chrome 80) — blog.chromium.org
Ulashish:Telegram'da

Izohlar (0)

Izoh yozish uchun kiring.

  • Hozircha izoh yo'q. Birinchi bo'ling!
JavaScript Permissions, Notifications va Geolocation: ruxsat so'raydigan API'lar — IlmHamroh