WisarWisar
Dasturlash kitobi/2-QISM — JavaScript11 daqiqa

2.16-bob: DOM — tanlash, o'zgartirish, CRUD, events, delegation, bubbling

2-QISM — JavaScript (0 dan chuqurgacha) · 16-mavzu


1. Kirish va motivatsiya

Bu — JavaScript'ni sahifaga ulovchi bob. Hozirgacha JS mantiqini (o'zgaruvchi, funksiya, massiv...) o'rgandik, lekin u sahifadan "ajralgan" edi. DOM — JS bilan HTML o'rtasidagi ko'prik: tugma bosilsa javob berish, ro'yxatga element qo'shish, matnni o'zgartirish — frontend'ning haqiqiy joni.

0.5-bobda DOM (Document Object Model — HTML'dan qurilgan daraxt) va renderingni ko'rdik. Endi JS bilan o'sha daraxtni boshqaramiz: element tanlash, o'zgartirish, yaratish/o'chirish (CRUD), va hodisalarga (events) javob berish.

O'xshatish: DOM — uyning ichki tuzilishi (xonalar, eshiklar). JS — elektrik: chiroqni yoqadi, eshikni ochadi, devorni bo'yaydi. DOM API — elektrikning asboblari. Statik HTML — yashab bo'lmaydigan bo'sh uy; DOM manipulyatsiyasi — uni jonli qiladi.

DOM — vanilla JS frontend'ning asosi. React (11) "Virtual DOM" bilan buni avtomatlashtiradi, lekin asosini bilish shart — React ham oxir-oqibat DOMni o'zgartiradi. Bu bob — interaktiv vebning yuragi.


2. Nazariya — chuqur tushuntirish

2.1. DOM nima (0.5 takror)

DOM — brauzer HTML'dan quradigan obyektlar daraxti 0.5-bob. Har HTML element — node (tugun); JS bu obyektlarni o'qiydi va o'zgartiradi:

text
  document
    └── html
         └── body
              ├── h1  (matn: "Salom")
              └── ul
                   ├── li
                   └── li

document — butun DOM'ning kirish nuqtasi (global obyekt — brauzerda).

2.2. Element tanlash (selecting)

js
// Eng ko'p ishlatiladigan (CSS selektorlar bilan — 1.2)
document.querySelector(".karta");       // BIRINCHi mos element (yoki null)
document.querySelectorAll("li");        // BARCHA mos (NodeList — massivga o'xshash)
document.getElementById("asosiy");      // id bo'yicha (eng tez)

// querySelector — CSS selektor 1.2-bob ishlatadi:
document.querySelector("#menu .havola.faol");
document.querySelector("input[type='email']");

querySelector — eng universal (CSS selektor — 1.2). null qaytarishi mumkin (element yo'q bo'lsa) — tekshiring (2.1: ?.). querySelectorAllNodeList (massiv emas, lekin forEach bor; to'liq Array metodlari uchun [...nodeList] — 2.7).

2.3. Kontent va atributni o'zgartirish

js
const el = document.querySelector("#sarlavha");

// Matn/HTML
el.textContent = "Yangi matn";    //  faqat matn (xavfsiz — XSS yo'q, 14)
el.innerHTML = "<b>Qalin</b>";    //  HTML (XSS xavfi — ishonchsiz ma'lumotda ishlatma!)

// Atribut
el.setAttribute("href", "/yangi");
el.getAttribute("href");
el.id = "yangiId";                // ko'p atribut to'g'ridan-to'g'ri
el.src = "rasm.jpg";

// Class (1.2)
el.classList.add("faol");
el.classList.remove("yashirin");
el.classList.toggle("ochiq");     // bor bo'lsa olib tashlaydi, yo'q bo'lsa qo'shadi
el.classList.contains("faol");    // true/false

// Stil 1.2-bob — odatda class afzal
el.style.color = "red";
el.style.display = "none";

textContent vs innerHTML: textContent — faqat matn (xavfsiz). innerHTML — HTML parse qiladi (XSS xavfi — foydalanuvchi kiritgan ma'lumotni innerHTMLga qo'yma! 14-bob). Imkoni bo'lsa textContent.

2.4. Element yaratish va qo'shish (Create)

js
// 1) Yangi element yaratish
const li = document.createElement("li");
li.textContent = "Yangi element";
li.classList.add("item");

// 2) Daraxtga qo'shish
const ul = document.querySelector("ul");
ul.appendChild(li);              // oxiriga
ul.prepend(li);                  // boshiga
el.before(yangiEl);              // oldiga
el.after(yangiEl);               // keyiniga

// Zamonaviy: insertAdjacentHTML (joylashuv bilan)
ul.insertAdjacentHTML("beforeend", "<li>Tez usul</li>");

Performance 0.5-bob: tsiklda appendChildni ko'p chaqirish — ko'p reflow 0.5-bob. DocumentFragment bilan yig'ib, bir marta qo'shing (0.5-Misol 4).

2.5. O'qish, yangilash, o'chirish (Read, Update, Delete)

js
// Read — tanlab, xususiyatni o'qi (2.2, 2.3)
const matn = el.textContent;
const qiymat = document.querySelector("input").value;   // input qiymati

// Update — o'zgartirish (2.3)
el.textContent = "Yangilangan";

// Delete — o'chirish
el.remove();                     // o'zini o'chiradi
parent.removeChild(child);       // eski usul

2.5b. DOM bo'ylab harakat (traversal)

Bitta elementni tanlab 2.2-bob, undan qo'shnilariga o'tish mumkin — daraxt bo'ylab "yurish":

js
const el = document.querySelector("li");

// Yuqoriga (ota)
el.parentElement;                // bevosita ota element
el.closest(".karta");            // eng yaqin mos ota (o'zidan boshlab — Misol 3)

// Pastga (bolalar)
el.children;                     // bola ELEMENTLAR (HTMLCollection)
el.firstElementChild;            // birinchi bola
el.lastElementChild;             // oxirgi bola

// Yon (qo'shni — sibling)
el.nextElementSibling;           // keyingi qo'shni
el.previousElementSibling;       // oldingi qo'shni

*Element* vs *Node*: parentElement/children/nextElementSibling — faqat elementlarni beradi (xavfsiz). parentNode/childNodes/nextSibling — matn va bo'shliqlarni (text node) ham qamraydi — odatda *Element* versiyasini ishlating. closest (2.9-delegation'da ko'p) — o'zidan boshlab yuqoriga eng yaqin mos ota'ni izlaydi.

2.6. Events (hodisalar) — addEventListener

Event — foydalanuvchi harakati (bosish, yozish, scroll) yoki tizim hodisasi. addEventListener bilan javob beramiz:

js
const tugma = document.querySelector("#btn");

tugma.addEventListener("click", (event) => {
  // event — hodisa haqida ma'lumot (qaysi element, koordinata...)
  console.log("Bosildi!", event.target);
});

Mashhur eventlar:

Event Qachon
click bosilganda
input input qiymati o'zgarganda (har harf)
change input o'zgarib, fokus ketganda
submit forma yuborilganda (1.1)
keydown/keyup klaviatura
mouseenter/mouseleave sichqoncha ustiga/chetga
DOMContentLoaded DOM tayyor bo'lganda (0.5)

addEventListener — eng moslashuvchan: bir elementga ko'p listener, removeEventListener bilan o'chirish mumkin. onclick= (HTML/property) o'rniga doim shu.

2.7. Event obyekti, preventDefault, stopPropagation

js
form.addEventListener("submit", (e) => {
  e.preventDefault();        // brauzerning standart xulqini TO'XTATADI
  // (submit'da sahifa yangilanishini to'xtatadi — SPA uchun zarur)
  console.log(e.target);     // hodisa yuz bergan element
});

el.addEventListener("click", (e) => {
  e.stopPropagation();       // hodisaning yuqoriga "ko'tarilishini" to'xtatadi (2.8)
});
  • e.preventDefault() — standart xulqni to'xtatadi (forma submit, havola o'tishi). SPA va validatsiyada zarur.
  • e.target — hodisa yuz bergan aniq element.
  • e.currentTarget — listener biriktirilgan element.

2.8. Event bubbling va capturing — propagation

Element ichida bossangiz, hodisa tarqaladi (propagation). Ikki faza:

text
  CAPTURING (pastga): document  ...  target  (kamdan-kam)
  TARGET: hodisa yuz bergan element
  BUBBLING (yuqoriga): target  ...  document  (DEFAULT)
js
// Default — BUBBLING (ichki  tashqi, "pufakcha ko'tarilganday")
ichki.addEventListener("click", () => console.log("ichki"));
tashqi.addEventListener("click", () => console.log("tashqi"));
// ichki bosilsa: "ichki"  "tashqi" (yuqoriga ko'tariladi — MDN)

// Capturing fazasini yoqish (kamdan-kam):
tashqi.addEventListener("click", fn, { capture: true });

Bubbling — hodisa ichki elementdan tashqiga ketma-ket "ko'tariladi". Bu — keyingi (2.9) event delegationning asosi. e.stopPropagation() buni to'xtatadi.

2.9. Event delegation — kuchli naqsh

Event delegation — ko'p bolaga alohida listener o'rniga, bitta listener'ni ota'ga qo'yish; bubbling 2.8-bob tufayli ota barcha bola hodisalarini "eshitadi":

js
//  Har li uchun alohida listener (1000 li = 1000 listener — sekin)
document.querySelectorAll("li").forEach(li =>
  li.addEventListener("click", ...)
);

//  Bitta listener ota'da (delegation — MDN)
document.querySelector("ul").addEventListener("click", (e) => {
  if (e.target.matches("li")) {     // qaysi li bosildi? (e.target — 2.7)
    console.log(e.target.textContent);
  }
});

Nega delegation kuchli: (1) bitta listener (xotira/tezlik — 0.1); (2) dinamik qo'shilgan elementlar ham ishlaydi (keyin qo'shilgan li uchun ham); (3) toza kod. Ro'yxat, jadval, dinamik UI uchun ideal.


