IlmHamroh
JavaScript Full-stack/9-qism. JavaScript brauzerda va debugging3/29-dars20 daqiqa
Mundarija (34)

JavaScript'da elementlarni tanlash: querySelector, getElementById, closest va matches

Qisqacha: Sahifadagi elementni JavaScript'da CSS selektor bilan topasiz. Birinchi mos element — document.querySelector(".narx") (yo'q bo'lsa null), hammasi — querySelectorAll, id bo'yicha — getElementById("menyu"). Yuqoriga qarab eng yaqin otani closest topadi, "shu selektorga mosmi?" degan savolga matches javob beradi. Eslab qoling: querySelectorAll ro'yxati statik, children va getElementsBy... esa jonli.

Bu darsda

  • getElementById, querySelector va querySelectorAll bilan istalgan elementni topa olasiz.
  • CSS darslaridagi selektorlarni JavaScript'da qayta ishlatasiz.
  • closest bilan elementning "oilasini" topib, matches bilan uni tekshira olasiz.
  • Statik NodeList va jonli HTMLCollection farqini va undan kelib chiqadigan tuzoqni bilasiz.
  • null va noto'g'ri selektor xatolarini tez topib tuzata olasiz.

Oldin bilishingiz kerak: DOM daraxti va tugun turlari, Asosiy selektorlar va kombinatorlar, Atribut selektorlari, ?? va optional chaining.

1. Nega bu kerak?

Ofitsiant Otabekka Jasur aka aytdi: "Doskadagi manti narxini 30 000 deb yangilab qo'y". Otabek avval menyu doskasida manti qatorini topadi, keyin narxni o'chirib yozadi. Topmasdan yozib bo'lmaydi.

JavaScript ham shunday. Sahifada matnni almashtirish, rangni o'zgartirish, tugmani yashirish — har ish bitta savoldan boshlanadi: qaysi element? O'tgan darsda DOM daraxti qanday tuzilganini ko'rdingiz. Endi undan kerakli tugunni tez va aniq topishni o'rganamiz.

Yaxshi xabar: siz buni allaqachon bilasiz. CSS'da .narx, #menyu li, a[href^="https://"] deb yozgansiz — bu selektorlar (selectors), ya'ni "qaysi elementlar" degan savolning javobi. Asosiy selektorlar darsida va'da qilingandek, JavaScript elementni aynan shu selektorlar bilan qidiradi. CSS'da selektor elementni bezash uchun edi, JavaScript'da — topish uchun.

Dars uch savolga javob beradi: bitta elementni qanday topish, ko'p elementni qanday topish va topilgan elementdan uning "qarindoshlari"ga qanday o'tish. Oxirida qaysi usul qachon qulayligini bitta diagrammada jamlaymiz.

Bu darsdagi misollar bitta «Bahor» menyusida:

html
<ul id="menyu">
  <li class="taom-karta">Osh <span class="narx">35 000</span></li>
  <li class="taom-karta yangi">
    Manti <span class="narx">30 000</span>
  </li>
  <li class="taom-karta">Lag'mon <span class="narx">28 000</span></li>
</ul>

2. getElementById — id bo'yicha

Eng eski va eng sodda usul. id sahifada takrorlanmas bo'lgani uchun natija — bitta element yoki null:

html
<ul id="menyu">
  <li class="taom-karta">Osh <span class="narx">35 000</span></li>
  <li class="taom-karta yangi">
    Manti <span class="narx">30 000</span>
  </li>
</ul>
<script>
  const menyu = document.getElementById("menyu");
  console.log(menyu);
  console.log(document.getElementById("bron"));
  console.log(document.getElementById("#menyu"));
</script>

Konsolda:

text
<ul id="menyu">…</ul>
null
null
  • Birinchi qator — topildi.
  • bron degan id yo'q — null. Xato emas, "topilmadi" degan javob (undefined va null darsida va'da qilingan holat shu).
  • Uchinchisi — eng ko'p uchraydigan xato: getElementById ga # yozilmaydi. Unga selektor emas, id ning o'zi beriladi. #menyu degan id yo'q — null.

Metod faqat document da bor: menyu.getElementById(...) deb element ichida qidirib bo'lmaydi.

Sahifada bir xil id ikki marta yozilgan bo'lsa-chi? HTML qoidasi buzilgan, lekin brauzer xato bermaydi: getElementById birinchisini qaytaradi, ikkinchisi esa JavaScript'dan "yashirinib" qoladi. Bunday xatoni HTML validatori darhol ushlaydi — npm run check shuning uchun ham foydali.

Tekshirib ko'ring: document.getElementById("Menyu") — sahifada id="menyu" bo'lsa, nima qaytaradi?

Javob

null. id katta-kichik harfni farqlaydi: Menyu va menyu — ikki xil nom. JavaScript'dagi o'zgaruvchi nomlari kabi.

3. querySelector — istalgan CSS selektor bilan

3.1 Birinchi mos element

querySelector istalgan CSS selektorni qabul qiladi va daraxtni yuqoridan pastga ko'rib chiqib, birinchi mos elementni qaytaradi:

