Mundarija (35)
- Bu darsda
- 1. Nega bu kerak?
- 2. once — bir martalik tinglovchi
- 2.1 Bir marta ishlaydi va o'zi ketadi
- 2.2 Qachon qulay?
- 2.3 Tuzoq: once va preventDefault
- 3. capture — obyekt shaklida va olib tashlashda
- 3.1 true va { capture: true } — bir xil
- 3.2 Olib tashlashda capture muhim
- 4. passive — surishni kutdirmaslik
- 4.1 Muammo: brauzer tinglovchini kutadi
- 4.2 Va'dani buzsangiz
- 4.3 Brauzer o'zi yoqadigan joylar
- 4.4 Amaliy qoida
- 5. signal — tinglovchilarni guruh bilan olib tashlash
- 5.1 AbortController nima?
- 5.2 Amaliy holat: oyna ochilganda qo'shilgan tinglovchilar
- 5.3 Tuzoq: bekor qilingan signalni qayta ishlatish
- 5.4 Nega bu muhim: xotira
- 5.5 Signal boshqa joylarda ham
- 6. Opsiyalar jadvali
- 7. Ko'p uchraydigan xatolar
- 7.1 Opsiya nomida xato
- 7.2 once li tinglovchida preventDefault
- 7.3 Capture tinglovchisini capture'siz olib tashlash
- 7.4 passive da preventDefault — va hayrat
- 7.5 Bitta AbortController bilan ikki "sessiya"
- 8. Mashqlar
- 1-mashq (oson): Bir martalik kupon
- 2-mashq (o'rta): Nega olib tashlanmadi?
- 3-mashq (qiyin): Bron oynasi — hamma tinglovchi bitta pultda
- 4-mashq: Portfolio qadami — mavzu haqida bir martalik eslatma
- 9. Real ishda
- Xulosa
- Manbalar
addEventListener opsiyalari: once, passive, capture va signal bilan tozalash
Qisqacha: Tinglovchining xulqini
addEventListenerning uchinchi argumenti — opsiyalar obyekti boshqaradi. Bir martalik tinglovchi —{ once: true }: u ishlagach o'zi olib tashlanadi. Silliq surish uchun —{ passive: true }, ya'ni "preventDefaultchaqirmayman" degan va'da: brauzer sahifani kutmasdan suradi. Capture bosqichi —{ capture: true }. Ko'p tinglovchini birdan olib tashlash —{ signal }vaAbortController: bittaabort()hammasini tozalaydi.
Bu darsda
- Uchinchi argumentning ikki shaklini (
trueva obyekt) farqlay olasiz. - Bir martalik tinglovchini
oncebilan yozasiz va uningpreventDefaultbilan tuzog'ini bilasiz. passivenima uchun surishni silliq qilishini va brauzer uni qayerda o'zi yoqishini tushuntira olasiz.captureli tinglovchini to'g'ri olib tashlaysiz.AbortControllervasignalbilan bir guruh tinglovchini bitta chaqiruv bilan tozalaysiz.
Oldin bilishingiz kerak: Event delegation, addEventListener asoslari, Parametrda destructuring va options obyekti.
1. Nega bu kerak?
Hozirgacha addEventListener ga ikki argument berdik, hodisa tarqalishi darsida esa uchinchisini — true (capture) — ham ko'rdik. Lekin hayotda tinglovchidan yana ko'p narsa kerak bo'ladi:
- "Faqat bir marta".
addEventListenerasoslari darsidagi "bepul choy" aksiyasi uchun nomli funksiya, hisoblagich varemoveEventListeneryozdik. Oddiy talab uchun ancha kod. - "Surishni sekinlashtirma". Telefonda sahifani barmoq bilan surayotganda bitta noto'g'ri tinglovchi butun sahifani "tiqilib-tiqilib" harakatlantirishi mumkin.
- "Hammasini birdan olib tashla". «Tezkor bron» oynasi ochilganda
documentga ikki-uchta tinglovchi qo'shiladi: tashqariga bosish,Esctugmasi. Oyna yopilganda ularning hammasini olib tashlash kerak — har birini nomi bilan eslab.
Uchalasi ham uchinchi argument — opsiyalar obyekti bilan hal bo'ladi. Bu options obyekti darsidagi qolipning o'zi: funksiyaga sozlamalarni nomli kalitlar bilan beramiz.
element.addEventListener("click", funksiya, {
once: true, // bir marta ishlasin
passive: true, // preventDefault chaqirmayman
capture: true, // capture bosqichida tingla
signal, // shu signal bekor qilinsa — olib tashla
});Hammasi ixtiyoriy: kerakligini yozasiz, qolgani standart qiymatda (false, signal — yo'q).
2. once — bir martalik tinglovchi
2.1 Bir marta ishlaydi va o'zi ketadi
«Bahor»da yangi mehmonlarga sovg'a: birinchi bosishda "Ko'k choy — sovg'a!" chiqadi. Keyingi bosishlar hech narsa qilmaydi:
<button id="sovga" type="button">Sovg'ani olish</button>
<p id="holat"></p>
<script>
const tugma = document.getElementById("sovga");
tugma.addEventListener("click", () => {
document.getElementById("holat").textContent =
"Ko'k choy — sovg'a! Ofitsiantga ayting.";
console.log("Sovg'a berildi");
}, { once: true });
</script>Tugmani uch marta bosing. Konsolda "Sovg'a berildi" faqat bir marta chiqadi. Brauzer tinglovchini birinchi chaqiruvdan oldin o'zi olib tashladi.
addEventListener asoslari darsidagi qo'lda yechim bilan solishtiring: u yerda nomli funksiya kerak edi, chunki removeEventListener ga aynan o'sha funksiya kerak. Opsiya once bilan funksiyaga nom bermasa ham bo'ladi — olib tashlashni brauzer bajaradi.
2.2 Qachon qulay?
- Birinchi harakatga javob: sahifada birinchi bosish yoki tegishda biror narsani tayyorlash (masalan, og'ir vidjetni faqat kerak bo'lganda yuklash).
- Bir martalik xabar: "Yangi!" belgisi birinchi bosishda olib tashlanadi, maslahat bir marta ko'rsatiladi.
- Bitta javobni kutish: video yuklanib, o'ynashga tayyor bo'lgan zahoti bir martalik ish (Audio va video darsida).
Bitta muhim tafsilot: once faqat shu tinglovchini olib tashlaydi. Shu elementdagi boshqa tinglovchilar ishlashda davom etadi.
once li tinglovchini muddatidan oldin olib tashlash ham mumkin — oddiy removeEventListener bilan, agar funksiyaga nom bergan bo'lsangiz. Masalan, sovg'a aksiyasi soat 12:00 da tugasa, hech kim bosmagan bo'lsa ham tinglovchini olib tashlaysiz. Yoki, qulayrog'i, unga signal ham berasiz — buni pastda ko'ramiz. Opsiyalarni birga yozish mumkin: { once: true, signal }.
2.3 Tuzoq: once va preventDefault
Bron formasini "birinchi marta tekshiramiz" deb once bilan yozilgan kod:
<form id="bron" action="https://example.com/bron" method="post">
<label for="ism">Ism</label>
<input id="ism" name="ism">
<button>Yuborish</button>
</form>
<script>
const forma = document.getElementById("bron");
forma.addEventListener("submit", (event) => {
event.preventDefault();
console.log("Tekshirildi, yuborilmadi");
}, { once: true });
forma.addEventListener("submit", (event) => {
console.log("Bekor qilinganmi:", event.defaultPrevented);
});
</script>«Yuborish» ni bosing. Konsolda "Tekshirildi, yuborilmadi" va "Bekor qilinganmi: true". Ikkinchi marta bosing: faqat "Bekor qilinganmi: false". Birinchi tinglovchi yo'q — u bilan birga preventDefault ham ketdi. Forma endi haqiqatan yuboriladi. Bu oynada uni jonli oynaning o'z yordamchisi ushlab qoladi — pastda yashil " Forma tekshiruvdan o'tdi" yozuvi chiqadi (Forma qiymatlarini o'qish darsida batafsil). Haqiqiy saytda esa mehmon begona sahifaga o'tib ketardi.
Qoida: standart harakatni har safar bekor qiladigan tinglovchiga once qo'ymang. Bir martalik ish kerak bo'lsa, uni alohida tinglovchiga ajrating: preventDefault — doimiy tinglovchida, bir martalik xabar — once li tinglovchida. Portfolio qadamida xuddi shu g'oyani mavzu tugmasida qo'llaymiz.
Tekshirib ko'ring: Tugmada ikkita
clicktinglovchisi bor: A —{ once: true }bilan, B — oddiy. Tugma uch marta bosildi. A va B necha martadan ishlaydi?
Javob
A — bir marta, B — uch marta. once faqat o'zi qo'yilgan tinglovchiga tegishli. Birinchi bosishda ikkalasi ishlaydi (A — oxirgi marta), keyingi bosishlarda faqat B.
3. capture — obyekt shaklida va olib tashlashda
3.1 true va { capture: true } — bir xil
Hodisa tarqalishi darsida capture tinglovchisini ikki xil yozish mumkinligini ko'rdik. Uchinchi argument true/false bo'lsa — bu faqat capture. Obyekt bo'lsa — ichidagi kalitlar o'qiladi. Ikkinchisi aniqroq: true ni ko'rgan o'quvchi "bu nima edi?" deb o'ylaydi, { capture: true } esa o'zi aytib turadi. Uchinchi argument sifatida true yozish eski kodlarda keng tarqalgan — tanib turing.
3.2 Olib tashlashda capture muhim
Brauzer tinglovchilarni uchta belgi bilan ajratadi: hodisa turi, funksiya va capture. Boshqa opsiyalar (once, passive, signal) bu ro'yxatga kirmaydi. Demak capture bilan qo'shilgan tinglovchini olib tashlashda ham capture ko'rsatilishi shart:
<button id="osh" type="button">Osh</button>
<button id="olib" type="button">Tinglovchini olib tashlash</button>
<script>
const osh = document.getElementById("osh");
function kuzat() {
console.log("Osh bosildi");
}
osh.addEventListener("click", kuzat, { capture: true });
document.getElementById("olib").addEventListener("click", () => {
osh.removeEventListener("click", kuzat);
console.log("1-urinish: capture'siz");
});
</script>«Tinglovchini olib tashlash» ni bosing, keyin «Osh» ni bosing — "Osh bosildi" baribir chiqadi. removeEventListener("click", kuzat) capture'siz tinglovchini qidirdi, bunday yozuv yo'q edi — jim tugadi.
"Tahrirlab ko'rish" bilan olib tashlash qatorini osh.removeEventListener("click", kuzat, { capture: true }); ga almashtiring — endi «Osh» jim. Biz ikkalasini Chrome'da sinadik.
Bu addEventListener asoslari darsidagi "bir xil ko'rinadigan, lekin boshqa" tuzog'ining qarindoshi: funksiya bir xil, lekin capture belgisi boshqa — brauzer uchun bu boshqa tinglovchi.
4. passive — surishni kutdirmaslik
4.1 Muammo: brauzer tinglovchini kutadi
Telefonda sahifani barmoq bilan surasiz. Barmoq ekranga tekkanda touchstart, harakatlanganda touchmove hodisalari keladi. Kompyuterda sichqoncha g'ildiragi wheel hodisasini beradi. Bu hodisalarning standart harakati — sahifani surish.
Endi brauzerning holatini tasavvur qiling. Barmoq harakatlandi. Sahifani darhol surish kerak — lekin touchmove ga tinglovchi qo'yilgan. Balki u preventDefault() chaqirib, surishni bekor qilar? Brauzer buni bilmaydi. Shuning uchun u kutadi: avval tinglovchi ishlab bo'lsin, keyin surish haqida qaror. Tinglovchi 50 ms ishlasa, har harakat 50 ms kechikadi — sahifa "tiqilib" suriladi.
O'xshatish: taksi haydovchisi har chorrahada yo'lovchidan "bu yerda to'xtaymizmi?" deb so'rab to'xtaydi. Yo'lovchi "to'xtamaymiz, haydayvering" deb oldindan aytsa — yo'l silliq o'tadi.
{ passive: true } — aynan shu oldindan aytilgan va'da: "bu tinglovchi preventDefault chaqirmaydi". Brauzer kutmasdan suradi, tinglovchi esa yonma-yon ishlaydi.
4.2 Va'dani buzsangiz
passive tinglovchida preventDefault() chaqirilsa, brauzer uni e'tiborsiz qoldiradi. Quyidagi qutini kompyuterda sichqoncha g'ildiragi bilan suring. Chap qutida tinglovchi passive, o'ngdagisida — oddiy:
<style>
.quti { display: inline-block; width: 45%; height: 120px;
overflow: auto; border: 1px solid #2f6b3a; }
.ichi { height: 600px; padding: 0.5rem; }
</style>
<div class="quti" id="passiv">
<div class="ichi">Passive ↓</div>
</div>
<div class="quti" id="oddiy">
<div class="ichi">Oddiy ↓</div>
</div>
<script>
function toxtat(event) {
event.preventDefault();
const nom = event.currentTarget.id;
const bekor = event.defaultPrevented;
console.log(`${nom}: bekor qilindimi — ${bekor}`);
}
document.getElementById("passiv")
.addEventListener("wheel", toxtat, { passive: true });
document.getElementById("oddiy")
.addEventListener("wheel", toxtat);
</script>Chap qutini g'ildirak bilan suring: u suriladi, konsolda passiv: bekor qilindimi — false. O'ng qutini suring: u joyidan qimirlamaydi, konsolda oddiy: bekor qilindimi — true. Biz buni Chrome'da sinadik.
DevTools konsolida (jonli oyna konsolida emas — u faqat console.log larni ko'rsatadi) passive tinglovchi uchun Chrome ogohlantiradi:
Unable to preventDefault inside passive event listener invocation.Tarjimasi: "passive tinglovchi ichida preventDefault qilib bo'lmaydi". Bu — va'dani buzganingiz haqida eslatma.
4.3 Brauzer o'zi yoqadigan joylar
Ko'p saytlar touchstart va wheel ga tinglovchini document ga qo'yardi va sahifa sekinlashardi. Shuning uchun brauzerlar qoida kiritdi: window, document va body dagi touchstart, touchmove, wheel tinglovchilari sukut bo'yicha passive. Brauzerlar ma'lumotlar bazasiga (MDN BCD) ko'ra, touch uchun bu qoida hamma asosiy brauzerda bor — Baseline 2020-yil yanvaridan. wheel uchun esa faqat Chrome (73-versiyadan) va Firefox'da — Safari'da yo'q.
Amalda: document.addEventListener("wheel", ...) ichida preventDefault Chrome'da ishlamaydi. Biz sinadik: sahifa baribir surildi, konsolda esa boshqacha ogohlantirish chiqdi — Unable to preventDefault inside passive event listener due to target being treated as passive. Haqiqatan surishni to'xtatish kerak bo'lsa, { passive: false } ni aniq yozish kerak.
4.4 Amaliy qoida
- Surish bilan bog'liq hodisani (
touchstart,touchmove,wheel) faqat kuzatsangiz —{ passive: true }. - Surishni boshqarishingiz kerak bo'lsa (o'zingizning karusel yoki chizish maydoni) —
{ passive: false }vapreventDefault. Ko'pincha esa CSS'dagitouch-actionyaxshiroq yechim — Pointer hodisalari darsida. scrollhodisasining o'zigapassivekerak emas: u surilgandan keyin keladi va bekor qilinmaydi (Event obyekti darsidagicancelable: false).click,submitkabi hodisalargapassivening ta'siri yo'q — ular surishni kutdirmaydi.
Tekshirib ko'ring: Analitika kodi foydalanuvchi sahifani qanchalik surganini hisoblash uchun
documentgatouchmovetinglovchisi qo'yadi.passivehaqida nima deysiz?
Javob
Tinglovchi faqat kuzatadi, surishni to'xtatmaydi — demak { passive: true } yozish kerak. Aslida document da u baribir sukut bo'yicha passive, lekin aniq yozish yaxshi: kod o'quvchiga niyatingizni aytadi va tinglovchini boshqa elementga ko'chirsangiz ham to'g'ri qoladi.
5. signal — tinglovchilarni guruh bilan olib tashlash
5.1 AbortController nima?
AbortController (bekor qilish boshqaruvchisi) — brauzer beradigan tayyor obyekt, "bekor qilish pulti". Unda ikki narsa bor:
controller.signal— signal: pultga ulangan "chiroq". Uni istalgan joyga — tinglovchiga, keyinroq server so'roviga — berasiz.controller.abort()— pultdagi tugma. Bosilganda signal "bekor qilindi" holatiga o'tadi va unga ulangan hamma narsa to'xtaydi.
Uni new AbortController() bilan yaratasiz. new — tayyor qolipdan yangi obyekt yasaydigan kalit so'z; uni 08-qismda Intl bilan ko'rgan edingiz, to'liq ma'nosini klasslar mavzusida o'rganamiz.
Tinglovchiga signal berilsa, abort() chaqirilganda brauzer tinglovchini o'zi olib tashlaydi:
<button id="osh" type="button">Osh</button>
<button id="manti" type="button">Manti</button>
<button id="toxtat" type="button">Buyurtmani yopish</button>
<script>
const pult = new AbortController();
console.log(pult.signal);
for (const id of ["osh", "manti"]) {
document.getElementById(id).addEventListener("click", () => {
console.log(`${id} savatga qo'shildi`);
}, { signal: pult.signal });
}
document.getElementById("toxtat").addEventListener("click", () => {
pult.abort();
console.log("Bekor qilindimi:", pult.signal.aborted);
});
</script>Konsolda:
AbortSignal {}«Osh» va «Manti» ni bosing — ikkalasi ishlaydi. «Buyurtmani yopish» ni bosing — "Bekor qilindimi: true". Endi «Osh» va «Manti» jim: bitta abort() ikkala tinglovchini olib tashladi. Funksiyalarga nom ham kerak bo'lmadi.
Jonli konsol signalni AbortSignal {} deb qisqa ko'rsatadi. DevTools'da uni ochsangiz, ichida aborted: false va boshqa xususiyatlarni ko'rasiz.
Qo'llanishi: signal opsiyasi Baseline ma'lumotiga ko'ra 2021-yil sentyabridan hamma asosiy brauzerda (Chrome 90, Firefox 86, Safari 15), 2024-yil martidan esa "keng tarqalgan" (widely available) — bemalol ishlating.
5.2 Amaliy holat: oyna ochilganda qo'shilgan tinglovchilar
Signalning asl kuchi — vaqtinchalik tinglovchilarda. «Ish vaqti» paneli ochiq turganda ikki tinglovchi kerak: tashqariga bosganda va Esc bosilganda yopish. Panel yopilgach ular ortiqcha — document da qolib, har bosishda bekorga ishlaydi.
Esc ni bilish uchun keydown hodisasi va uning key xususiyati kerak ("Escape"). Klaviatura hodisalarini Klaviatura hodisalari darsida to'liq o'rganamiz, bu yerda bitta shart yetadi.
<button id="vaqt" type="button" aria-expanded="false">
Ish vaqti ▾
</button>
<div id="panel" hidden>Har kuni 7:00–23:00 · Bron: 10:00–22:00</div>
<p>Sahifaning boshqa qismi</p>
<script>
const tugma = document.getElementById("vaqt");
const panel = document.getElementById("panel");
let pult = null;
function yop() {
panel.hidden = true;
tugma.setAttribute("aria-expanded", "false");
pult.abort();
console.log("yopildi, tinglovchilar olib tashlandi");
}
function och() {
panel.hidden = false;
tugma.setAttribute("aria-expanded", "true");
pult = new AbortController();
document.addEventListener("click", (event) => {
if (!panel.contains(event.target)
&& !tugma.contains(event.target)) {
yop();
}
}, { signal: pult.signal });
document.addEventListener("keydown", (event) => {
if (event.key === "Escape") {
yop();
}
}, { signal: pult.signal });
console.log("ochildi");
}
tugma.addEventListener("click", () => {
if (panel.hidden) {
och();
} else {
yop();
}
});
</script>Sinab ko'ring:
- «Ish vaqti ▾» — "ochildi". "Sahifaning boshqa qismi" ni bosing — "yopildi, tinglovchilar olib tashlandi".
- Yana oching va
Escbosing — yana "yopildi…". - Panel yopiq turganda sahifani bosing yoki
Escbosing — konsol jim:documentda tinglovchi qolmagan.
Muhim tafsilot: har och() da yangi AbortController yaratiladi. Bir marta bekor qilingan signal abadiy "bekor qilingan" bo'lib qoladi. Unga tinglovchi bersangiz, brauzer uni umuman qo'shmaydi — keyingi bo'limdagi tuzoq.
Bu hodisa tarqalishi darsidagi «Tashqariga bosganda yopilsin» mashqining yaxshilangan varianti: u yerda document tinglovchisi doim turardi va har bosishda "panel ochiqmi?" deb tekshirardi. Endi u faqat panel ochiq paytda mavjud.
5.3 Tuzoq: bekor qilingan signalni qayta ishlatish
<button id="osh" type="button">Osh</button>
<script>
const pult = new AbortController();
pult.abort();
document.getElementById("osh").addEventListener("click", () => {
console.log("Osh bosildi");
}, { signal: pult.signal });
console.log("signal bekor qilinganmi:", pult.signal.aborted);
</script>Konsolda:
signal bekor qilinganmi: true«Osh» ni bosing — hech narsa. Xato xabari ham yo'q: signal allaqachon bekor qilingani uchun brauzer tinglovchini qo'shmadi. Real kodda bu "ikkinchi marta ochilgan oyna ishlamayapti" ko'rinishida chiqadi. Tuzatish: har yangi "sessiya" uchun yangi new AbortController().
5.4 Nega bu muhim: xotira
document va window sahifa yopilguncha yashaydi. Ularga qo'shilgan tinglovchi ham — siz olib tashlamaguncha. Oyna 20 marta ochilib-yopilsa va har safar ikki tinglovchi qolib ketsa — 40 ta ortiqcha funksiya har bosishda ishlaydi. Bu xotira sizishi (memory leak) — sahifa vaqt o'tishi bilan sekinlashadi. Uni topish va o'lchashni Xotira sizishlari darsida o'rganamiz. signal — eng oson oldini olish usuli.
5.5 Signal boshqa joylarda ham
Bitta signal nafaqat tinglovchilarni, balki server so'rovlarini (fetch) ham bekor qila oladi — buni 11-qismda ko'rasiz. Ikki yordamchi ham bor:
AbortSignal.timeout(5000)— 5 soniyadan keyin o'zi bekor bo'ladigan signal. Baseline ma'lumotiga ko'ra u 2024-yil apreldan hamma asosiy brauzerda, lekin hali "yangi" (newly available) — eski brauzerlarda yo'q.AbortSignal.any([a, b])— bir nechta signaldan istalgani bekor qilinsa, bekor bo'ladigan signal. 2026-yil sentyabridan "keng tarqalgan".
Ikkalasi asosan server so'rovlari bilan ishlatiladi — kerak bo'lganda ularga qaytamiz.
6. Opsiyalar jadvali
| Opsiya | Nima qiladi | Olib tashlashda muhimmi? |
|---|---|---|
once |
bir marta ishlab, o'zi ketadi | yo'q |
capture |
capture bosqichida tinglaydi | ha |
passive |
preventDefault chaqirmaslik va'dasi |
yo'q |
signal |
abort() da o'zi ketadi |
yo'q |
once, capture va passive Baseline ma'lumotiga ko'ra 2017-yildan hamma asosiy brauzerda.
flowchart TD
S{"Tinglovchi qachongacha kerak?"}
S -->|"bir marta"| O["once: true"]
S -->|"sahifa yashagancha"| D["opsiyasiz"]
S -->|"biror holat davomida"| G["signal + AbortController"]
P{"touch yoki wheel kuzatyapsizmi?"}
P -->|ha| PA["passive: true"]Diagrammani ikki savol bilan o'qing: tinglovchi qancha yashaydi va u surishga xalaqit beradimi.
7. Ko'p uchraydigan xatolar
7.1 Opsiya nomida xato
{ onse: true } yoki { Once: true } — xato chiqmaydi. Brauzer noma'lum kalitni jim o'tkazib yuboradi va tinglovchi oddiy bo'lib qoladi. Kutilgan xulq bo'lmasa, birinchi navbatda opsiya nomini tekshiring.
7.2 once li tinglovchida preventDefault
«once va preventDefault» bo'limidagi tuzoq: ikkinchi safar standart harakat qaytadi. Bekor qilish — doimiy tinglovchida.
7.3 Capture tinglovchisini capture'siz olib tashlash
removeEventListener ga ham { capture: true } kerak. once, passive, signal esa olib tashlashga ta'sir qilmaydi.
7.4 passive da preventDefault — va hayrat
"Surishni to'xtatdim, lekin sahifa suriladi" — tinglovchi passive (aniq yozilgan yoki document dagi sukut). DevTools konsolidagi ogohlantirishga qarang. Kerak bo'lsa — { passive: false }.
7.5 Bitta AbortController bilan ikki "sessiya"
Bekor qilingan signal qayta ishlamaydi. Har ochilishda — yangi controller.
8. Mashqlar
1-mashq (oson): Bir martalik kupon
«Kupon kodini ko'rsatish» tugmasi bosilganda #kod ga "BAHOR10" yozilsin va konsolga "Kupon ko'rsatildi" chiqsin — faqat birinchi bosishda. once ishlating.
<button id="kupon" type="button">Kupon kodini ko'rsatish</button>
<p id="kod"></p>
<script>
// shu yerga yozing
</script>Yechim
<button id="kupon" type="button">Kupon kodini ko'rsatish</button>
<p id="kod"></p>
<script>
const tugma = document.getElementById("kupon");
tugma.addEventListener("click", () => {
document.getElementById("kod").textContent = "BAHOR10";
console.log("Kupon ko'rsatildi");
}, { once: true });
</script>Bir necha marta bosing — "Kupon ko'rsatildi" bir marta. Kod ekranda qoladi — once faqat tinglovchini olib tashladi, sahifadagi matnga tegmadi. Kupon nomi — mashq uchun misol.
2-mashq (o'rta): Nega olib tashlanmadi?
Quyidagi kodda «Kuzatuvni to'xtatish» bosilgandan keyin ham «Osh» bosilganda konsolga yozilyapti. Xatoni toping va tuzating. Ikki xil yechim toping: biri removeEventListener bilan, biri signal bilan.
<button id="osh" type="button">Osh</button>
<button id="toxtat" type="button">Kuzatuvni to'xtatish</button>
<script>
const osh = document.getElementById("osh");
function kuzat() {
console.log("Kuzatildi: Osh");
}
osh.addEventListener("click", kuzat, true);
document.getElementById("toxtat").addEventListener("click", () => {
osh.removeEventListener("click", kuzat);
});
</script>Yechim
Tinglovchi true bilan — capture'da qo'shilgan, olib tashlash esa capture'siz. 1-yechim: osh.removeEventListener("click", kuzat, true);.
2-yechim — signal bilan; bu yerda capture belgisi ahamiyatsiz bo'lib qoladi:
<button id="osh" type="button">Osh</button>
<button id="toxtat" type="button">Kuzatuvni to'xtatish</button>
<script>
const osh = document.getElementById("osh");
const pult = new AbortController();
osh.addEventListener("click", () => {
console.log("Kuzatildi: Osh");
}, { capture: true, signal: pult.signal });
document.getElementById("toxtat").addEventListener("click", () => {
pult.abort();
});
</script>«Osh» — "Kuzatildi: Osh". «Kuzatuvni to'xtatish», keyin «Osh» — konsol jim. Signal bilan funksiya nomi ham, capture belgisini eslab qolish ham kerak emas — shuning uchun yangi kodda signal ko'pincha qulayroq.
3-mashq (qiyin): Bron oynasi — hamma tinglovchi bitta pultda
«Tezkor bron» paneli ochilganda uchta vaqtinchalik tinglovchi qo'shilsin: tashqariga bosish → yopish, Esc → yopish va paneldagi «−»/«+» tugmalari uchun delegation (Event delegation) — odamlar soni 1…20 oralig'ida. Yopilganda bitta abort() uchalasini olib tashlasin. Har ochilish — yangi AbortController. Odamlar soni konsolga Odam: 3 kabi chiqsin.
<button id="bron" type="button" aria-expanded="false">
Tezkor bron
</button>
<div id="panel" hidden>
Odam:
<button type="button" data-amal="ayir">−</button>
<output id="soni">2</output>
<button type="button" data-amal="qosh">+</button>
</div>
<p>Sahifaning boshqa qismi</p>
<script>
// shu yerga yozing
</script>Yechim
<button id="bron" type="button" aria-expanded="false">
Tezkor bron
</button>
<div id="panel" hidden>
Odam:
<button type="button" data-amal="ayir">−</button>
<output id="soni">2</output>
<button type="button" data-amal="qosh">+</button>
</div>
<p>Sahifaning boshqa qismi</p>
<script>
const tugma = document.getElementById("bron");
const panel = document.getElementById("panel");
const soni = document.getElementById("soni");
let odam = 2;
let pult = null;
function yop() {
panel.hidden = true;
tugma.setAttribute("aria-expanded", "false");
pult.abort();
}
function och() {
panel.hidden = false;
tugma.setAttribute("aria-expanded", "true");
pult = new AbortController();
const { signal } = pult;
document.addEventListener("click", (event) => {
const t = event.target;
if (!panel.contains(t) && !tugma.contains(t)) {
yop();
}
}, { signal });
document.addEventListener("keydown", (event) => {
if (event.key === "Escape") {
yop();
}
}, { signal });
panel.addEventListener("click", (event) => {
const amal = event.target.closest("[data-amal]");
if (amal === null) {
return;
}
if (amal.dataset.amal === "qosh" && odam < 20) {
odam++;
}
if (amal.dataset.amal === "ayir" && odam > 1) {
odam--;
}
soni.textContent = odam;
console.log(`Odam: ${odam}`);
}, { signal });
}
tugma.addEventListener("click", () => {
if (panel.hidden) {
och();
} else {
yop();
}
});
</script>«Tezkor bron» → + ni bosing — "Odam: 3". Tashqariga bosing — panel yopiladi. Qayta oching va + ni bosing — "Odam: 4", bir marta. Biz buni Chrome'da tekshirdik.
Oxirgi qadam — eng muhim tekshiruv. Agar paneldagi delegation tinglovchisi signalsiz qo'shilganida edi, har ochilishda yana bittasi qo'shilardi. Uchinchi ochilishda bitta + sonni uchga oshirib yuborardi — addEventListener asoslari darsidagi "tinglovchini ikki marta qo'shish" xatosi. Yozuv { signal } — destructuring bilan qisqa yozuv: pult.signal ni signal nomli o'zgaruvchiga oldik (Obyekt destructuring), { signal } esa { signal: signal } ning qisqasi (Shorthand).
4-mashq: Portfolio qadami — mavzu haqida bir martalik eslatma
addEventListener asoslari darsida portfolio'ga «Qorong'i rejim» tugmasi qo'shildi. Lekin tanlov hozircha saqlanmaydi: mehmon qorong'i rejimni yoqib, boshqa sahifaga o'tsa — sayt yana yorug'. Mehmon buni xato deb o'ylashi mumkin. Saqlashni 11-qismda localStorage bilan qo'shamiz. Ungacha mehmonni halol ogohlantiraylik — lekin faqat bir marta, har bosishda emas.
- Mavzu tugmasiga ikkinchi
clicktinglovchisini qo'shing,{ once: true }bilan. O'sha darsdagi tinglovchiga tegmang — u har bosishda ishlashi kerak. - Bu tinglovchi
pyaratsin: "Mavzu tanlovi hozircha faqat shu sahifada amal qiladi." Ungarole="status"qo'ying — Malika NVDA'da xabarni eshitsin (Jonli hududlar). - Paragrafni
mainning boshiga joylang (prepend— Element yaratish).
«once va preventDefault» bo'limidagi qoida shu yerda ham: doimiy ish (mavzuni almashtirish) va bir martalik ish (eslatma) — ikki alohida tinglovchida.
Yechim
asosiy.js dagi mavzu tugmasi qismiga (avvalgi tinglovchidan keyin):
// Bir martalik eslatma: tanlov hali saqlanmaydi (11-qismgacha)
temaTugma?.addEventListener("click", () => {
const eslatma = document.createElement("p");
eslatma.setAttribute("role", "status");
eslatma.textContent =
"Mavzu tanlovi hozircha faqat shu sahifada amal qiladi.";
document.querySelector("main")?.prepend(eslatma);
}, { once: true });Shunga o'xshash parchada sinang — tugmani uch marta bosing:
<header>
<button type="button" id="tema-tugma" aria-pressed="false">
Qorong'i rejim
</button>
</header>
<main>
<h1>Salom!</h1>
</main>
<script>
const temaTugma = document.getElementById("tema-tugma");
temaTugma?.addEventListener("click", () => {
const holati = temaTugma.getAttribute("aria-pressed");
const yoqilgan = holati === "true";
temaTugma.setAttribute("aria-pressed", String(!yoqilgan));
const soni = document.querySelectorAll("[role=status]").length;
console.log("aria-pressed:", !yoqilgan, "eslatmalar:", soni);
});
temaTugma?.addEventListener("click", () => {
const eslatma = document.createElement("p");
eslatma.setAttribute("role", "status");
eslatma.textContent =
"Mavzu tanlovi hozircha faqat shu sahifada amal qiladi.";
document.querySelector("main")?.prepend(eslatma);
}, { once: true });
</script>Konsolda ketma-ket: "aria-pressed: true eslatmalar: 0", "aria-pressed: false eslatmalar: 1", "aria-pressed: true eslatmalar: 1". Birinchi bosishda eslatma hali yo'q — doimiy tinglovchi oldin qo'shilgan, u birinchi ishlaydi. Keyin eslatma paydo bo'ladi va boshqa ko'paymaydi. Mavzu esa har bosishda almashadi. (Parchada temaniQoy o'rniga faqat aria-pressed almashtirildi va console.log qo'shildi — ular tekshirish uchun, portfolio'ga ko'chirmang.)
Tekshiring: bosh sahifada «Qorong'i rejim» ni ikki-uch marta bosing — eslatma bitta. Sahifani yangilang — tugma yana birinchi bosishda eslatma beradi: once faqat shu sahifa ochiq turgancha "eslab qoladi". 11-qismda tanlov saqlanadigan bo'lgach, bu eslatmani olib tashlaysiz. npm run check, commit: JS: mavzu tanlovi haqida bir martalik eslatma.
Portfolio'ning asosiy.js i endi hodisalar modulining hammasini ishlatadi: tinglovchi, hodisa obyekti va preventDefault, delegation, once. Aloqa formasini JavaScript bilan boshqarish — Forma qiymatlarini o'qish darsidan boshlab.
9. Real ishda
- Komponentlar va freymvorklar. Oyna, dropdown, tooltip — hammasi ochilganda
documentga tinglovchi qo'shadi va yopilganda tozalaydi. Zamonaviy kodda bu ko'pinchaAbortControllerbilan qilinadi. React kabi freymvorklarda (kursda alohida o'rganamiz) "komponent yo'qolganda tozalash" degan alohida bosqich bor — g'oyasi aynan shu. - Tezlik auditlari. Chrome DevTools'ning Performance paneli uzoq ishlaydigan
touchmove/wheeltinglovchilarini ko'rsatadi;passive— mobil tezlikni oshirishning eng arzon usullaridan biri (Tezlik darslarida). - Bir martalik ishlar —
once: birinchi o'zaro ta'sirda analitika yoki chat vidjetini yuklash; sahifa tezroq ochiladi, og'ir kod faqat kerak bo'lganda keladi. - Intervyuda: "Passive listener nima va nega kerak?", "Tinglovchini qanday olib tashlaysiz — uch usulini ayting" (
removeEventListener,once,signal).
Xulosa
- Uchinchi argument —
true(faqat capture) yoki opsiyalar obyekti:{ once, capture, passive, signal }. once— bir martalik tinglovchi; standart harakatni har safar bekor qiladigan tinglovchiga qo'ymang.passive: true— "preventDefaultchaqirmayman" va'dasi; brauzer surishni kutmaydi.document/windowdagitouchvawheeltinglovchilari sukut bo'yicha passive.- Olib tashlashda faqat
capturemuhim — u mos bo'lmasa,removeEventListenerjim ishlamaydi. signal+AbortController— bir guruh tinglovchini bittaabort()bilan olib tashlash; har "sessiya" uchun yangi controller.
Keyingi dars: Sichqoncha, pointer va touch hodisalari — sichqoncha, barmoq va qalamni bitta API bilan qamraymiz va touch-action bilan surishni boshqaramiz.
Manbalar
- MDN: "EventTarget: addEventListener() method" (options: once, passive, capture, signal) — developer.mozilla.org
- MDN: "AbortController", "AbortSignal", "AbortSignal: timeout() static method" — developer.mozilla.org
- web.dev / Chrome for Developers: "Improving scroll performance with passive event listeners" — developer.chrome.com
- web-features (Baseline):
events,aborting,abortsignal-timeout,abortsignal-any— github.com/web-platform-dx/web-features
Izohlar (0)
Izoh yozish uchun kiring.
- Hozircha izoh yo'q. Birinchi bo'ling!