3. Sintaksis — tez ma'lumotnoma

js
// Tanlash (2.2)
document.querySelector(sel)  .querySelectorAll(sel)  .getElementById(id)

// O'zgartirish (2.3)
el.textContent  el.innerHTML  el.value
el.classList.add/remove/toggle/contains(c)
el.setAttribute(n, v)  el.style.prop

// CRUD (2.4, 2.5)
document.createElement(tag)  parent.appendChild(el)
el.remove()  el.insertAdjacentHTML(pos, html)

// Events (2.6–2.9)
el.addEventListener("click", (e) => { e.preventDefault(); e.target; })
parent.addEventListener("click", e => { if (e.target.matches(sel)) {} })  // delegation

4. Batafsil kod namunalari

Misol 1 — Tanlash, o'zgartirish, class (2.2, 2.3)

js
const sarlavha = document.querySelector("#sarlavha");
sarlavha.textContent = "Yangi sarlavha";       // matn (xavfsiz — 2.3)
sarlavha.classList.add("katta", "markaz");      // class (1.2)

const tugma = document.querySelector(".tugma");
tugma.addEventListener("click", () => {
  sarlavha.classList.toggle("yashirin");        // bosilganda yashir/ko'rsat
});

Misol 2 — Forma va preventDefault (2.7)