html
<ul id="menyu">
  <li class="taom-karta">Osh <span class="narx">35 000</span></li>
  <li class="taom-karta yangi">
    Manti <span class="narx">30 000</span>
  </li>
  <li class="taom-karta">Lag'mon <span class="narx">28 000</span></li>
</ul>
<script>
  console.log(document.querySelector(".narx"));
  console.log(document.querySelector(".yangi .narx"));
  console.log(document.querySelector("li:last-child"));
  console.log(document.querySelector("#menyu"));
</script>

Konsolda:

text
<span class="narx">35 000</span>
<span class="narx">30 000</span>
<li class="taom-karta">…</li>
<ul id="menyu">…</ul>
  • .narx uchta, lekin qaytgani birinchisi — osh narxi.
  • .yangi .narx — kombinator: "yangi class'li element ichidagi narx" (Kombinatorlar).
  • li:last-child — Tuzilma pseudo-klasslari darsidagi selektor. Lag'mon qatori.
  • #menyu — getElementById("menyu") bilan bir xil natija. Bu yerda # kerak, chunki bu selektor.

Hayotdan o'xshatish: kutubxonachiga "qizil muqovali birinchi kitobni bering" deysiz. U javonlarni chapdan o'ngga ko'rib chiqadi va birinchi qizil kitobni olib keladi. Qolgan qizil kitoblarni ham olib kelmaydi.

3.2 Topilmasa — null, va undan himoya

Selektorga hech narsa mos kelmasa, null qaytadi. Keyingi qatorda uning textContent ini o'qisangiz — o'tgan darslardagi tanish xato. Element bo'lmasligi mumkin bo'lgan joyda ikki yo'l bor:

html
<p class="narx">35 000</p>
<script>
  const aksiya = document.querySelector(".aksiya");

  if (aksiya !== null) {
    console.log(aksiya.textContent);
  } else {
    console.log("Bugun aksiya yo'q");
  }

  console.log(document.querySelector(".aksiya")?.textContent);
</script>

Konsolda:

text
Bugun aksiya yo'q
undefined

Birinchisi — oddiy if. Ikkinchisi — optional chaining ?.: chap tomoni null bo'lsa, xato o'rniga undefined qaytaradi.

Qaysi biri qachon? Element albatta bo'lishi kerak bo'lsa (sahifaning asosiy tugmasi) — ?. qo'ymang. Xato darhol chiqsin, siz uni ko'rib tuzatasiz. Element bo'lmasligi mumkin bo'lsa (aksiya faqat juma kuni chiqadi) — if yoki ?..

Tekshirib ko'ring: Menyuda uchta .narx bor. document.querySelector(".narx").textContent nima qaytaradi?

Javob

"35 000" — faqat birinchi narx. querySelector doim bitta element qaytaradi. Hammasi kerak bo'lsa — querySelectorAll.

3.3 JavaScript uchun qanday selektor yaxshi?

CSS'da selektor faqat ko'rinishga ta'sir qiladi: xato bo'lsa, rang noto'g'ri chiqadi. JavaScript'da esa selektor buzilsa, tugma umuman ishlamay qoladi. Shuning uchun JavaScript selektori mustahkam bo'lishi kerak — dizayn o'zgarganda buzilmasin.

Selektor Baho Nega
#menyu yaxshi id takrorlanmas, kamdan-kam o'zgaradi
nav[aria-label="Asosiy menyu"] yaxshi Ma'noga bog'langan, qulaylik uchun ham kerak
.taom-karta odatda yaxshi Ma'noli nom, ko'rinishga emas
div > div:nth-child(3) span mo'rt Bitta div qo'shilsa — boshqa element topiladi

Qoida: elementni uning ma'nosi bo'yicha toping, sahifadagi joyi bo'yicha emas. Nodira dizaynni yangilab, kartalarni boshqa tartibda joylashi mumkin. .taom-karta baribir o'sha kartani topadi, div:nth-child(3) esa boshqasini.

Keyinroq JavaScript uchun maxsus atributlar ham ko'rasiz: data-* (Atributlar va dataset). Ular dizaynchi tegmaydigan, faqat dastur uchun nom — CSS class'larini o'zgartirish JavaScript'ni buzmaydi.

4. querySelectorAll — hammasi

4.1 NodeList ro'yxati

querySelectorAll mos kelgan barcha elementlarni NodeList deb ataladigan ro'yxatda qaytaradi:

html
<ul id="menyu">
  <li class="taom-karta">Osh <span class="narx">35 000</span></li>
  <li class="taom-karta yangi">
    Manti <span class="narx">30 000</span>
  </li>
  <li class="taom-karta">Lag'mon <span class="narx">28 000</span></li>
</ul>
<script>
  const kartalar = document.querySelectorAll(".taom-karta");
  console.log(kartalar);
  console.log(kartalar.length);
  console.log(kartalar[1]);

  for (const narx of document.querySelectorAll(".narx")) {
    console.log(narx.textContent);
  }

  console.log(document.querySelectorAll(".aksiya"));
</script>

Konsolda:

