Mundarija (41)
- Bu darsda
- 1. Nega bu kerak?
- 2. Web Components — uch texnologiya
- 2.1 Uch qism birga
- 2.2 Xonadon o'xshatishi
- 3. Custom element: o'z tegingiz
- 3.1 Nom qoidalari
- 3.2 Ro'yxatdan o'tmagan teg ham ishlaydi
- 3.3 :defined — ro'yxatdan o'tganmi?
- 4. Shadow DOM: yopiq ichki dunyo
- 4.1 Atamalar
- 4.2 Chegara nimani to'xtatadi
- 4.3 O'z ko'zingiz bilan: hamkor sahifasi
- 5. Declarative Shadow DOM
- 5.1 Sintaksis
- 5.2 Nega JavaScript'siz yozish kerak?
- 5.3 open va closed
- 5.4 closed — xavfsizlik emas
- 5.5 Qolgan uchta atribut
- 5.6 DSD faqat HTML tahlilida ishlaydi
- 6. Hodisalar chegaradan o'tganda
- 6.1 Retargeting
- 6.2 composed — chegaradan o'tadimi?
- 7. Foydalanish qulayligi
- 8. Qachon ishlatiladi?
- 8.1 Yaxshi tanlov
- 8.2 Har doim ham emas
- 9. Ko'p uchraydigan xatolar
- 9.1 Nomda defis yo'q
- 9.2 DSD'ni innerHTML bilan qo'shish
- 9.3 Bitta host'ga ikki template
- 9.4 Hamma elementga Shadow DOM berish mumkin deb o'ylash
- 9.5 closed ni himoya deb hisoblash
- 10. Mashqlar
- 1-mashq (oson): Nomlarni saralang
- 2-mashq (o'rta): Ish vaqti kartasi
- 3-mashq (qiyin): Nima nusxalanadi?
- 4-mashq: Amaliy tajriba — DevTools'da Shadow DOM
- 11. Real ishda
- Xulosa
- Manbalar
Web Components'ga kirish: custom element va Declarative Shadow DOM
Qisqacha: Web Components — brauzerning o'zida bor, freymvorksiz o'z HTML tegingizni yaratish texnologiyasi. U uch qismdan iborat: custom element (nomida defis bor o'z teg, masalan
<dish-card>), Shadow DOM (tegning tashqi sahifadan yopiq ichki dunyosi — stil va qidiruv u yerga kirmaydi) vatemplate/slot. Declarative Shadow DOM shu yopiq dunyoni JavaScript'siz, HTML'ning o'zida yozadi:<template shadowrootmode="open">.
Bu darsda
- Custom element nomi qachon to'g'ri, qachon xato ekanini bilasiz va
:definedholatini tushuntira olasiz. - Shadow DOM nima uchun kerakligini, host, shadow root va light DOM nimaligini ko'rsata olasiz.
- Declarative Shadow DOM'ni
shadowrootmode,shadowrootdelegatesfocus,shadowrootclonablevashadowrootserializableatributlari bilan yoza olasiz. - Shadow DOM ichidan chiqqan hodisa tashqarida qanday ko'rinishini bilasiz.
- Web Components qachon to'g'ri tanlov, qachon emasligini ayta olasiz.
Oldin bilishingiz kerak: template va slot, Kontent modeli, Holat pseudo-klasslari, Hodisa tarqalishi: capture va bubble.
1. Nega bu kerak?
O'tgan uch darsda brauzerning ilovaga oid imkoniyatlarini ko'rdik: Service Worker, kesh va PWA. Endi boshqa savol: sahifaning qismlarini qanday qilib bir marta yozib, ko'p joyda ishlatish mumkin?
Jasur aka «Bahor» oshxonasining menyu kartasini chiroyli qildirgan: rasm, nom, narx, «Savatga» tugmasi. Endi hamkor kafe ham shu kartani o'z saytiga qo'ymoqchi. Stajyor Sardor kartaning HTML va CSS kodini yuborib berdi.
Ertasi kuni hamkor qo'ng'iroq qildi: "Kartangiz buzilib chiqyapti". Sardor qarab ko'rdi. Hamkor saytida h3 { color: red; text-transform: uppercase; } qoidasi bor ekan. Taom nomi qizil va katta harflarga aylangan. Teskarisi ham bo'ldi: kartaning .price klassi hamkorning o'z .price klassini buzib yuborgan.
Muammo — CSS global. Kaskad darsida ko'rganimizdek, sahifadagi har bir qoida har bir elementga tegishi mumkin. CSS metodologiyalari va @scope bu muammoni kamaytiradi, lekin to'liq yopmaydi: hamkorning h3 qoidasi baribir kartaga yetib boradi.
Brauzerda buning uchun tayyor yechim bor. Kartani o'z tegiga aylantirish mumkin — <dish-card> ("taom kartasi"). Uning ichki tuzilmasi va stili alohida, yopiq "xonada" yashaydi. Tashqi h3 qoidasi u yerga kirmaydi, ichki .price esa tashqariga chiqmaydi. Bu texnologiyaning nomi — Web Components.
2. Web Components — uch texnologiya
2.1 Uch qism birga
Web Components (veb komponentlar) — bitta API emas, birgalikda ishlatiladigan uchta brauzer texnologiyasi:
| Qism | Nima qiladi | Qayerda o'rganamiz |
|---|---|---|
| Custom element | O'z tegingizni brauzerga tanitadi | shu dars + Web Components |
| Shadow DOM | Tegning ichini tashqi sahifadan yopadi | shu dars + stillash |
template va slot |
Qolip va to'ldiriladigan joylar | template va slot |
Bularning hech biri kutubxona emas. Hammasi brauzerda allaqachon bor, hech narsa o'rnatish shart emas. Yozgan tegingiz oddiy HTML sahifada ham, keyinroq o'rganadigan React loyihasida ham ishlaydi.
Bu dars — HTML tomoni: teg nomi, Shadow DOM tushunchasi va uni HTML'ning o'zida yozish. Tegga xatti-harakat beradigan JavaScript klassi — keyingi darsda.
2.2 Xonadon o'xshatishi
Ko'p qavatli uyni tasavvur qiling. Uyning umumiy yo'lagi bor — hamma ko'radi, hamma yuradi. Har bir xonadonning esa eshigi bor. Ichkarida egasi o'zi xohlagan rangga bo'yagan, o'z mebelini qo'ygan.
Yo'lakdagi farrosh yo'lakni oqqa bo'yasa, xonadonlar ichi oq bo'lib qolmaydi. Xonadon ichidagi qizil devor esa yo'lakka chiqmaydi. Lekin ba'zi narsalar eshikdan o'tadi: yo'lakdagi issiqlik (isitish) xonadonga ham kiradi.
Sahifa — yo'lak. Custom element — xonadon eshigi. Shadow DOM — xonadon ichi. "Issiqlik" esa CSS merosi: rang va shrift ichkariga meros bo'lib o'tadi. Buni pastda o'z ko'zingiz bilan ko'rasiz.
3. Custom element: o'z tegingiz
3.1 Nom qoidalari
Custom element (maxsus element) — siz o'ylab topgan va brauzerga ro'yxatdan o'tkazgan teg. Uning nomiga qat'iy qoidalar bor:
- Nomda kamida bitta defis (
-) bo'lishi shart:dish-card,bahor-cart,star-rating. - Nom kichik lotin harfi bilan boshlanadi. Katta harf ishlatilmaydi.
- Ba'zi defisli nomlar band — ular eski SVG va MathML teglari:
font-face,color-profile,annotation-xmlva yana bir nechtasi.
Nomlarni inglizcha tanlaymiz — Toza kod: nomlash darsidan beri hamma nomlar shunday. dish-card — "taom kartasi", bahor-cart — "«Bahor» savati", star-rating — "yulduzli baho". Keyingi darsda har tegga JavaScript klassi yoziladi va uning nomi teg bilan bir xil bo'ladi: dish-card → DishCard.
Nega defis? HTML standarti kelajakda yangi teglar qo'shadi, lekin ularning nomida hech qachon defis bo'lmaydi. Siz defisli nom tanlasangiz, ertaga brauzer xuddi shu nomli standart teg chiqarib, saytingizni buzib qo'ymaydi.
Ro'yxatdan o'tkazish JavaScript'da bitta qator — customElements.define(nom, klass). Klassning o'zini keyingi darsda yozamiz. Hozir faqat nom qoidasini tekshirib ko'raylik. Chrome 154 da noto'g'ri nom shunday xato beradi:
Uncaught SyntaxError: Failed to execute 'define' on 'CustomElementRegistry': "dishcard" is not a valid custom element nameTarjimasi: "CustomElementRegistry ning define metodini bajarib bo'lmadi: dishcard — yaroqli custom element nomi emas". CustomElementRegistry — brauzerdagi ro'yxat, unda qaysi nomga qaysi klass berilgani yoziladi. Dish-card (katta harf) va font-face (band nom) uchun ham aynan shu xato chiqadi.
Defis yo'q nom bilan sahifada teg yozsa bo'ladi, lekin brauzer uni noma'lum element deb oladi. Uni hech qachon "jonlantirib" bo'lmaydi.
Tekshirib ko'ring: Qaysi nomlar custom element uchun yaroqli?
menu,menu-list,Menu-List,bahor-2026,font-face.
Javob
Yaroqli — menu-list va bahor-2026 (kichik harf bilan boshlanadi, defis bor; raqam boshqa joyda bo'lishi mumkin). menu — defis yo'q. Menu-List — katta harf bor. font-face — band nom (eski SVG tegi).
3.2 Ro'yxatdan o'tmagan teg ham ishlaydi
Qiziq fakt: <dish-card> ni HTML'ga hali hech qanday JavaScript'siz yozsangiz ham, brauzer xato bermaydi. U tegni oddiy inline element kabi ko'rsatadi — xuddi span kabi. Ichidagi matn ekranda chiqadi.
Brauzer ichida esa farq bor. Chrome'da tekshirdik:
document.createElement("dishcard")→HTMLUnknownElement— "noma'lum element", u abadiy shunday qoladi.document.createElement("dish-card")→HTMLElement— "hali ro'yxatdan o'tmagan, lekin bo'lishi mumkin bo'lgan element".
Defisli teg kutib turadi. Skript keyinroq customElements.define("dish-card", ...) ni chaqirsa, sahifadagi hamma <dish-card> lar shu zahoti "jonlanadi". Bu jarayon upgrade (yangilash) deyiladi. Skript sahifa oxirida yoki defer bilan yuklansa ham, teglar o'z vaqtida yangilanadi.
3.3 :defined — ro'yxatdan o'tganmi?
Holat pseudo-klasslari darsida :defined ni tanigan edik. Endi uni amalda ko'ramiz. Ro'yxatdan o'tmagan custom element :defined emas, ro'yxatdan o'tgani — :defined. Oddiy HTML teglari (p, div) doim :defined.
Quyidagi oynada karta avval xira. Tugmani bosing:
<style>
body { font: 1rem/1.5 system-ui, sans-serif; margin: 1rem; }
dish-card { display: block; padding: 0.75rem;
border: 1px solid #c8d6c8; border-radius: 0.5rem; }
dish-card:not(:defined) { opacity: 0.35; }
button { font: inherit; padding: 0.4rem 0.8rem;
margin-top: 0.5rem; }
</style>
<dish-card>Osh — 35 000 so'm</dish-card>
<button id="define" type="button">Ro'yxatdan o'tkazish</button>
<script>
const card = document.querySelector("dish-card");
console.log("defined:", card.matches(":defined"));
document.getElementById("define").addEventListener("click", () => {
if (customElements.get("dish-card")) return;
customElements.define("dish-card", class extends HTMLElement {});
console.log("defined:", card.matches(":defined"));
});
</script>Konsolda:
defined: falseTugmani bosganingizda karta to'liq rangga kiradi va konsolga defined: true chiqadi. Biz buni bosib sinadik — aynan shunday.
Qatorma-qator:
dish-card:not(:defined)— "hali ro'yxatdan o'tmagan karta". Unga xiralik berdik.class extends HTMLElement {}— bo'sh klass. U hech narsa qilmaydi, lekin nomni ro'yxatga yozish uchun yetarli.extendsvaHTMLElementhaqida — keyingi darsda.customElements.get("dish-card")— shu nom allaqachon ro'yxatda bo'lsa, klassni qaytaradi. Ikkinchi martadefineqilish xato beradi, shuning uchun avval tekshirdik.
Real saytda bu holat bilan FOUC (flash of unstyled content — "bezaksiz kontent miltillashi") ning oldini olishadi. Skript yuklanguncha chala tegni yashirib turishadi: dish-card:not(:defined) { visibility: hidden; }.
Diqqat:
visibility: hiddenbilan ehtiyot bo'ling. Skript yuklanmasa (xato yoki sekin internet), kontent butunlay ko'rinmay qoladi. Shuning uchun quyidagi Declarative Shadow DOM ko'pincha yaxshiroq yo'l: u JavaScript'ni kutmaydi.
4. Shadow DOM: yopiq ichki dunyo
4.1 Atamalar
Shadow DOM (soya DOM) — elementga biriktirilgan, asosiy sahifadan ajratilgan alohida DOM daraxti. Uni xonadon o'xshatishi bilan nomlab olamiz. Asosiy so'z uchta:
- Host (xo'jayin element) — shadow daraxt biriktirilgan element, ya'ni xonadonning eshigi. Bizda —
<dish-card>. - Shadow root (soya ildizi) — xonadonning ichi boshlanadigan joy. DevTools'da u
#shadow-rootdeb ko'rinadi. Uning ichidagi hamma elementlar birgalikda shadow tree (soya daraxti) deyiladi. - Light DOM (yorug' DOM) — host'ning oddiy farzandlari, ya'ni sahifa muallifi teg ichiga yozgan kontent. Ular yo'lakda turadi, lekin slotlar orqali ichkarida ko'rinadi.
Ikki dunyo orasidagi devor va eshik — shadow boundary (soya chegarasi). Keyingi bo'limlarda "chegara" deganimiz — shu.
flowchart TD
D["document"] --> B["body"]
B --> H["dish-card<br/>(host)"]
H -. "shadow root" .-> S["#shadow-root"]
S --> A["article"]
A --> T["h3 → slot name"]
A --> SL["slot"]
H --> L1["span slot=name<br/>(light DOM)"]
H --> L2["p<br/>(light DOM)"]Nimaga qarang: host'dan ikki yo'l chiqyapti. Uzluksiz chiziqlar — oddiy farzandlar (light DOM), ular asosiy sahifaga tegishli. Uzuq chiziq — shadow root. Uning ichidagi article, h3 va slot sahifaning oddiy daraxtida yo'q.
4.2 Chegara nimani to'xtatadi
Shadow DOM uch narsani ajratadi. Buni Chrome'da birma-bir tekshirdik:
- Tashqi CSS ichkariga kirmaydi. Sahifadagi
h3 { color: red }shadow ichidagih3ga tegmaydi. - Ichki CSS tashqariga chiqmaydi. Shadow ichidagi
h3 { color: green }sahifaning boshqah3lariga ta'sir qilmaydi. - Qidiruv ichkariga kirmaydi.
document.querySelector("h3")shadow ichidagih3ni topmaydi. Shadow ichidagiidlar ham sahifaningidlari bilan to'qnashmaydi.
Bu xususiyat inkapsulyatsiya (encapsulation) deyiladi — Private # maydonlar darsidagi g'oyaning HTML va CSS'dagi ko'rinishi: ichki tuzilma tashqaridan yopiq.
Lekin bitta narsa o'tadi — meros. color, font-family, line-height kabi meros bo'ladigan xususiyatlar host'dan ichkariga o'tadi. Xonadon o'xshatishidagi "issiqlik" — shu.
4.3 O'z ko'zingiz bilan: hamkor sahifasi
Quyidagi oynada hamkor kafening "buzuvchi" stili bor: hamma h3 qizil va katta harfli, .price esa ko'k. Pastda ikki karta: biri oddiy HTML, ikkinchisi Shadow DOM bilan:
<style>
body { font: 1rem/1.5 Georgia, serif; color: #333; margin: 1rem; }
/* hamkor saytining global stillari */
h3 { color: #c0392b; text-transform: uppercase; }
.price { color: #1f4e9c; font-size: 2rem; }
</style>
<p>Oddiy karta:</p>
<div class="card">
<h3>Osh</h3>
<p class="price">35 000 so'm</p>
</div>
<p>Shadow DOM bilan:</p>
<dish-card>
<template shadowrootmode="open">
<style>
h3 { color: #2e7d32; margin: 0; }
.price { font-weight: 700; margin: 0; }
</style>
<h3>Osh</h3>
<p class="price">35 000 so'm</p>
</template>
</dish-card>
<script>
const pageCount = document.querySelectorAll("h3").length;
console.log("Sahifadagi h3:", pageCount);
const root = document.querySelector("dish-card").shadowRoot;
const innerCount = root.querySelectorAll("h3").length;
console.log("Shadow ichidagi h3:", innerCount);
</script>Konsolda:
Sahifadagi h3: 1
Shadow ichidagi h3: 1Nimaga qarang:
- Oddiy kartada sarlavha qizil va katta harfli, narx ko'k va katta. Hamkor stili uni "egallab oldi".
- Shadow kartada sarlavha yashil, oddiy harfli. Narx esa qalin, lekin ko'k ham, katta ham emas. Tashqi
h3va.priceqoidalari ichkariga kirmadi. - Shrift esa ikkala kartada bir xil — Georgia.
font-familybodydan meros bo'lib, chegaradan o'tdi. document.querySelectorAll("h3")faqat bittah3ni topdi — oddiy kartadagini. Shadow ichidagini topish uchun avvalshadowRootga kirish kerak.
Tekshirib ko'ring: Hamkor sahifasiga
dish-card { color: purple; }qoidasini qo'shsa, shadow kartadagi narx qanday rangda bo'ladi? Sarlavha-chi?
Javob
Narx — binafsha. .price ning ichki stilida color yo'q, u rangni meros oladi: host'ning rangi — binafsha, meros chegaradan o'tadi. Sarlavha esa yashil qoladi: ichki h3 { color: #2e7d32 } meros qiymatdan kuchli. Meros faqat element o'zi qiymat olmaganda ishlaydi (Meros darsidagi qoida).
5. Declarative Shadow DOM
5.1 Sintaksis
template va slot darsida bitta atributni qisqa ko'rgan edik. Endi to'liq:
<dish-card>
<template shadowrootmode="open">
<!-- shadow daraxt: stil va tuzilma -->
</template>
<!-- light DOM: sahifa muallifining kontenti -->
</dish-card>Declarative Shadow DOM (deklarativ Shadow DOM, DSD) — Shadow DOM'ni JavaScript'siz, HTML'ning o'zida yozish usuli. "Deklarativ" — "nima kerakligini aytaman, qanday qilishni brauzer biladi" degani.
Brauzer sahifani o'qiyotganda (parse qilayotganda) shadowrootmode atributli template ni ko'radi. U qolipni saqlab qo'ymaydi, darhol ota elementga shadow root yasaydi va qolip ichini o'sha yerga ko'chiradi. template tegining o'zi DOM'da qolmaydi.
Qo'llanishi: Chrome 111 (2023), Safari 16.4 (2023), Firefox 123 (2024-yil fevral). 2024-yil fevralidan Baseline, 2026-yil avgustidan esa widely available (web-features 3.40).
5.2 Nega JavaScript'siz yozish kerak?
Shadow DOM 2016-yildan beri bor, lekin faqat JavaScript bilan yaratilardi. Bu ikki muammo tug'dirardi:
- Kutish. JavaScript yuklanib, bajarilguncha kartaning ichi yo'q. Sekin internetda foydalanuvchi bo'sh joy yoki "sakrayotgan" sahifa ko'radi.
- Server tayyorlay olmaydi. Ko'p saytlarda HTML'ni server tayyorlab yuboradi — bu SSR (server-side rendering, "serverda chizish"). Server oddiy HTML matn yuboradi, unda JavaScript ishlamaydi. DSD'gacha Shadow DOM'ni HTML matnda ifodalashning yo'li yo'q edi.
DSD ikkalasini hal qiladi. HTML kelishi bilan karta to'liq tayyor, stili bilan. JavaScript keyin yetib kelib, unga faqat xatti-harakat qo'shadi: tugma bosilishi, hisob. Bu — progressive enhancement g'oyasi: avval ishlaydigan asos, keyin qo'shimcha imkoniyat.
5.3 open va closed
shadowrootmode ning ikki qiymati bor:
open— ochiq. Tashqi JavaScriptelement.shadowRootorqali ichkariga kira oladi. DevTools'da ham, testlarda ham qulay.closed— yopiq.element.shadowRootnullqaytaradi. Tashqi skript ichkariga oddiy yo'l bilan kira olmaydi.
Quyidagi oynada ikki teg: <open-card> ("ochiq karta") va <closed-card> ("yopiq karta"):
<open-card>
<template shadowrootmode="open"><p>Ochiq</p></template>
</open-card>
<closed-card>
<template shadowrootmode="closed"><p>Yopiq</p></template>
</closed-card>
<script>
const open = document.querySelector("open-card");
const closed = document.querySelector("closed-card");
console.log("ochiq:", open.shadowRoot.mode);
console.log("yopiq:", closed.shadowRoot);
</script>Konsolda:
ochiq: open
yopiq: nullIkkala karta ham ekranda ko'rinadi — closed ko'rinishga ta'sir qilmaydi. U faqat JavaScript'dan kirishni yopadi.
Bo'sh joyni to'ldiring: shadowrootmode="closed" bilan yozilgan kartaning shadowRoot xususiyati qaytaradi.
Qaysi birini tanlash kerak? Deyarli har doim open. Sabab keyingi bo'limda.
5.4 closed — xavfsizlik emas
closed ni ko'pincha "himoya" deb o'ylashadi. Bu xato. Sahifadagi har qanday skript — sizniki ham, reklama skripti ham — bitta sahifada, bitta huquq bilan ishlaydi. Yopiq rejimni aylanib o'tishning yo'llari bor: masalan, skript attachShadow metodini komponent yuklanishidan oldin almashtirib qo'yishi mumkin. DevTools'da esa yopiq daraxt ham bemalol ko'rinadi.
Shuning uchun closed maxfiy ma'lumotni (token, parol) yashirish uchun yaramaydi. U faqat "ichkariga tegmang, bu ichki tuzilma" degan kelishuv. Lit kabi mashhur kutubxonalar open ni standart qilib olgan. Sahifa xavfsizligi — Brauzer xavfsizlik modeli darsida, shu qismning oxirida.
5.5 Qolgan uchta atribut
template ga yana uchta mantiqiy (qiymatsiz) atribut qo'shish mumkin:
| Atribut | Nima qiladi |
|---|---|
shadowrootdelegatesfocus |
Host'ga fokus berilsa, ichidagi birinchi fokuslanadigan elementga o'tkazadi |
shadowrootclonable |
cloneNode(true) shadow daraxtni ham nusxalaydi |
shadowrootserializable |
getHTML() shadow daraxtni ham HTML matnga aylantira oladi |
Fokusni uzatish. «Bahor» qidiruv maydoni o'z tegi bo'lsin: <search-field> ("qidiruv maydoni"), ichida yozuv va input. Bobur faqat klaviatura bilan ishlaydi. Unga sahifadagi "Qidiruvga o'tish" havolasi kerak — havola host'ga fokus beradi. Atributsiz host'ning o'zi fokus ololmaydi, hech narsa bo'lmaydi. shadowrootdelegatesfocus bilan fokus darhol ichidagi input ga o'tadi:
<style>
body { font: 1rem/1.5 system-ui, sans-serif; margin: 1rem; }
search-field:focus-within { outline: 2px solid #2e7d32;
outline-offset: 4px; display: inline-block; }
button { font: inherit; padding: 0.4rem 0.8rem; margin-top: 1rem; }
</style>
<search-field id="search">
<template shadowrootmode="open" shadowrootdelegatesfocus>
<label>Taom qidirish: <input name="q"></label>
</template>
</search-field>
<br>
<button id="go" type="button">Qidiruvga o'tish</button>
<script>
const search = document.getElementById("search");
document.getElementById("go").addEventListener("click", () => {
search.focus();
console.log("Fokusda:", document.activeElement.localName);
const inner = search.shadowRoot.activeElement;
console.log("Ichkarida:", inner.localName);
});
</script>Tugmani bosing: kursor maydonga tushadi va ramka paydo bo'ladi. Konsolga ikki qator chiqadi — sinovimizda aynan shunday chiqdi:
Fokusda: search-field
Ichkarida: inputE'tibor bering: document.activeElement — host, ichidagi input emas. Tashqi dunyo ichkariga qaramaydi, u faqat "fokus shu tegda" deb biladi. Haqiqiy fokuslangan element — shadowRoot.activeElement da. Shu sabab host'ga :focus va :focus-within ham ishlaydi.
Nusxa va matn. Chrome'da tekshirdik: atributsiz cloneNode(true) qilingan host'ning shadowRoot i null — shadow daraxt nusxalanmaydi. getHTML() esa faqat { serializableShadowRoots: true } opsiyasi bilan va faqat shadowrootserializable belgilangan daraxtlarni matnga aylantiradi. Bu ikki atribut sahifani nusxalaydigan yoki saqlaydigan vositalar uchun kerak — oddiy saytda kam ishlatiladi.
5.6 DSD faqat HTML tahlilida ishlaydi
Muhim tuzoq. DSD faqat brauzer HTML'ni o'qiyotganda ishlaydi. innerHTML orqali qo'shsangiz, template oddiy, uxlab yotgan qolip bo'lib qoladi. Chrome 154 konsolda shunday ogohlantiradi:
Found declarative shadowrootmode attribute on a template, but declarative Shadow DOM is not being parsed. Use setHTMLUnsafe() or parseHTMLUnsafe() instead.Tarjimasi: "Qolipda deklarativ shadowrootmode atributi topildi, lekin deklarativ Shadow DOM tahlil qilinmayapti. O'rniga setHTMLUnsafe() yoki parseHTMLUnsafe() ni ishlating".
setHTMLUnsafe(matn) — innerHTML ning DSD'ni tushunadigan varianti. Nomidagi "Unsafe" ("xavfli") so'zi ogohlantirish: u ham innerHTML kabi matnni HTML qilib tahlil qiladi. Foydalanuvchi yozgan matnni unga berish — XSS eshigini ochish. Unga faqat o'zingiz yozgan, ishonchli HTML beriladi.
const box = document.querySelector("#menu");
const html = `<dish-card>
<template shadowrootmode="open"><b>Osh</b></template>
</dish-card>`;
box.innerHTML = html;
console.log(box.firstElementChild.shadowRoot); // null
box.setHTMLUnsafe(html);
console.log(box.firstElementChild.shadowRoot.mode); // openTekshirib ko'ring: Server HTML'ni yuboradi, unda
<template shadowrootmode="open">bor. Sahifa JavaScript'i o'chirilgan brauzerda ochildi. Karta stili bilan ko'rinadimi?
Javob
Ha. DSD — HTML tahlilining bir qismi, uning uchun JavaScript kerak emas. Shadow root, uning stili va slotlar JavaScript'siz ishlaydi. Faqat xatti-harakat (tugma bosilganda nimadir qilish) bo'lmaydi — u keyingi darsda yoziladigan klassda.
6. Hodisalar chegaradan o'tganda
6.1 Retargeting
Hodisa tarqalishi darsida composedPath() ni ko'rib, "composed so'zi Shadow DOM bilan bog'liq" degan edik. Endi sababini ko'ramiz.
Shadow ichidagi tugma bosildi. Hodisa yuqoriga ko'tariladi (bubble), chegaradan o'tib, document ga yetadi. Lekin tashqaridagi tinglovchi event.target da ichki tugmani ko'rmaydi. U host'ni ko'radi. Brauzer hodisaning nishonini chegarada "almashtiradi". Bu retargeting (nishonni qayta belgilash) deyiladi.
<dish-card>
<template shadowrootmode="open">
<button type="button">Savatga</button>
</template>
</dish-card>
<script>
document.addEventListener("click", (event) => {
const path = event.composedPath()
.map((node) => node.localName || node.nodeName || "window");
console.log("target:", event.target.localName);
console.log("yo'l:", path.join(" > "));
});
</script>«Savatga» tugmasini bosing. Biz sinovda bosganimizda konsolga shu chiqdi:
target: dish-card
yo'l: button > #document-fragment > dish-card > body > html > #document > windowtarget—dish-card. Tashqi kod ichki tuzilmani bilmasligi kerak — inkapsulyatsiya hodisalarda ham ishlaydi.composedPath()esa to'liq yo'lni beradi:buttondan boshlab, shadow root (#document-fragment), host va oxirigacha. Eng oxirgisiwindow— u DOM tuguni emas,localNameham,nodeNameham yo'q. Shuning uchun uni|| "window"bilan o'zimiz nomladik.
6.2 composed — chegaradan o'tadimi?
click, input, keydown kabi foydalanuvchi hodisalari chegaradan o'tadi — ular composed. O'zingiz yaratgan CustomEvent esa standart holatda o'tmaydi: u shadow root'da to'xtaydi. Chegaradan chiqarish uchun composed: true opsiyasi kerak. Buni keyingi darsda, <dish-card> «Savatga» bosilganda tashqariga xabar yuborganda yozamiz.
7. Foydalanish qulayligi
Shadow DOM bilan ishlaganda ikkita narsani yodda tuting. Malika ekran o'quvchi (NVDA) bilan ishlaydi.
Slotdagi kontent joyida o'qiladi. template va slot darsida aytgan edik: ekran o'quvchi light DOM kontentini slot turgan joyda o'qiydi. Malika ko'z bilan ko'radigan tartibni eshitadi. Bu yaxshi.
id havolalari chegaradan o'tmaydi. Sahifadagi <label for="name"> shadow ichidagi <input id="name"> ni topmaydi. Chrome'da tekshirdik: bunday label ning control xususiyati null. aria-labelledby va aria-describedby ham chegaradan o'tmaydi. Natijada Malika nomsiz maydonni eshitadi: "tahrirlash maydoni" — nima yozish kerakligi noma'lum.
Yechim — label va uning maydonini bir tomonda saqlash. Yuqoridagi qidiruv misolida label ham, input ham shadow ichida — shuning uchun ishlaydi.
8. Qachon ishlatiladi?
8.1 Yaxshi tanlov
- Dizayn tizimlari. Katta kompaniyada o'nlab sayt bor, ba'zisi bir texnologiyada, ba'zisi boshqasida. Tugma, karta, dialog bir xil bo'lishi kerak. Web Components har qanday sahifada ishlaydi. Google'ning Material Web, Adobe'ning Spectrum Web Components, Web Awesome (avvalgi nomi Shoelace) — shunday kutubxonalar.
- Boshqa saytga qo'yiladigan vidjetlar. Chat oynasi, to'lov tugmasi, xarita — hamkor saytining CSS'idan himoyalangan bo'lishi kerak. Bizning hamkor kafe muammosi — aynan shu.
- Kichik, mustaqil qismlar. GitHub sahifalaridagi vaqt ko'rsatkichi
<relative-time>("3 soat oldin") — custom element.
Ko'p jamoalar Web Components'ni qo'lda emas, Lit kutubxonasi bilan yozadi. Lit — Google jamoasi yaratgan kichik kutubxona. U Tagged template literals darsida ko'rgan html va css teglari ustiga qurilgan. Hozir bilish shart emas: avval qo'lda yozishni o'rganamiz, shunda Lit ichida nima bo'layotganini tushunasiz.
8.2 Har doim ham emas
- Butun ilova (ko'p sahifali, murakkab holatli) uchun ko'pincha React kabi freymvork qulayroq. React'ni 17-qismdan o'rganamiz.
- Oddiy, bitta saytdagi qism uchun Shadow DOM ortiqcha bo'lishi mumkin. Global stil kerak bo'lsa (sayt bo'ylab bir xil shrift va ranglar), yopiq dunyo xalaqit beradi.
| Vaziyat | Tanlov |
|---|---|
| Ko'p saytda bir xil komponent | Web Components |
| Begona saytga qo'yiladigan vidjet | Web Components + Shadow DOM |
| Bitta katta ilova | Freymvork (React — 17-qism) |
| Kichik sayt, bitta CSS fayl | Oddiy HTML + CSS |
9. Ko'p uchraydigan xatolar
9.1 Nomda defis yo'q
<dishcard> — ro'yxatdan o'tkazib bo'lmaydi, define is not a valid custom element name xatosini beradi. Tuzatish: dish-card. Loyiha prefiksi qulay: bahor-card, bahor-cart.
9.2 DSD'ni innerHTML bilan qo'shish
Shadow root yaratilmaydi, karta ichi bo'sh ko'rinadi, konsolda Found declarative shadowrootmode attribute on a template… ogohlantirishi chiqadi. Tuzatish: server yuborgan HTML'da yozing yoki ishonchli matn uchun setHTMLUnsafe().
9.3 Bitta host'ga ikki template
Element ichida ikkita shadowrootmode li template bo'lsa, faqat birinchisi shadow root bo'ladi. Ikkinchisi oddiy qolip bo'lib qoladi, Chrome konsolda ogohlantiradi: A second declarative shadow root cannot be created on a host. ("Host'da ikkinchi deklarativ shadow root yaratib bo'lmaydi"). Bir element — bitta shadow root.
9.4 Hamma elementga Shadow DOM berish mumkin deb o'ylash
Shadow root faqat custom element'larga va ba'zi oddiy teglarga biriktiriladi: div, span, p, article, section, header, footer, main, nav, aside, blockquote, body, h1–h6. input, a, img, button — yo'q. Chrome'da tekshirdik, input uchun: This element does not support attachShadow ("bu element attachShadow ni qo'llamaydi"). DSD'da esa sahifa to'xtamaydi: konsolga shu matn ogohlantirish bo'lib chiqadi, template esa oddiy qolip bo'lib qoladi.
9.5 closed ni himoya deb hisoblash
"Yopiq — demak, hech kim ko'rmaydi" — yo'q. «closed — xavfsizlik emas» bo'limiga qarang.
10. Mashqlar
1-mashq (oson): Nomlarni saralang
Har bir teg nomi uchun ayting: custom element bo'la oladimi? Bo'lmasa, nega?
bahor-menu, menu, Bahor-Menu, bahor-menu-2, color-profile, b-.
Yechim
bahor-menu— ha.menu— yo'q, defis yo'q (HTMLUnknownElementbo'lib qoladi).Bahor-Menu— yo'q, katta harf bor.bahor-menu-2— ha, defis ko'p bo'lishi mumkin, raqam ham.color-profile— yo'q, band nom (eski SVG tegi).b-— ha. G'alati ko'rinadi, lekin qoidaga mos: kichik harf bilan boshlanadi, defis bor. Real loyihada bunday nom tushunarsiz — tavsiya etilmaydi.
2-mashq (o'rta): Ish vaqti kartasi
«Bahor» ish vaqti uchun <bahor-hours> tegini DSD bilan yozing ("hours" — "ish soatlari"). Talablar:
- Shadow ichida:
h2(nomli slottitle, zaxirasi "Ish vaqti") vap(nomli slottime). - Ichki stil:
h2yashil (#2e7d32),pqalin. - Sahifada "buzuvchi" stil bo'lsin:
h2 { color: red; }. Kartaning sarlavhasi baribir yashil qolsin.
Ishora: template va slot darsidagi nomli slotlar; stil template ichidagi <style> da.
Yechim
<style>
body { font: 1rem/1.5 system-ui, sans-serif; margin: 1rem; }
h2 { color: red; }
</style>
<h2>Sahifa sarlavhasi</h2>
<bahor-hours>
<template shadowrootmode="open">
<style>
h2 { color: #2e7d32; margin: 0 0 0.25rem; }
p { font-weight: 700; margin: 0; }
</style>
<h2><slot name="title">Ish vaqti</slot></h2>
<p><slot name="time"></slot></p>
</template>
<span slot="time">Har kuni 07:00 – 23:00</span>
</bahor-hours>Sahifa sarlavhasi qizil, kartaniki yashil. Sarlavha slotiga hech narsa berilmadi — zaxira "Ish vaqti" ko'rinadi. Vaqt esa qalin: span light DOM'da, lekin u ichki p ichiga proyeksiya qilinadi va font-weight ni undan meros oladi.
3-mashq (qiyin): Nima nusxalanadi?
Ikki karta yarating: <a-card> (shadowrootmode="open" faqat) va <b-card> (shadowrootmode="open", shadowrootclonable, shadowrootserializable). Ichida — Osh matni. Har biri uchun konsolga chiqaring:
cloneNode(true)nusxasiningshadowRooti bormi (true/false)?getHTML({ serializableShadowRoots: true })natijasi.
Ishora: Boolean(qiymat) — null ni false ga aylantiradi.
Yechim
<a-card><template shadowrootmode="open">Osh</template></a-card>
<b-card><template shadowrootmode="open" shadowrootclonable
shadowrootserializable>Osh</template></b-card>
<script>
for (const name of ["a-card", "b-card"]) {
const card = document.querySelector(name);
const copy = card.cloneNode(true);
console.log(name, "nusxada shadow:", Boolean(copy.shadowRoot));
const html = card.getHTML({ serializableShadowRoots: true });
console.log(name, "getHTML:", JSON.stringify(html));
}
</script>Konsolda:
a-card nusxada shadow: false
a-card getHTML: ""
b-card nusxada shadow: true
b-card getHTML: "<template shadowrootmode=\"open\" shadowrootserializable=\"\" shadowrootclonable=\"\">Osh</template>"Birinchi karta uchun getHTML bo'sh satr qaytardi: light DOM'da hech narsa yo'q, shadow daraxt esa serializable emas. Ikkinchi karta esa o'zini qaytadan DSD ko'rinishida yozib berdi. Shu matnni serverga saqlab, keyin qayta ochsangiz, karta aynan tiklanadi. Biz JSON.stringify ni satr chegarasini aniq ko'rish uchun ishlatdik — u ichki qo'shtirnoqlarni \" qilib ko'rsatadi.
4-mashq: Amaliy tajriba — DevTools'da Shadow DOM
kurs/mashqlar/13/24-dsd/index.html faylini yarating. Unga 2-mashqdagi <bahor-hours> ni va «open va closed» bo'limidagi ikki kartani qo'ying. Live Server bilan oching, DevTools → Elements panelini oching.
- Kartani oching:
#shadow-root (open)qatorini toping. Ichidagih2ni tanlang — Styles panelida sahifadagih2 { color: red }qoidasi umuman yo'qligini ko'ring. closed-cardichida#shadow-root (closed)ko'rinadimi?- Konsolda
$0.shadowRootni yozing (avval Elements'da kartani tanlang).
Yechim
- Styles panelida faqat ichki qoida bor. Tashqi
h2qoidasi bu elementga tegishli emas — DevTools uni ko'rsatmaydi ham. - Ha, DevTools yopiq daraxtni ham ko'rsatadi:
#shadow-root (closed). Demak,closedfoydalanuvchidan hech narsani yashirmaydi — faqat sahifa skriptlaridan. $0— Elements'da tanlangan element. Ochiq karta uchun#shadow-root (open)obyekti chiqadi, yopiq karta uchunnull.
git add 13/24-dsd/index.html
git commit -m "13/24: Declarative Shadow DOM bilan ish vaqti kartasi"11. Real ishda
- Dizayn tizimlari. Material Web, Spectrum Web Components, Web Awesome — tayyor tugma, dialog, sana tanlagich teglari. Ular React, Vue yoki oddiy HTML loyihada bir xil ishlaydi.
- Vidjetlar. To'lov, chat, sharh vidjetlari begona saytga Shadow DOM bilan qo'yiladi — sayt CSS'i ularni buzmasligi uchun.
- SSR va DSD. Lit va boshqa vositalar serverda komponentni DSD ko'rinishida HTML'ga aylantiradi. Foydalanuvchi JavaScript yuklanmasidan oldin tayyor sahifani ko'radi.
- Intervyu. "Shadow DOM nima va u nimani izolyatsiya qiladi?", "
openvaclosedfarqi?", "Custom element nomida nega defis bor?" — frontend intervyularida uchraydi.
Xulosa
- Web Components — uch texnologiya: custom element, Shadow DOM,
template/slot. Hammasi brauzerda bor, kutubxonasiz. - Custom element nomi — kichik harf bilan boshlanadi va defis bor. Ro'yxatdan o'tmagan defisli teg kutib turadi,
definedan keyin "yangilanadi";:definedbuni ko'rsatadi. - Shadow DOM tashqi CSS'ni ichkariga kiritmaydi, ichkisini tashqariga chiqarmaydi,
querySelectorichkariga qaramaydi. Meros o'tadi. - Declarative Shadow DOM —
<template shadowrootmode="open">: shadow root JavaScript'siz, HTML tahlilida yaratiladi.innerHTMLbilan ishlamaydi. closed— himoya emas.shadowrootdelegatesfocus— fokusni ichkariga uzatadi.- Ichkaridan chiqqan hodisaning
targeti tashqarida — host (retargeting).
Keyingi dars: Web Components — <dish-card> ga JavaScript klassi bilan "jon" beramiz: hayot sikli callback'lari, atributlar, attachShadow, slotlar va tashqariga hodisa yuborish.
Manbalar
- HTML Living Standard: "Custom elements", "The template element" (
shadowrootmode) — html.spec.whatwg.org - DOM Standard: "Shadow tree", "Interface ShadowRoot" — dom.spec.whatwg.org
- MDN: "Using shadow DOM",
<template>:shadowrootmode— developer.mozilla.org - web.dev: "Declarative Shadow DOM" — web.dev
- web-features 3.40:
declarative-shadow-dom,shadow-dom,autonomous-custom-elements— github.com/web-platform-dx/web-features
Izohlar (0)
Izoh yozish uchun kiring.
- Hozircha izoh yo'q. Birinchi bo'ling!