js
const form = document.querySelector("#login");
form.addEventListener("submit", (e) => {
  e.preventDefault();                            // sahifa yangilanishini to'xtat (2.7)

  const email = form.querySelector("#email").value.trim();   // (2.6)
  const parol = form.querySelector("#parol").value;

  if (!email.includes("@")) {                    // validatsiya (2.6)
    document.querySelector("#xato").textContent = "Email noto'g'ri";
    return;
  }
  console.log("Yuborildi:", { email, parol });   // (haqiqatda fetch — 2.11)
});

Misol 3 — Dinamik ro'yxat (CRUD + delegation) (2.4, 2.9)

js
const ul = document.querySelector("#royxat");
const input = document.querySelector("#yangi");

// CREATE — element qo'shish (2.4)
function qosh(matn) {
  const li = document.createElement("li");
  li.textContent = matn;
  li.insertAdjacentHTML("beforeend", ' <button class="ochir"></button>');
  ul.appendChild(li);
}

// DELETE — delegation bilan (bitta listener — 2.9)
ul.addEventListener("click", (e) => {
  if (e.target.matches(".ochir")) {              // o'chir tugmasi bosildimi? (2.9)
    e.target.closest("li").remove();             // ota li'ni o'chir (2.5)
  }
});

document.querySelector("#qosh").addEventListener("click", () => {
  if (input.value.trim()) { qosh(input.value.trim()); input.value = ""; }
});

Misol 4 — Bubbling namoyishi (2.8)

js
document.querySelector(".tashqi").addEventListener("click", () => {
  console.log("Tashqi (ota)");
});
document.querySelector(".ichki").addEventListener("click", (e) => {
  console.log("Ichki (bola)");
  // e.stopPropagation();   // bu yo'q bo'lsa, "Tashqi" ham chiqadi (bubbling — 2.8)
});
// .ichki bosilsa: "Ichki (bola)"  "Tashqi (ota)" (yuqoriga ko'tariladi)