text
NodeList(3) [li.taom-karta, li.taom-karta.yangi, li.taom-karta]
3
<li class="taom-karta yangi">…</li>
35 000
30 000
28 000
NodeList(0) []

NodeList massivga juda o'xshaydi:

  • .length — nechta topildi;
  • [1] — indeks bo'yicha element;
  • for...of va forEach bilan aylanish mumkin (Massiv bo'ylab yurish).

Muhim farq: hech narsa topilmasa, null emas, bo'sh ro'yxat NodeList(0) [] qaytadi. Bo'sh ro'yxatni aylansangiz, sikl bir marta ham ishlamaydi — xato ham chiqmaydi.

4.2 NodeList — massiv emas

NodeList da forEach bor, lekin massivning ko'p metodlari yo'q. Ularni 10-qismda o'rganasiz, lekin farqni bugun bilib qo'ying. Haqiqiy massiv kerak bo'lsa — spread bilan aylantiring:

html
<a href="https://example.com/bron">Bron</a>
<a href="#menyu">Menyu</a>
<script>
  const havolalar = document.querySelectorAll("a");
  console.log(Array.isArray(havolalar), havolalar);

  const massiv = [...havolalar];
  console.log(Array.isArray(massiv), massiv);
</script>

Konsolda:

text
false NodeList(2) [a, a]
true (2) [a, a]

Ro'yxatdan faqat ba'zilarini tanlash kerak bo'lsa, bugungi bilim bilan — for...of va if. Masalan, faqat yangi taomlar — buni «matches» bo'limida ko'ramiz.

Tekshirib ko'ring: document.querySelectorAll("li")[5] — menyuda faqat uchta li bo'lsa, nima qaytaradi?

Javob

undefined — xuddi massivda mavjud bo'lmagan indeks kabi. Xato chiqmaydi, lekin keyingi .textContent da Cannot read properties of undefined xatosi chiqadi. E'tibor bering: bu safar null emas, undefined.

5. Element ichida qidirish

querySelector va querySelectorAll faqat document da emas, har qanday elementda bor. Shunda qidiruv faqat shu elementning ichida bo'ladi:

html
<article class="taom-karta">
  <h2>Osh</h2>
  <span class="narx">35 000</span>
</article>
<article class="taom-karta">
  <h2>Manti</h2>
  <span class="narx">30 000</span>
</article>
<script>
  const ikkinchi = document.querySelectorAll(".taom-karta")[1];
  console.log(ikkinchi.querySelector("h2").textContent);
  console.log(ikkinchi.querySelector(".narx").textContent);
</script>

Konsolda:

text
Manti
30 000

Bu juda ko'p ishlatiladi: avval "karta"ni topasiz, keyin uning ichidan kerakli bo'laklarni olasiz. Butun sahifadan .narx ni qidirsangiz, osh narxi chiqardi.

Bitta nozik tuzoq bor. Selektorning o'zi butun sahifaga nisbatan tekshiriladi, faqat natija element ichidan olinadi:

html
<ul id="menyu">
  <li>Osh</li>
  <li>Manti</li>
</ul>
<script>
  const menyu = document.getElementById("menyu");
  console.log(menyu.querySelectorAll("body li").length);
  console.log(menyu.querySelectorAll(":scope > li").length);
</script>

Konsolda:

text
2
2

Birinchi qatorga qarang: menyu ning ichida body yo'q, lekin body li baribir ikkita topdi. Brauzer har li ni tekshirdi: "uning ota-bobolari orasida body bormi?" — bor (menyudan tashqarida bo'lsa ham). Selektor "menyu ichidagi body ichidagi li" emas, "butun sahifada body ichida turgan va menyuning ichidagi li" deb o'qiladi.

:scope — "aynan shu element" degan maxsus pseudo-klass. :scope > li — "menyuning bevosita bolalari bo'lgan li lar". Element ichida qidirishda selektorni elementning o'ziga bog'lash kerak bo'lsa — :scope bilan boshlang.

Tekshirib ko'ring: Sahifada ikkita karta bor, ikkalasida ham h2. ikkinchi — ikkinchi karta. document.querySelector("h2") va ikkinchi.querySelector("h2") bir xil elementni beradimi?

Javob

Yo'q. document.querySelector("h2") butun sahifadan qidiradi va birinchi kartaning sarlavhasini topadi. ikkinchi.querySelector("h2") esa faqat ikkinchi kartaning ichidan qidiradi va uning o'z sarlavhasini beradi. Qidiruv qayerdan boshlansa, natija o'sha joyga bog'liq.

6. closest — yuqoriga qarab qidirish

querySelector pastga (ichkariga) qidiradi. Ba'zan teskari kerak bo'ladi: sizda narx span i bor, siz uning kartasini topmoqchisiz. closest(selektor) elementning o'zidan boshlab otasi, bobosi — tobora yuqoriga qarab boradi va birinchi mos kelganini qaytaradi:

html
<ul id="menyu">
  <li class="taom-karta">Osh <span class="narx">35 000</span></li>
  <li class="taom-karta yangi">
    Manti <span class="narx">30 000</span>
  </li>
