Hujjat (DOM)

Node xossalari: tur, teg va tarkib

Endi DOM tugunlarini chuqurroq o'rganamiz. Ular turli sinflarga tegishli bo'lib, har biri o'z xossalariga ega. Ushbu darsda tugunlarning turini, teg nomini va ichki tarkibini qanday o'qish va yozishni ko'rib chiqamiz โ€” bu DOM bilan har kunlik ishning asosi.

DOM tugun sinflari

Turli DOM tugunlari turli xossalarga ega bo'lishi mumkin. Masalan, <a> tegiga mos element tuguni havola bilan bog'liq xossalarga (href), <input> tuguni esa kiritish bilan bog'liq xossalarga (value) ega. Matn tugunlari esa element tugunlaridan farqli. Ammo ular orasida umumiy narsalar ham bor, chunki barcha DOM tugunlari bitta ierarxiyani tashkil etadi.

Har bir DOM tuguni tegishli sinfga tegishli. Ierarxiyaning ildizi โ€” EventTarget. Undan Node, undan esa boshqalari meros oladi:

Ya'ni, biror aniq tugunning xossa va metodlari to'plami merosning natijasidir. Masalan, <input> uchun DOM obyekti quyidagilardan meros oladi (yuqoridan pastga): HTMLInputElement โ†’ HTMLElement โ†’ Element โ†’ Node โ†’ EventTarget โ†’ Object.

// input elementining sinfini va merosini ko'ramiz
let input = document.body.querySelector('input');
alert(input.constructor.name); // HTMLInputElement
Elementning barcha xossa va metodlarini ko'rish uchun uni konsolda console.dir(elem) orqali ko'ring. Yoki spetsifikatsiyalarni o'qing: DOM (dom.spec.whatwg.org) va HTML (html.spec.whatwg.org). Ular murakkab, ammo to'liq.

nodeType โ€” tugun turi

nodeType xossasi tugun turini bildiruvchi eski, ammo hali ishlatiladigan usul. U son qiymat qaytaradi:

<body>
  <script>
    let elem = document.body;

    // element tuguni ekanligini tekshiramiz
    alert(elem.nodeType); // 1

    // birinchi bolasi โ€” matn tuguni
    alert(elem.firstChild.nodeType); // 3

    // document obyekti uchun
    alert(document.nodeType); // 9
  </script>
</body>
Zamonaviy skriptlarda tugun turini instanceof yoki boshqa sinf-asosli tekshiruvlar orqali aniqlash mumkin. Ammo ba'zan nodeType eng sodda yo'l. Uni faqat o'qishimiz mumkin, o'zgartira olmaymiz.

nodeName va tagName

Tugunning teg nomini nodeName yoki tagName orqali o'qishimiz mumkin:

alert( document.body.nodeName ); // BODY
alert( document.body.tagName ); // BODY

Ular orasida farq bormi? Bor, va u nozik:

<body>
  <!-- izoh -->
  <script>
    // izoh uchun
    alert( document.body.firstChild.nodeName ); // #comment

    // document uchun
    alert( document.nodeName ); // #document
  </script>
</body>

Boshqacha aytganda, tagName faqat elementlarda ishlaydi (bu mantiqan to'g'ri โ€” faqat elementlarda teg bo'ladi), nodeName esa boshqa tugun turlari uchun ham biror-bir ma'lumot beradi.

Teg nomi HTML rejimida har doim BOSH harflarda qaytariladi. Brauzer HTML'ni katta-kichik harfga befarq qayta ishlaydi, ammo tagName va nodeName teglarni BOSH harfda beradi: BODY, DIV, INPUT va h.k. (XML rejimida esa asl holicha saqlanadi, ammo bu kamdan-kam uchraydi).

innerHTML โ€” tarkib

innerHTML xossasi elementning ichidagi HTML'ni satr sifatida olish yoki o'rnatish imkonini beradi. Bu eng ko'p ishlatiladigan xossalardan biri:

<body>
  <p>Paragraf</p>
  <div>DIV</div>

  <script>
    alert( document.body.innerHTML ); // joriy tarkibni o'qiydi
    document.body.innerHTML = 'Yangi BODY!'; // to'liq almashtiradi
  </script>
</body>

Biz ichiga noto'g'ri (buzuq) HTML kiritsak ham, brauzer uni o'qishda avtomatik tuzatadi:

document.body.innerHTML = '<b>test'; // yopilmagan teg
alert( document.body.innerHTML ); // <b>test</b> (avtomatik tuzatildi)
Diqqat: innerHTML += ... to'liq qayta yozadi! Bu jumla ichiga qo'shayotgandek ko'rinsa-da, aslida ikki qadamdan iborat: 1) eski tarkib o'chiriladi; 2) eski + yangi mazmun to'liq qayta yaratiladi. Bu esa muhim oqibatlarga ega: barcha rasmlar va boshqa resurslar qayta yuklanadi, oldingi tugunlarga bog'langan hodisa qayta ishlovchilari (event listeners) yo'qoladi, matn belgilangan (selection) bo'lsa โ€” u yo'qoladi.
// masalan, bu barcha rasmlarni qayta yuklaydi (miltillash bo'lishi mumkin):
chatDiv.innerHTML += '<div>Salom<img src='smile.gif'/> !</div>';
chatDiv.innerHTML += 'Xayr <img src='goodbye.jpg'/> :)';
Element ichiga HTML qo'shishning yaxshiroq yo'llari bor: elem.insertAdjacentHTML yoki append/prepend metodlari. Ular mavjud tarkibni qayta yozmaydi. Bular bilan keyingi darslarda tanishamiz.