5. To'g'ri va noto'g'ri holatlar

1) innerHTMLga ishonchsiz ma'lumot (XSS)

js
//  foydalanuvchi kiritgan ma'lumot — XSS xavfi (2.3, 14)
el.innerHTML = foydalanuvchiKiritdi;

//  textContent (xavfsiz)
el.textContent = foydalanuvchiKiritdi;

2) Ko'p element uchun alohida listener (delegation o'rniga)

js
//  1000 listener (2.9)
items.forEach(i => i.addEventListener("click", ...));

//  delegation — bitta listener ota'da
parent.addEventListener("click", e => { if (e.target.matches(".item")) {} });

3) Element yo'qligini tekshirmaslik

js
//  element yo'q bo'lsa null.textContent  xato (2.2, 0.5)
document.querySelector("#yoq").textContent = "x";

//  tekshir (2.1)
const el = document.querySelector("#yoq");
if (el) el.textContent = "x";   // yoki el?.

4) Forma submit'da preventDefault unutish

js
//  sahifa yangilanib ketadi (SPA buziladi — 2.7)
form.addEventListener("submit", () => { fetch(...); });

//  preventDefault
form.addEventListener("submit", (e) => { e.preventDefault(); fetch(...); });

6. Keng tarqalgan xatolar va yechimlari

Xato 1 — Cannot read properties of null (addEventListener)

Sababi: element topilmadi — JS DOMdan oldin ishladi 0.5-bob yoki selektor xato. Yechimi: defer/DOMContentLoaded 0.5-bob; selektorni tekshiring; null tekshiring 2.2-bob.

Xato 2 — Dinamik element'da listener ishlamaydi

Sababi: keyin qo'shilgan elementga eski listener tegmaydi. Yechimi: event delegation 2.9-bob — ota'da listener.

Xato 3 — Forma sahifani yangilab yuboradi

Sababi: preventDefault yo'q 2.7-bob. Yechimi: submit handler'da e.preventDefault().

Xato 4 — Hodisa kutilmaganda ikki marta ishlaydi

Sababi: listener ikki marta qo'shilgan yoki bubbling 2.8-bob tufayli. Yechimi: listener'ni bir marta qo'shing; kerak bo'lsa stopPropagation yoki { once: true }.

Xato 5 — this event handler'da kutilmagan (2.5)

Sababi: oddiy functionda this — element; arrow'da — tashqi 2.5-bob. Yechimi: elementga e.currentTarget/e.target ishlating (aniq).


7. Integratsiya — bu mavzu stack'ning qayerida uchraydi

  • HTML/CSS (1.1, 1.2): DOM — HTML daraxti; classList — CSS class.
  • Rendering 0.5-bob: DOM o'zgarish reflow/repaint; performance.
  • Events + async 2.11-bob: click fetch DOMni yangila.
  • Browser APIs 2.17-bob: localStorage bilan saqlash; DOM bilan birga.
  • React (11): Virtual DOM — DOM manipulyatsiyasini avtomatlashtiradi; lekin asosi shu. JSX event (onClick).
  • Forma (1.1, 11.10): submit, validatsiya, value.
  • Xavfsizlik (14): XSS — innerHTML xavfi; sanitatsiya.

8. Eng yaxshi amaliyotlar (best practices)

  • querySelector (CSS selektor) ishlating; natijani null tekshiring 2.2-bob.
  • textContent (xavfsiz), innerHTML faqat ishonchli ma'lumotda (XSS — 2.3, 14).
  • Event delegation — ko'p/dinamik element uchun bitta listener ota'da 2.9-bob.
  • addEventListener (HTML onclick emas) — moslashuvchan, o'chirib bo'ladi 2.6-bob.
  • Forma submit'da preventDefault 2.7-bob.
  • DOM o'zgarishni yig'ingDocumentFragment, reflow kamaytiring (0.5, 2.4).
  • Class bilan stil (classList), styleni kam (2.3, 1.2).
  • DOM tayyor bo'lganda ishladefer/DOMContentLoaded 0.5-bob.

9. Amaliy loyiha: "Vazifalar Ro'yxati (To-Do App) — vanilla JS"

DOM'ning klassik loyihasi — hamma narsani birlashtiradi.