</ul>
<script>
  const narx = document.querySelector(".yangi .narx");
  console.log(narx.closest(".taom-karta"));
  console.log(narx.closest("ul"));
  console.log(narx.closest("span"));
  console.log(narx.closest("form"));
</script>

Konsolda:

text
<li class="taom-karta yangi">…</li>
<ul id="menyu">…</ul>
<span class="narx">30 000</span>
null
  • .taom-karta — eng yaqin ota-karta, manti qatori.
  • ul — ikki qadam yuqoridagi menyu.
  • span — elementning o'zi. closest qidiruvni o'zidan boshlaydi.
  • form — yuqorida forma yo'q, null.

Hayotdan o'xshatish: siz katta bozordasiz va "eng yaqin chiqish qayerda?" deb so'raysiz. Siz turgan joyning o'zi chiqish bo'lsa — javob "shu yer". Bo'lmasa, yo'lak, keyin qator, keyin bo'lim bo'ylab chiqishni qidirasiz.

6.1 Amaliy holat: tugmadan kartaga

Har taom kartasida "Savatga" tugmasi bor. Tugma bosilganda qaysi taom tanlanganini bilish kerak. Tugmaning o'zida taom nomi yo'q — u kartada. Yo'l: tugmadan closest bilan kartaga chiqish, keyin karta ichidan nomni olish. Hozircha bosishsiz, ikkinchi tugmani qo'lda tanlab ko'ramiz:

html
<article class="taom-karta">
  <h2 class="taom-nomi">Osh</h2>
  <button type="button">Savatga</button>
</article>
<article class="taom-karta">
  <h2 class="taom-nomi">Manti</h2>
  <button type="button">Savatga</button>
</article>
<script>
  const tugma = document.querySelectorAll("button")[1];
  const karta = tugma.closest(".taom-karta");
  const nomi = karta.querySelector(".taom-nomi").textContent;
  console.log(`Savatga qo'shildi: ${nomi}`);
</script>

Konsolda:

text
Savatga qo'shildi: Manti

Kartalar 2 ta emas, 200 ta bo'lsa ham kod o'zgarmaydi: har tugma o'z kartasini topadi. Tugmani bosishga javob berishni addEventListener asoslari darsida qo'shamiz.

closest hodisalar darslarida eng ko'p ishlatiladigan metodlardan biriga aylanadi: foydalanuvchi kartaning qayeriga bosganini bilasiz, closest(".taom-karta") bilan esa kartaning o'zini topasiz.

7. matches — "shu selektorga mosmi?"

matches(selektor) elementni qidirmaydi — uni tekshiradi va true yoki false qaytaradi:

html
<ul id="menyu">
  <li class="taom-karta">Osh</li>
  <li class="taom-karta yangi">Manti</li>
  <li class="taom-karta yangi">Lag'mon</li>
</ul>
<script>
  const kartalar = document.querySelectorAll(".taom-karta");
  console.log(kartalar[0].matches(".yangi"));
  console.log(kartalar[1].matches("#menyu > .yangi"));

  let yangilar = 0;
  for (const karta of kartalar) {
    if (karta.matches(".yangi")) {
      yangilar++;
      console.log("Yangi:", karta.textContent);
    }
  }
  console.log(`Jami yangi taom: ${yangilar}`);
</script>

Konsolda:

text
false
true
Yangi: Manti
Yangi: Lag'mon
Jami yangi taom: 2

matches istalgan murakkab selektorni tushunadi, shuning uchun nodeName === "LI" kabi qo'lda tekshiruvlardan ishonchliroq (DOM daraxti darsidagi katta harf tuzog'ini eslang).

Tekshirib ko'ring: closest va matches farqi nima? el.closest(".x") === el qachon true bo'ladi?

Javob

matches faqat elementning o'zini tekshiradi va true/false qaytaradi. closest esa o'zidan yuqoriga qarab qidiradi va element (yoki null) qaytaradi. el.closest(".x") === el — element o'zi .x ga mos bo'lsa, ya'ni el.matches(".x") true bo'lsa.

8. Jonli va statik ro'yxatlar

8.1 Ikki xil ro'yxat

Elementlar to'plamini qaytaradigan eski metodlar ham bor:

Metod Nima qaytaradi Holati
querySelectorAll(".x") NodeList statik
getElementsByClassName("x") HTMLCollection jonli
getElementsByTagName("li") HTMLCollection jonli
element.children HTMLCollection jonli

Statik (static) ro'yxat — chaqirilgan paytdagi "surat". Keyin DOM o'zgarsa ham, ro'yxat o'zgarmaydi. Jonli (live) ro'yxat esa DOM'ga ulangan: element qo'shilsa yoki o'chirilsa, ro'yxat ham o'zi o'zgaradi.

Farqni ko'rish uchun bitta elementni o'chiramiz. element.remove() — elementni DOM'dan olib tashlaydi, uni Element yaratish va o'chirish darsida to'liq o'rganamiz:

html
<ul id="menyu">
  <li class="taom-karta">Osh</li>
  <li class="taom-karta">Manti</li>
  <li class="taom-karta">Lag'mon</li>