outerHTML โ€” element bilan birga

outerHTML xossasi elementning to'liq HTML'ini โ€” elementning o'zini ham โ€” o'z ichiga oladi. Bu innerHTML ustiga element tegining o'zini qo'shgani bilan farq qiladi:

<div id="elem">Salom <b>Dunyo</b></div>

<script>
  alert(elem.outerHTML); // <div id="elem">Salom <b>Dunyo</b></div>
</script>
Muhim tuzoq: outerHTMLga yozish elementni O'ZGARTIRMAYDI, balki uni ALMASHTIRADI! Ya'ni yangi HTML DOM'da eski elementning o'rniga qo'yiladi, ammo bizning eski elem o'zgaruvchisi hali ham eski (endi DOM'da bo'lmagan) elementga ishora qilib qoladi.
<div>Salom, Dunyo!</div>

<script>
  let div = document.querySelector('div');

  // div.outerHTML'ni <p>...</p> bilan almashtiramiz
  div.outerHTML = '<p>Yangi element!</p>';

  // Diqqat! div hali ham eski qiymat!
  alert(div.outerHTML); // <div>Salom, Dunyo!</div> (!)
  // DOM'da esa endi <p> turibdi
</script>

Yuqoridagi kod ko'pchilikni chalkashtiradi. Sabab: div.outerHTML = ... divni DOM'dan olib tashladi va uning o'rniga yangi HTML qo'ydi. Ammo div o'zgaruvchisi hali ham eski elementga ishora qiladi. Yangi qo'shilgan tugunni olish uchun DOM'dan qayta topish kerak.

nodeValue va textContent

innerHTML faqat element tugunlarida ishlaydi. Boshqa tugun turlarida (matn, izoh) tarkibni o'qish uchun ularning "o'xshashlari" bor: nodeValue va data xossalari. Ular deyarli bir xil, amaliyotda kichik farqlari bilan:

<body>
  Salom
  <!-- Izoh -->
  <script>
    let text = document.body.firstChild;
    alert(text.data); // Salom

    let comment = text.nextSibling;
    alert(comment.data); // Izoh
  </script>
</body>

Matn tugunlari uchun ular ma'noli bo'ladi, izohlar uchun ham. Ammo element tugunlari uchun nega bizga ular kerak? Odatda kerak emas โ€” element uchun textContent foydaliroq.

textContent

textContent element ichidagi faqat matnni beradi โ€” barcha teglar tashlab yuboriladi, faqat matn qoladi:

<div id="news">
  <h1>Sarlavha!</h1>
  <p>Xabar matni.</p>
</div>

<script>
  // Sarlavha! Xabar matni. (teglarsiz, faqat matn)
  alert(news.textContent);
</script>

Amaliyotda uni o'qish kamdan-kam kerak, ammo yozish juda foydali. Chunki innerHTMLga matn yozsak, u HTML sifatida talqin qilinadi (teglar ishlaydi), textContentga yozsak esa matn AYNAN matn sifatida qo'yiladi โ€” barcha maxsus belgilar avtomatik xavfsiz "eskeyp" qilinadi:

<div id="elem1"></div>
<div id="elem2"></div>

<script>
  let name = prompt('Ismingizni kiriting?', '<b>Salbarchi</b>');

  // innerHTML: teg sifatida qo'yadi (xavfli!)
  elem1.innerHTML = name; // <b>Salbarchi</b> โ€” qalin

  // textContent: matn sifatida qo'yadi (xavfsiz)
  elem2.textContent = name; // <b>Salbarchi</b> โ€” matn holicha ko'rinadi
</script>
textContent foydalanuvchi kiritgan matnni chiqarishda XAVFSIZLIK uchun juda muhim! Agar foydalanuvchi <script>...</script> yoki boshqa xavfli HTML kiritsa, innerHTML uni bajarishi mumkin (XSS hujumi). textContent esa uni oddiy matn deb qabul qiladi va hech qanday teg ishlamaydi.

hidden xossasi

hidden atributi va DOM xossasi element ko'rinishmi yoki yashiringanmi ekanligini belgilaydi. Bu texnik jihatdan style="display:none" bilan bir xil ishlaydi, ammo yozishda qulayroq:

<div>Ikkalasi ham quyida yashiringan</div>

<div hidden>HTML atributi bilan</div>

<div id="elem">JavaScript xossasi bilan</div>

<script>
  elem.hidden = true; // endi bu div ko'rinmaydi
</script>

hidden xossasi bilan biz elementni miltillatib (blinking) ko'rsatishimiz ham mumkin:

<div id="elem">Miltillovchi element</div>

<script>
  setInterval(() => elem.hidden = !elem.hidden, 1000);
</script>

Boshqa xossalar

DOM elementlari yana ko'plab xossalarga ega. Xususan, aniq element sinfiga bog'liq bo'lganlari:

<input type="text" id="elem" value="qiymat">

<script>
  alert(elem.type);  // text
  alert(elem.id);    // elem
  alert(elem.value); // qiymat
</script>
Aksariyat standart HTML atributlari mos DOM xossasiga ega, biz ularni o'qiy va o'zgartira olamiz. Xossa va atribut orasidagi nozik farqlarni keyingi "Atributlar va xossalar" darsida batafsil ko'ramiz.

Xulosa