Maqsad

Element tanlash, CRUD, events, delegation, preventDefault va localStorage'ni 2.17-bob birlashtirib, to'liq interaktiv ilova qurish.

Talablar (requirements)

  1. HTML: input + "Qo'sh" tugma + ro'yxat (ul) (1.1 semantik).
  2. CREATE: forma submit'da (preventDefault — 2.7) yangi vazifa qo'shing (createElement — 2.4); bo'sh kiritishda qo'shmang (validatsiya).
  3. READ/render: vazifalarni massivda saqlang, DOMda ko'rsating (map g'oyasi — 2.7).
  4. UPDATE: vazifani bossangiz "bajarilgan" (classList.toggle — 2.3); ikki marta bossangiz tahrirlang (bonus).
  5. DELETE: har vazifada "" tugma; delegation bilan o'chiring (Misol 3, 2.9).
  6. Filtr: "hammasi / bajarilgan / qolgan" tugmalari (filter — 2.7).
  7. Saqlash 2.17-bob: localStorageda saqlang — sahifa yangilansa ham qolsin (JSON.stringify/parse — 2.8).
  8. Hisoblagich: "Qolgan: N ta" (Array metodi — 2.7).
  9. Element yo'qligi tekshirilsin 2.2-bob; XSS yo'q (textContent — 2.3).

Maslahatlar (hint)

  • Holatni massivda tuting ([{id, matn, bajarildi}]), har o'zgarishda DOMni qayta render qiling (React g'oyasi — 11).
  • Delegation: ul.addEventListener("click", e => { if (e.target.matches(".ochir")) ... }) 2.9-bob.
  • e.target.closest("li") — bosilgan tugmaning ota li'si 2.5-bob.
  • localStorage: localStorage.setItem("vazifalar", JSON.stringify(arr)) (2.17, 2.8).
  • ID uchun Date.now() yoki hisoblagich (2.4-closure).
  • XSS: li.textContent = vazifa.matn (innerHTML emas — 2.3).

"Tayyor" mezonlari (acceptance criteria)

  • Vazifa qo'shish ishlaydi (forma + preventDefault).
  • Bajarilgan holati toggle bilan; o'chirish delegation bilan.
  • Filtr (hammasi/bajarilgan/qolgan) ishlaydi.
  • localStorage'da saqlanadi (yangilansa ham qoladi).
  • Qolgan vazifalar hisoblagichi to'g'ri.
  • Bo'sh/noto'g'ri kiritish tekshirilgan.
  • textContent (XSS yo'q); element null tekshirilgan.
  • Bitta delegation listener (har li uchun alohida emas).

Yechim kodi ataylab berilmagan — bu loyihani o'zingiz yozib ko'ring.


10. Xulosa va keyingi bobga ko'prik

Bu bobda JS'ni sahifaga ulovchi — DOMni o'rgandik:

  • DOM — HTML'dan qurilgan obyektlar daraxti 0.5-bob; document — kirish nuqtasi.
  • Tanlash: querySelector/querySelectorAll/getElementById (CSS selektor — 1.2; null tekshirish).
  • O'zgartirish: textContent (xavfsiz) vs innerHTML (XSS — 14); classList; setAttribute.
  • CRUD: createElement/appendChild/remove; reflow kamaytirish 0.5-bob.
  • Events: addEventListener; e.preventDefault()/e.target; bubbling (yuqoriga); event delegation (bitta listener ota'da — ko'p/dinamik element uchun).

Keyingi bob — 2.17-bob: Browser APIs — localStorage, sessionStorage, fetch, Intersection Observer. DOM bilan sahifani o'zgartirdik; endi brauzer beradigan boshqa kuchli API'larni o'rganamiz: ma'lumotni brauzerda saqlash (localStorage), tarmoq (fetch — 2.11 takror), va element ko'rinishini kuzatish (Intersection Observer — lazy loading uchun).


Foydalanilgan rasmiy/ishonchli manbalar

  • MDN Web Docs — Document Object Model, querySelector, manipulating documents
  • MDN Web Docs — addEventListener, Event bubbling, event delegation
  • MDN Web Docs — Event.preventDefault/stopPropagation/target

Izohlar (0)

Izoh yozish uchun kiring.

  • Hozircha izoh yo'q. Birinchi bo'ling!
2.16-bob: DOM — tanlash, o'zgartirish, CRUD, events, delegation, bubbling — Wisar