</ul>
<script>
  const surat = document.querySelectorAll(".taom-karta");
  const jonli = document.getElementsByClassName("taom-karta");
  console.log(surat.length, jonli.length);

  surat[2].remove();
  console.log(surat.length, jonli.length);
  console.log(jonli);
</script>

Konsolda:

text
3 3
3 2
HTMLCollection(2) [li.taom-karta, li.taom-karta]

Lag'mon sahifadan o'chdi. surat hali ham "3" deydi — u eski surat, uchinchi elementi endi sahifada yo'q. jonli esa o'zi 2 ga tushdi.

Hayotdan o'xshatish: querySelectorAll — zalning ertalabki fotosurati. HTMLCollection — zaldagi jonli kamera: mehmon chiqib ketsa, ekrandan ham yo'qoladi.

Bitta istisno: childNodes ham NodeList, lekin u jonli. Statiklik NodeList turiga emas, uni querySelectorAll qaytarganiga bog'liq.

8.2 Jonli ro'yxat va sikl — klassik tuzoq

Jonli ro'yxatni aylanayotib undan element o'chirsangiz, ro'yxat oyog'ingiz ostida qisqaradi:

html
<ul>
  <li class="eski">1</li>
  <li class="eski">2</li>
  <li class="eski">3</li>
  <li class="eski">4</li>
</ul>
<script>
  const eskilar = document.getElementsByClassName("eski");
  for (let i = 0; i < eskilar.length; i++) {
    eskilar[i].remove();
  }
  console.log(document.querySelectorAll("li").length);
</script>

Konsolda:

text
2

To'rttasini o'chirmoqchi edik — ikkitasi qoldi. Nega? i = 0 da birinchi li o'chdi, ro'yxat siljidi: endi "2" nolinchi o'rinda. i = 1 da biz "3" ni o'chirdik — "2" tashlab ketildi. Xato xabari yo'q — natija shunchaki noto'g'ri.

Tuzatish: statik ro'yxat bilan ishlang — document.querySelectorAll(".eski"). U surat, o'chirish uni siljitmaydi. Yangi kodda deyarli doim querySelectorAll ishlatiladi — tuzoqning o'zi kamroq.

Tekshirib ko'ring: Yuqoridagi tuzoqli kodda getElementsByClassName ni querySelectorAll(".eski") ga almashtiring ("Tahrirlab ko'rish"). Konsolda nima chiqadi va nega?

Javob

0 — hamma li o'chdi. Statik NodeList sikl davomida o'zgarmaydi: to'rtta element bor edi, sikl to'rt marta aylanib, har birini o'chirdi.

8.3 Eski to'plamlar va id nomidagi "global o'zgaruvchi"

Eski kodda yana bir necha tanlash usulini uchratasiz. Ular ishlaydi, lekin yangi kodda kam yoziladi:

html
<ul id="menyu"><li>Osh</li></ul>
<form name="bron" action="https://example.com/bron" method="post">
  <input name="ism" aria-label="Ism">
</form>
<a href="https://example.com/menyu">Menyu</a>
<script>
  console.log(document.forms.length, document.links.length);
  console.log(document.getElementsByName("ism"));
  console.log(menyu);
  console.log(window.menyu === document.getElementById("menyu"));
</script>

Konsolda:

text
1 1
NodeList(1) [input]
<ul id="menyu">…</ul>
true
  • document.forms, document.links, document.images — sahifadagi barcha formalar, havolalar, rasmlar. Formalarni forma darslarida o'rganamiz: u yerda formani querySelector bilan topib, maydonlarini forma.elements orqali olamiz.
  • getElementsByName("ism") — name atributi bo'yicha.
  • Uchinchi qator g'alati: biz menyu degan o'zgaruvchi e'lon qilmadik, lekin u bor! Brauzer id li har elementni window ga shu nom bilan qo'yib qo'yadi.

Bu "sovg'a"ga hech qachon tayanmang. id ni o'zgartirsangiz yoki shu nomli o'zgaruvchi e'lon qilsangiz, kod jimgina buziladi. Kod o'quvchisi menyu qayerdan kelganini ham tushunmaydi. Doim aniq yozing: const menyu = document.getElementById("menyu");.

9. Qaysi metodni tanlash kerak?

Diagrammani yuqoridan o'qing — har savolga "ha" yoki "yo'q":

flowchart TD
  S{"Nechta element kerak?"}
  S -->|bitta| B{"Yuqoriga qarab, ota-karta?"}
  S -->|hammasi| A["querySelectorAll — NodeList"]
  B -->|ha| C["closest"]
  B -->|yo'q| D{"Faqat id bo'yicha?"}
  D -->|ha| E["getElementById yoki querySelector"]
  D -->|yo'q| F["querySelector"]
  T{"Topmasdan, faqat tekshirish?"}
  T --> M["matches — true / false"]

Amaliy qoida: kundalik ishda querySelector, querySelectorAll va closest yetadi. getElementById — tez va aniq, id bo'lsa bemalol. getElementsBy... — asosan eski kodda uchratasiz.

Tezlik haqida: farq millisekundning mingdan biriga teng, ko'p hollarda sezilmaydi. Muhimi boshqa: bir elementni sikl ichida qayta-qayta qidirmang — bir marta topib, const ga saqlang.

js
// ❌ har aylanishda butun sahifa qayta qidiriladi
for (const nom of ["Osh", "Manti", "Lag'mon"]) {
  document.querySelector("#menyu").append(nom);
}

// ✅ bir marta topiladi, keyin tayyor o'zgaruvchi ishlatiladi
const menyu = document.querySelector("#menyu");
for (const nom of ["Osh", "Manti", "Lag'mon"]) {
  menyu.append(nom);
}

append — elementga matn yoki element qo'shadi, uni Element yaratish, joylash va o'chirish darsida o'rganamiz; bu yerda faqat tanlash qayerda turganiga qarang. Ikkinchi variant ham tezroq, ham o'qishga qulayroq: menyu nomi kodning qolgan qismida nima bilan ishlayotganimizni aytib turadi.

10. DevTools yordamchilari: $ va $$

Kompyuterdagi DevTools konsolida ikki qisqartma bor (DOM daraxti darsidagi $0 ning qarindoshlari):

js
$(".narx");      // document.querySelector(".narx") bilan bir xil
$$(".narx");     // querySelectorAll, lekin natija — haqiqiy massiv

Selektorni kodga yozishdan oldin konsolda $$ bilan sinash qulay: nechta topildi, aynan o'shalarmi?

Lekin ular faqat DevTools konsolining yordamchisi. Kodingizda yoki jonli oynada ishlatsangiz:

html
<p class="narx">35 000</p>
<script>
  const narx = $(".narx");
</script>

Konsolda:

text
Uncaught ReferenceError: $ is not defined

Tarjimasi: "$ aniqlanmagan". Eski kodda $(...) ni ko'p uchratasiz — bu jQuery degan kutubxona, u alohida ulanadi. Brauzerning o'zida $ yo'q.

11. Ko'p uchraydigan xatolar

11.1 Noto'g'ri selektor

id raqam bilan boshlangan bo'lsa (HTML'da bunga ruxsat bor), CSS selektor sifatida u yaroqsiz:

html
<p id="1-taom">Osh</p>
<script>
  console.log(document.getElementById("1-taom"));
  document.querySelector("#1-taom");
</script>

Konsolda:

text
<p id="1-taom">Osh</p>
Uncaught SyntaxError: Failed to execute 'querySelector' on 'Document': '#1-taom' is not a valid selector.

Tarjimasi: "Document da querySelector ni bajarib bo'lmadi: '#1-taom' — yaroqli selektor emas". null dan farqli, bu — haqiqiy xato: skript shu yerda to'xtaydi. Yopilmagan qavs (li:not(.yangi), ortiqcha belgi ham shu xatoni beradi. Tuzatish: id ni harf bilan boshlang (taom-1) yoki getElementById ishlating — u selektor emas, shuning uchun bunday cheklovi yo'q.

11.2 getElementById ga # yozish

getElementById("#menyu") → null. # faqat selektorda: querySelector("#menyu").

11.3 Bitta kutib, ro'yxat olish (va aksincha)

querySelectorAll(".narx").textContent → undefined: ro'yxatning textContent i yo'q, u har elementda. querySelector(".narx") esa faqat birinchisini beradi. Qaysi biri kerakligini oldindan hal qiling: bitta — querySelector, hammasi — querySelectorAll + sikl.

11.4 Skript elementdan oldin

Hamma tanlash metodlari faqat DOM'da bor elementni topadi. Skript defer siz <head> da bo'lsa — hammasi null yoki bo'sh ro'yxat (Brauzer muhiti darsidagi tuzoq).

11.5 Xato yo'q, lekin "boshqa" element topildi

Eng ayyor xato — kod ishlaydi, lekin noto'g'ri elementni o'zgartiradi. Masalan, querySelector("h2") menyu kartasidagi sarlavhani topadi deb o'ylaysiz, lekin sahifa tepasida aksiya bloki bor va uning ichida ham h2. Birinchi topilgani — aksiya sarlavhasi.

Bunday xato faqat sahifaga yangi blok qo'shilganda paydo bo'ladi — kecha ishlagan kod bugun "o'zi" buziladi. Ikki odat yordam beradi:

  • Selektorni aniqroq yozing: h2 emas, #menyu .taom-nomi.
  • Kerakli joy ichida qidiring: avval kartani yoki bo'limni toping, keyin uning ichidan.

Shubha bo'lsa, natijani konsolga chiqarib qarang: console.log(element) qaysi element topilganini darhol ko'rsatadi. DevTools konsolida bu elementning ustiga sichqonchani olib borsangiz, sahifada u yoritiladi.

12. Mashqlar

1-mashq (oson): Topib chiqaring

Quyidagi oynada konsolga chiqaring: (1) id="bron" li elementning matnini; (2) birinchi .narx ni; (3) nechta li borligini.

html
<ul id="menyu">
  <li>Osh <span class="narx">35 000</span></li>
  <li>Ko'k choy <span class="narx">5 000</span></li>
</ul>
<a id="bron" href="https://example.com/bron">Stol band qilish</a>
<script>
  // shu yerga yozing
</script>
Yechim
html
<ul id="menyu">
  <li>Osh <span class="narx">35 000</span></li>
  <li>Ko'k choy <span class="narx">5 000</span></li>
</ul>
<a id="bron" href="https://example.com/bron">Stol band qilish</a>
<script>
  console.log(document.getElementById("bron").textContent);
  console.log(document.querySelector(".narx"));
  console.log(document.querySelectorAll("li").length);
</script>

Konsolda:

text
Stol band qilish
<span class="narx">35 000</span>
2

2-mashq (o'rta): Buyurtma summasi

Mehmon buyurtmasi — #buyurtma ro'yxati. Har .narx ni o'qib, umumiy summani hisoblang: Jami: 68000 so'm.

Ishora: "35 000" — satr va ichida bo'sh joy. Avval replaceAll(" ", "") bilan bo'sh joyni olib tashlang (Matnni o'zgartirish), keyin Number() bilan songa aylantiring (Matn ↔ son). Qidiruvni #buyurtma ichida qiling — sahifada boshqa narxlar ham bor.

html
<p>Bugungi aksiya: <span class="narx">25 000</span></p>
<ul id="buyurtma">
  <li>Osh <span class="narx">35 000</span></li>
  <li>Lag'mon <span class="narx">28 000</span></li>
  <li>Ko'k choy <span class="narx">5 000</span></li>
</ul>
<script>
  // shu yerga yozing
</script>
Yechim
html
<p>Bugungi aksiya: <span class="narx">25 000</span></p>
<ul id="buyurtma">
  <li>Osh <span class="narx">35 000</span></li>
  <li>Lag'mon <span class="narx">28 000</span></li>
  <li>Ko'k choy <span class="narx">5 000</span></li>
</ul>
<script>
  const buyurtma = document.getElementById("buyurtma");
  let jami = 0;
  for (const narx of buyurtma.querySelectorAll(".narx")) {
    jami += Number(narx.textContent.replaceAll(" ", ""));
  }
  console.log(`Jami: ${jami} so'm`);
</script>

Konsolda:

text
Jami: 68000 so'm

document.querySelectorAll(".narx") yozsangiz, aksiya narxi ham qo'shilib, 93000 chiqardi. Element ichida qidirish aynan shunday xatolardan saqlaydi. Pulni chiroyli yozish (68 000) — 08-qismdagi somYoz funksiyasi bilan.

3-mashq (qiyin): Achchiq taomlar

Har .narx uchun uning kartasini closest bilan toping. Karta .achchiq bo'lsa (matches), konsolga taom nomini (.taom-nomi) chiqaring. Oxirida achchiq taomlar sonini chiqaring. Kutilgan natija: Achchiq: Lag'mon, Achchiq: Shashlik, Jami: 2.

html
<ul id="menyu">
  <li class="taom-karta">
    <span class="taom-nomi">Osh</span>
    <span class="narx">35 000</span>
  </li>
  <li class="taom-karta achchiq">
    <span class="taom-nomi">Lag'mon</span>
    <span class="narx">28 000</span>
  </li>
  <li class="taom-karta achchiq">
    <span class="taom-nomi">Shashlik</span>
    <span class="narx">18 000</span>
  </li>
</ul>
<script>
  // shu yerga yozing
</script>
Yechim
html
<ul id="menyu">
  <li class="taom-karta">
    <span class="taom-nomi">Osh</span>
    <span class="narx">35 000</span>
  </li>
  <li class="taom-karta achchiq">
    <span class="taom-nomi">Lag'mon</span>
    <span class="narx">28 000</span>
  </li>
  <li class="taom-karta achchiq">
    <span class="taom-nomi">Shashlik</span>
    <span class="narx">18 000</span>
  </li>
</ul>
<script>
  let soni = 0;
  for (const narx of document.querySelectorAll(".narx")) {
    const karta = narx.closest(".taom-karta");
    if (karta.matches(".achchiq")) {
      const nomi = karta.querySelector(".taom-nomi").textContent;
      console.log(`Achchiq: ${nomi}`);
      soni++;
    }
  }
  console.log(`Jami: ${soni}`);
</script>

Konsolda:

text
Achchiq: Lag'mon
Achchiq: Shashlik
Jami: 2

Uch harakat birga: closest — yuqoriga (narxdan kartaga), matches — tekshirish, querySelector — karta ichida pastga (nomga). Hodisalar darslarida aynan shu "yuqoriga chiq — tekshir — pastga tush" qolipi har kuni kerak bo'ladi. Shashlik va uning narxi — shu mashq uchun misol, «Bahor» menyusiga kirmaydi.

4-mashq: Portfolio qadami — menyuni topish

Keyingi darslarda portfolio'ga tema tugmasi va mobil menyu uchun kod qo'shiladi. Ular uchun avval kerakli elementlarni topib olish kerak. asosiy.js ga menyuniTop funksiyasini qo'shing:

  1. nav[aria-label="Asosiy menyu"] ni toping (Atribut selektorlari).
  2. Menyu bo'lmasa (404.html da menyu yo'q) — funksiya hech narsa qilmay tugasin (erta qaytish).
  3. Menyudagi havolalar sonini va joriy sahifa havolasining ([aria-current="page"]) matnini konsolga chiqaring. Joriy havola topilmasa, ?. bilan undefined chiqsin, xato emas.
Yechim

asosiy.js ga (o'tgan darsdagi sahifaniTekshir dan keyin):

js
function menyuniTop() {
  const menyu = document.querySelector(
    'nav[aria-label="Asosiy menyu"]',
  );
  if (menyu === null) {
    return;
  }
  const havolalar = menyu.querySelectorAll("a");
  const joriy = menyu.querySelector('[aria-current="page"]');
  console.log(`Menyuda ${havolalar.length} ta havola`);
  console.log(`Joriy sahifa: ${joriy?.textContent}`);
}

menyuniTop();

Natijani shu menyuga o'xshash parchada tekshiring:

html
<nav aria-label="Asosiy menyu">
  <ul>
    <li><a href="./">Bosh sahifa</a></li>
    <li><a href="loyihalar/" aria-current="page">Loyihalar</a></li>
    <li><a href="aloqa/">Aloqa</a></li>
  </ul>
</nav>
<script>
  function menyuniTop() {
    const menyu = document.querySelector(
      'nav[aria-label="Asosiy menyu"]',
    );
    if (menyu === null) {
      return;
    }
    const havolalar = menyu.querySelectorAll("a");
    const joriy = menyu.querySelector('[aria-current="page"]');
    console.log(`Menyuda ${havolalar.length} ta havola`);
    console.log(`Joriy sahifa: ${joriy?.textContent}`);
  }
  menyuniTop();
</script>

Konsolda:

text
Menyuda 3 ta havola
Joriy sahifa: Loyihalar

Sizning portfolio'ngizda havolalar soni va nomlari boshqacha bo'lishi mumkin. 404.html da konsolda faqat "asosiy.js ulandi" chiqadi — menyu yo'q, funksiya jim tugadi. Selektordagi qo'shtirnoq: tashqisi bitta ('), ichkisi qo'sh (") — ikkalasi bir xil bo'lsa, satr erta yopilib qolardi.

Commit: JS: asosiy menyuni topish funksiyasini qo'sh.

13. Real ishda

  • Har bir interaktiv sahifa tanlashdan boshlanadi: forma, tugma, modal oyna — avval querySelector, keyin ish.
  • Avtomatik testlar (kursda alohida o'rganamiz) elementni ham shunday topadi: Playwright va Testing Library'da page.locator(".narx") yoki "tugmani matni bo'yicha top" degan buyruqlar — aslida shu g'oya.
  • Selektor sifati — kod sifati. div > div:nth-child(3) span kabi uzun selektor dizayn o'zgarishi bilan buziladi. Real loyihada JavaScript uchun aniq nomlar (id, ma'noli class, keyinroq data-* atributlari — dataset) ishlatiladi.
  • Brauzer kengaytmalari va skriptlar. Telegram Web yoki marketpleysdan narxlarni yig'adigan kichik vositalar ham sahifadan ma'lumotni aynan querySelectorAll bilan oladi. Sayt dizayni o'zgarsa, ular birinchi bo'lib buziladi — mo'rt selektor muammosining eng yorqin namunasi.
  • Freymvorklarda (React va boshqalar — kursda alohida o'rganamiz) elementni qo'lda kamroq tanlaysiz, lekin forma maydoniga fokus berish yoki tashqi kutubxonani ulash uchun baribir kerak bo'ladi.
  • Intervyuda: "querySelectorAll va getElementsByClassName farqi?" — javob: statik NodeList va jonli HTMLCollection.

Xulosa

  • getElementById("id") — # siz, bitta element yoki null.
  • querySelector(selektor) — birinchi mos element yoki null; querySelectorAll — hammasi, statik NodeList (topilmasa — bo'sh ro'yxat).
  • Element ichida qidirish: karta.querySelector(...); selektorni elementga bog'lash — :scope.
  • closest — o'zidan yuqoriga qarab qidiradi; matches — true/false bilan tekshiradi.
  • HTMLCollection va childNodes — jonli; jonli ro'yxatni aylanib o'chirmang.
  • Noto'g'ri selektor — SyntaxError, topilmagan element — null: ikkalasini farqlang.

Keyingi dars: Kontentni o'zgartirish va XSS xavfi — topgan elementingizga yangi matn yozamiz va innerHTML qanday qilib saytni hujumchiga ochib berishini ko'ramiz.

Manbalar

  • MDN: "Document: querySelector() method", "Element: closest() method", "Element: matches() method" — developer.mozilla.org
  • MDN: "NodeList", "HTMLCollection" — developer.mozilla.org
  • MDN: ":scope" — developer.mozilla.org
  • Chrome DevTools: "Console Utilities API reference" ($, $$) — developer.chrome.com
Ulashish:Telegram'da

Izohlar (0)

Izoh yozish uchun kiring.

  • Hozircha izoh yo'q. Birinchi bo'ling!
JavaScript'da elementlarni tanlash: querySelector, getElementById, closest va matches — IlmHamroh