IlmHamroh
JavaScript Full-stack/9-qism. JavaScript brauzerda va debugging4/29-dars23 daqiqa
Mundarija (40)

JavaScript'da matnni o'zgartirish: textContent, innerHTML va XSS xavfi

Qisqacha: Elementdagi matnni almashtirish uchun element.textContent = "yangi matn" yozing — u matnni har doim oddiy matn sifatida qo'yadi. Uning qo'shnisi innerHTML esa berilgan satrni HTML deb o'qiydi: foydalanuvchi yozgan matnni unga bersangiz, ichidagi <img onerror="..."> kabi kod ishga tushadi — bu XSS hujumi. Qoida: ma'lumot — textContent bilan, HTML — faqat o'zingiz yozgan satrdan.

Bu darsda

  • textContent bilan elementdagi matnni o'qiy va almashtira olasiz.
  • textContent, innerText va innerHTML farqini va qaysi biri qachon kerakligini bilasiz.
  • innerHTML va outerHTML bilan HTML qo'yishni va ularning tuzoqlarini tushunasiz.
  • XSS hujumi innerHTML orqali qanday ishlashini jonli ko'rib, undan himoyalana olasiz.
  • Yangi setHTML metodi nima va nega uni hali har joyda ishlatib bo'lmasligini bilasiz.

Oldin bilishingiz kerak: Elementlarni tanlash, Template literals, HTML darajasidagi xavfsizlik, Jonli hududlar va dinamik kontent.

1. Nega bu kerak?

O'tgan darsda elementni topishni o'rgandik. Topgandan keyingi eng ko'p qilinadigan ish — uning ichidagi narsani almashtirish. Narx o'zgardi — yangi narxni yozish. Savatga taom qo'shildi — "Savatda 3 ta taom" deb yozish. Serverdan mehmonlarning izohlari keldi — ularni ro'yxatga chiqarish.

Bu ishlarning hammasi uchun brauzer uchta asosiy xususiyat beradi: textContent, innerText va innerHTML. Ular bir-biriga juda o'xshaydi. Ko'p boshlovchi farqini bilmay, qo'liga birinchi kelganini ishlatadi. Ko'pincha bu innerHTML bo'ladi — u qulayroq ko'rinadi.

HTML darajasidagi xavfsizlik darsidagi voqeani eslang. «Bahor» saytida "Mehmonlar fikri" bo'limi bor edi. Hujumchi izohga oddiy matn o'rniga HTML yozdi, sayt uni "boricha" chiqardi — va kod Jasur akaning brauzerida ishladi. Bu hujumning nomi — XSS (Cross-Site Scripting, saytlararo skript): begona odamning JavaScript kodi sizning saytingizda, mehmonning brauzerida ishlab ketishi. O'sha darsda "matnni sahifaga qo'yadigan dastur — server yoki JavaScript" degan edik. Bugun o'sha dasturni o'zimiz yozamiz. Va aynan qaysi bitta so'z — textContent yoki innerHTML — saytni himoya qilishini yoki hujumchiga ochib berishini ko'ramiz.

Hayotdan o'xshatish: choyxonadagi menyu doskasi. Otabek doskaga mehmonning izohini bo'r bilan, harfma-harf ko'chiradi — bu textContent. Izohda "osh tekin!" deb yozilgan bo'lsa ham, bu shunchaki yozuv: kassa osh uchun pul olishda davom etadi. Uning teskarisi — innerHTML. U izohni buyruq deb bajaradigan ofitsiantga o'xshaydi: "osh tekin!" o'qidi — darhol tekin osh tarqata boshladi.

2. textContent — matnni o'qish va yozish

2.1 O'qish

textContent — elementning ichidagi barcha matn, teglarsiz. Ichma-ich elementlarning matni ham qo'shiladi:

html
<p id="taom">Osh — <b>35 000</b> so'm</p>
<script>
  const taom = document.querySelector("#taom");
  console.log(taom.textContent);
  console.log(taom.textContent.length);
</script>

Konsolda:

text
Osh — 35 000 so'm
17

<b> tegi yo'qoldi, lekin uning ichidagi "35 000" qoldi. DOM daraxti darsidagi tilda aytsak: textContent elementning ichidagi hamma matn tugunlarini tartib bilan yig'ib, bitta satrga ulaydi.

2.2 Yozish

textContent ga yangi qiymat berilsa, element ichidagi hamma narsa o'chadi va o'rniga bitta matn tuguni qo'yiladi:

html
<p id="taom">Osh — <b>35 000</b> so'm</p>
<script>
  const taom = document.querySelector("#taom");
  taom.textContent = "Osh — 38 000 so'm";
  console.log(taom);
  console.log(taom.children.length);
</script>

Konsolda:

text
<p id="taom">Osh — 38 000 so'm</p>
0

Natijaga qarang: narx endi qalin emas. <b> elementi ham, uning qalinligi ham ketdi — children.length endi 0. Shuning uchun textContent ni faqat kerakli eng kichik elementga yozing. Narxni almashtirmoqchi bo'lsangiz, butun p ni emas, faqat b ni toping (Elementlarni tanlash):

html
<p id="taom">Osh — <b class="narx">35 000</b> so'm</p>
<script>
  const narx = document.querySelector("#taom .narx");
  narx.textContent = "38 000";
  console.log(document.querySelector("#taom"));
</script>

Konsolda:

text
<p id="taom">…</p>

Endi narx yangilandi va qalinligicha qoldi. Konsolda … chiqdi, chunki p ichida endi bola element bor — jonli oyna konsoli uni qisqartiradi. Kod b ga tegdi, p ning qolgan qismiga emas. 38 000 — shu misol uchun o'ylab topilgan narx, «Bahor» menyusi o'zgarmaydi.

2.3 Son, bo'sh satr va null

textContent ga son bersangiz, u satrga aylanadi. Bo'sh satr yoki null bersangiz — element bo'shaydi:

html
<p id="savat">Savat bo'sh emas</p>
<script>
  const savat = document.querySelector("#savat");
  savat.textContent = 3;
  console.log(typeof savat.textContent, savat.textContent);

  savat.textContent = "";
  console.log(savat.childNodes.length);
</script>

Konsolda:

text
string 3
0

textContent = "" — elementni tozalashning eng qisqa yo'li. Undan keyin childNodes da birorta ham tugun qolmaydi.

2.4 Nega textContent xavfsiz?

Eng muhim xususiyati: textContent ga nima bersangiz ham, u har doim matn bo'lib qoladi. Hatto HTML teglari bo'lsa ham:

html
<p id="izoh"></p>
<script>
  const izoh = document.querySelector("#izoh");
  izoh.textContent = "<b>Osh zo'r!</b>";
  console.log(izoh.textContent);
  console.log(izoh.innerHTML);
</script>

Konsolda:

text
<b>Osh zo'r!</b>
&lt;b&gt;Osh zo'r!&lt;/b&gt;

Natija oynasida qalin matn yo'q — <b> belgilari ekranda harf sifatida ko'rinadi. Ikkinchi qatorga qarang: brauzer < ni o'zi &lt; ga aylantirib qo'ydi. Bu — HTML xavfsizligi darsidagi ekranlash (escaping). textContent uni siz uchun avtomatik qiladi.

Tekshirib ko'ring: <p>Narx: <span>5 000</span></p> dagi p ga p.textContent = "Narx: 6 000" yozildi. Endi p.querySelector("span") nima qaytaradi?

Javob

null. textContent ga yozish p ning ichidagi hamma narsani, jumladan span ni ham o'chiradi va o'rniga bitta matn tugunini qo'yadi. Faqat narxni o'zgartirish kerak bo'lsa, span ning o'zining textContent iga yozish kerak edi.

2.5 Jonli hududga xabar yozish

Jonli hududlar darsida va'da qilgan edik: aria-live hudud sahifada oldindan turadi, JavaScript esa faqat uning matnini almashtiradi. Mana, o'sha almashtirishning o'zi:

html
<button type="button">Savatga</button>
<p id="savat-xabar" aria-live="polite"></p>
<script>
  const xabar = document.querySelector("#savat-xabar");
  xabar.textContent = "Osh savatga qo'shildi. Savatda 3 ta taom.";
  console.log(xabar.textContent);
</script>

Konsolda:

text
Osh savatga qo'shildi. Savatda 3 ta taom.

Hozircha xabar sahifa ochilishi bilan yoziladi. Uni tugma bosilganda yozishni addEventListener darsida qilamiz. Malika NVDA bilan saytda bo'lsa, matn o'zgarishi bilan ekran o'quvchi uni ovoz chiqarib o'qiydi. Element o'zgarmadi, faqat matn — aynan shu "e'lon qil" degan signal.

3. innerText — ko'zga ko'rinadigan matn

innerText ham matn qaytaradi, lekin boshqacha: u sahifada ko'zga qanday ko'rinsa, shunday beradi. Buning uchun brauzer CSS'ni hisobga oladi:

html
<style>
  .katta { text-transform: uppercase; }
</style>
<p id="taom">Osh <span hidden>(sir: ziravor)</span>
  tayyor</p>
<p id="sarlavha" class="katta">bugungi menyu</p>
<script>
  const taom = document.querySelector("#taom");
  console.log(JSON.stringify(taom.textContent));
  console.log(JSON.stringify(taom.innerText));
  const sarlavha = document.querySelector("#sarlavha");
  console.log(sarlavha.textContent, "|", sarlavha.innerText);
</script>

Konsolda:

text
"Osh (sir: ziravor)\n  tayyor"
"Osh tayyor"
bugungi menyu | BUGUNGI MENYU

Matnni JSON.stringify bilan chiqardik — DOM daraxti darsidagi usul: Enter \n bo'lib ko'rinadi. Uch farqni ko'ring:

  • hidden span'ning matni textContent da bor, innerText da yo'q: u ekranda ko'rinmaydi.
  • HTML'dagi Enter va bo'sh joylar textContent da saqlangan, innerText da esa ekrandagidek bitta probelga qisqargan.
  • text-transform: uppercase ham hisobga olindi: innerText ekrandagi katta harflarni berdi.

Ko'rinadigan matnni hisoblash uchun brauzer sahifaning joylashuvini (layout) o'lchaydi. Shuning uchun innerText textContent dan sekinroq. Ko'p elementda, sikl ichida bu sezilishi mumkin — sababini Rendering pipeline darsida ko'ramiz.

innerText ga yozish ham textContent ga o'xshaydi — xavfsiz, matn sifatida. Bitta farq: matndagi \n belgilari <br> ga aylanadi.

textContent innerText
Nimani beradi HTML'dagi hamma matn Ekranda ko'rinadigan matn
CSS'ni hisobga oladimi Yo'q Ha (hidden, display: none, text-transform)
Tezligi Tez Sekinroq (layout kerak)
Qayerda bor Har qanday tugunda Faqat HTML elementlarida

Amaliy qoida: yozish uchun deyarli doim textContent. innerText — faqat "foydalanuvchi aynan nimani ko'ryapti?" degan savol kerak bo'lganda, masalan ko'rinadigan matnni nusxalashda.

Tekshirib ko'ring: Avtomatik test kartadagi taom nomini o'qib, "OSH" ga tenglashtirmoqchi. HTML'da "Osh" yozilgan, CSS esa uni uppercase bilan chiqaradi. Test qaysi xususiyatni o'qisa, "OSH" ni oladi?

Javob

innerText — u CSS'ni hisobga oladi va ekrandagi katta harflarni beradi. textContent esa HTML'dagi "Osh" ni qaytaradi. Shuning uchun testlar ba'zan "foydalanuvchi ko'radigan matn" uchun innerText ga o'xshash usuldan foydalanadi.

4. innerHTML — HTML sifatida o'qish va yozish

4.1 O'qish: element ichidagi HTML

innerHTML element ichini HTML satri sifatida qaytaradi — teglari bilan:

html
<ul id="menyu"><li>Osh</li><li class="yangi">Manti</li></ul>
<script>
  const menyu = document.querySelector("#menyu");
  console.log(menyu.innerHTML);
  console.log(menyu.textContent);
</script>

Konsolda:

text
<li>Osh</li><li class="yangi">Manti</li>
OshManti

Bu satr — fayldagi matn emas. Brauzer uni DOM'dan qaytadan yasaydi (DOM — manba kodi emas). Shuning uchun atributlar tartibi yoki qo'shtirnoqlar siz yozgandan biroz farq qilishi mumkin.

4.2 Yozish: satrdan elementlar

Endi teskarisi: innerHTML ga satr berilsa, brauzer uni HTML deb o'qiydi va elementlarga aylantiradi. Bir va'dani eslang: Template literals darsida backtick ichida yig'ilgan HTML "hozircha shunchaki satr", uni sahifaga qo'yishni keyin o'rganamiz. Mana o'sha joy:

html
<div id="karta"></div>
<script>
  const nomi = "Manti";
  const narx = 30000;
  const html = `
    <h3>${nomi}</h3>
    <p class="narx">${narx} so'm</p>
  `;
  const karta = document.querySelector("#karta");
  karta.innerHTML = html;
  console.log(karta.children.length);
  console.log(karta.querySelector(".narx"));
</script>

Konsolda:

text
2
<p class="narx">30000 so'm</p>

Satr endi haqiqiy h3 va p elementlariga aylandi: ularni querySelector bilan topsa bo'ladi, CSS ularni bezaydi. Bu juda qulay — shuning uchun innerHTML mashhur.

E'tibor bering: bu yerda nomi va narx ni biz o'zimiz yozdik. Ular kodning ichida, hech kim ularni o'zgartira olmaydi. Xavf ma'lumot tashqaridan kelganda boshlanadi — buni «Hujumchi nigohi» bo'limida ko'ramiz.

4.3 Brauzer satrni "tuzatadi"

Satrdagi HTML xato bo'lsa, brauzer DOM daraxti darsidagidek uni o'zi tuzatadi:

html
<div id="d"></div>
<script>
  const d = document.querySelector("#d");
  d.innerHTML = "<p>Osh <b>tayyor";
  console.log(d.innerHTML);
</script>

Konsolda:

text
<p>Osh <b>tayyor</b></p>

Yopilmagan b va p ni brauzer o'zi yopdi. Xato chiqmaydi — bu yaxshi tomoni ham, yomon tomoni ham: noto'g'ri yozilgan HTML jimgina "boshqacha" bo'lib qoladi.

4.4 <script> ishlamaydi — lekin bu himoya emas

innerHTML orqali qo'yilgan <script> tegi bajarilmaydi. Standart shunday yozilgan:

html
<div id="d"></div>
<script>
  const d = document.querySelector("#d");
  d.innerHTML = "<script>console.log('ishladi')<\/script>";
  console.log(d.children.length, d.firstElementChild.nodeName);
</script>

Konsolda:

text
1 SCRIPT

Skript DOM'ga qo'shildi, lekin "ishladi" yozuvi chiqmadi. <\/script> dagi teskari chiziq — kichik hiyla: bo'lmasa brauzer satr ichidagi </script> ni tashqi skriptning oxiri deb tushunardi.

Bu yerda xavfli xulosa chiqarish oson: "demak, innerHTML xavfsiz". Yo'q! HTML xavfsizligi darsidan eslang: hujumda <script> ning o'zi shart emas. Bu haqda pastda.

4.5 innerHTML += tuzog'i

Ro'yxat oxiriga bitta element qo'shish uchun ko'p boshlovchi shunday yozadi:

html
<ul id="menyu"><li>Osh</li></ul>
<script>
  const menyu = document.querySelector("#menyu");
  const osh = menyu.querySelector("li");
  console.log(osh.isConnected);

  menyu.innerHTML += "<li>Manti</li>";
  console.log(osh.isConnected);
  console.log(menyu.querySelector("li") === osh);
</script>

Konsolda:

text
true
false
false

Ekranda hammasi joyida: osh va manti. Lekin konsolga qarang. isConnected — "element hozir sahifadami?" degan savol. Oldin osh sahifada edi, endi — yo'q! Sahifadagi "Osh" — boshqa, yangi element.

Sababi: a += b aslida a = a + b. Brauzer avval butun ichki HTML'ni satrga aylantiradi, unga yangi qismni qo'shadi, keyin hamma narsani o'chirib, butun satrdan elementlarni qaytadan yasaydi. Natijada:

  • oldin olingan o'zgaruvchilar (osh) endi sahifada yo'q elementga qaraydi;
  • elementlarga keyin qo'shiladigan tinglovchilar (hodisalar) yo'qoladi;
  • forma maydonlariga yozilgan matn o'chadi;
  • ro'yxat uzun bo'lsa, har qo'shishda hammasi qayta quriladi — sekin.

Bitta element qo'shishning to'g'ri yo'llari — insertAdjacentHTML va append — Element yaratish, joylash va o'chirish darsida.

Tekshirib ko'ring: Nega innerHTML += dan keyin osh.textContent = "Palov" yozilsa, ekranda hech narsa o'zgarmaydi?

Javob

osh o'zgaruvchisi eski elementga qaraydi — u endi sahifada emas (isConnected — false). Unga yozilgan matn xotiradagi "uzilgan" elementda qoladi. Ekrandagi "Osh" esa innerHTML += yasagan yangi element. Uni qaytadan topish kerak: menyu.querySelector("li").

5. outerHTML — element o'zi bilan birga

innerHTML — elementning ichi. outerHTML — elementning o'zi va ichi:

html
<ul id="menyu"><li>Osh</li></ul>
<script>
  const menyu = document.querySelector("#menyu");
  console.log(menyu.innerHTML);
  console.log(menyu.outerHTML);
</script>

Konsolda:

text
<li>Osh</li>
<ul id="menyu"><li>Osh</li></ul>

outerHTML ga yozish elementni butunlay almashtiradi. Va bu yerda yana bitta tuzoq bor:

html
<p id="aksiya">Bugun aksiya yo'q</p>
<script>
  const aksiya = document.querySelector("#aksiya");
  aksiya.outerHTML = "<h2 id='aksiya'>Ko'k choy bepul!</h2>";
  console.log(aksiya, aksiya.isConnected);
  console.log(document.querySelector("#aksiya"));
</script>

Konsolda:

text
<p id="aksiya">Bugun aksiya yo'q</p> false
<h2 id="aksiya">Ko'k choy bepul!</h2>

Ekranda — yangi h2. Lekin aksiya o'zgaruvchisi hamon eski p ga qaraydi: u sahifadan chiqarib tashlangan. Yangi elementni ishlatish uchun uni qaytadan topish kerak. outerHTML ni o'qish debug uchun qulay; yozish esa kamdan-kam kerak — elementni almashtirishning aniqroq yo'li replaceWith (Element yaratish va o'chirish).

6. Hujumchi nigohi: XSS innerHTML orqali

6.1 Hujumni jonli ko'ramiz

Endi eng muhim qism — XSS'ni o'z ko'zimiz bilan ko'ramiz. «Bahor» saytida "Mehmonlar fikri" bor. Izohlar serverdan keladi — ularni mehmonlar yozgan. Dasturchi ularni chiqarish uchun qulay yo'lni tanladi:

html
<h2>Mehmonlar fikri</h2>
<ul id="izohlar"></ul>
<script>
  const izohlar = [
    "Osh zo'r, xizmat a'lo!",
    "Manti yaxshi " +
      '<img src="x" onerror="console.log(\'XSS: kod ishladi\')">',
  ];
  const royxat = document.querySelector("#izohlar");
  for (const izoh of izohlar) {
    royxat.innerHTML += `<li>${izoh}</li>`;
  }
  console.log("Izohlar chiqarildi");
</script>

Konsolda:

text
Izohlar chiqarildi
XSS: kod ishladi

Ikkinchi qatorni hujumchi yozmadi — uni mehmonning brauzeri bajardi. Ikkinchi izohda img tegi bor. Brauzer x degan rasmni yuklay olmadi va onerror ichidagi kodni ishga tushirdi. Natijada ikkinchi izohda faqat "Manti yaxshi" va buzilgan rasm belgisi ko'rinadi — mehmon hech narsa sezmaydi.

Bizning misolda kod faqat konsolga yozdi. Haqiqiy hujumda console.log o'rnida boshqa kod turadi. U sahifani o'zgartiradi, mehmon nomidan so'rov yuboradi, u yozayotgan parolni o'qiydi. Origin qoidasi bu yerda yordam bermaydi: kod sizning saytingizda turibdi, brauzer uni "o'ziniki" deb biladi.

Diqqat: Bu jonli oyna — izolyatsiyalangan "qum qutisi" (sandbox). Hujum shu oynaning ichida qoladi, darsga yoki saytga zarar yetmaydi. Lekin xuddi shu kod oddiy sahifada ham aynan shunday ishlaydi.

Hujum qanday yo'l bosganini diagrammada ko'ring — yuqoridan pastga:

flowchart TD
  H["Hujumchi izohi"] --> S["Server bazasi"]
  S --> J["JavaScript sahifaga qo'yadi"]
  J -->|innerHTML| P["Brauzer HTML deb o'qiydi"]
  P --> I["img: rasm yo'q"]
  I --> K["onerror: kod ishladi"]
  J -.->|textContent| T["Ekranda oddiy matn"]
  classDef xavf fill:#fdeee6,stroke:#b4461a
  classDef xavfsiz fill:#e8f1ec,stroke:#1f6f4a
  class P,I,K xavf
  class T xavfsiz

Hamma narsa bitta joyda hal bo'ladi: ma'lumot innerHTML ga tushdimi yoki textContent ga.

6.2 Xuddi shu ma'lumot — textContent bilan

Endi o'sha izohlarni xavfsiz yo'l bilan chiqaramiz. Har izoh uchun li kerak — uni document.createElement("li") yaratadi, append esa ro'yxat oxiriga qo'shadi. Ikkalasini Element yaratish darsida to'liq o'rganamiz, bugun faqat qolipni ko'ring:

html
<h2>Mehmonlar fikri</h2>
<ul id="izohlar"></ul>
<script>
  const izohlar = [
    "Osh zo'r, xizmat a'lo!",
    "Manti yaxshi " +
      '<img src="x" onerror="console.log(\'XSS: kod ishladi\')">',
  ];
  const royxat = document.querySelector("#izohlar");
  for (const izoh of izohlar) {
    const li = document.createElement("li");
    li.textContent = izoh;
    royxat.append(li);
  }
  console.log("Izohlar chiqarildi");
</script>

Konsolda:

text
Izohlar chiqarildi

"XSS" qatori yo'q. Natijaga qarang: hujumchining <img ...> yozuvi ekranda harflar bo'lib turibdi. Mehmon uni o'qiydi va "kimdir g'alati narsa yozibdi" deb o'tib ketadi. Kod ishlamadi.

6.3 Ikkalasini bosib solishtiring

Quyidagi oynada ikki tugma bor. Ular bitta hujumchi izohini ikki xil yo'l bilan qo'shadi. Tugma bosilganda kod ishlashi uchun addEventListener("click", ...) ishlatdik — "bosilganda shu funksiyani chaqir" degani, uni addEventListener asoslari darsida to'liq o'rganamiz:

html
<button type="button" id="xavfli">innerHTML bilan</button>
<button type="button" id="xavfsiz">textContent bilan</button>
<p id="izoh"></p>
<script>
  const izoh = document.querySelector("#izoh");
  const matn =
    'Zo\'r! <img src="x" onerror="console.log(\'XSS!\')">';

  document.querySelector("#xavfli")
    .addEventListener("click", () => {
      izoh.innerHTML = matn;
    });
  document.querySelector("#xavfsiz")
    .addEventListener("click", () => {
      izoh.textContent = matn;
    });
</script>

"innerHTML bilan" ni bosing — konsolda qizil emas, oddiy XSS! qatori chiqadi: hujumchining kodi ishladi. "textContent bilan" ni bosing — konsol jim, ekranda esa hujumchining matni harflar bo'lib turadi. Har bosishda paragraf qaytadan yoziladi, shuning uchun tartibni almashtirib ham sinab ko'ring.

6.4 Hujumchi yana qayerdan kiradi

innerHTML yolg'iz emas. Satrni HTML deb o'qiydigan har qanday joy — "eshik". Ularni xavfsizlik tilida sink (lavabo) deyishadi: ma'lumot oqib kelib, kodga aylanadigan joy.

Xavfli (satrni HTML deb o'qiydi) Xavfsiz (matn sifatida)
innerHTML, outerHTML textContent
insertAdjacentHTML (Element yaratish) innerText
document.write (eski, ishlatmang) createElement + textContent

Ma'lumot esa ko'p joydan kelishi mumkin: izoh, ism, forma maydoni, URL'dagi ?qidiruv=... qismi, boshqa saytdan kelgan javob. Qoida bitta: o'zingiz yozmagan har qanday satr — begona. Begona satr xavfli ustunga tushmasin.

7. HTML kerak bo'lsa nima qilish kerak?

Ba'zan begona ma'lumotni HTML ichiga qo'yish kerak bo'ladi: masalan, kartada nom qalin, narx alohida rangda. Uchta yo'l bor.

7.1 Tuzilmani o'zingiz yasang, ma'lumotni textContent bilan qo'ying

Eng ishonchli yo'l — HTML qolipini o'zingiz yozasiz (u ichida begona ma'lumot yo'q), keyin begona qiymatlarni textContent bilan joyiga qo'yasiz:

html
<article id="karta"></article>
<script>
  const taom = { nomi: "Manti <i>yangi</i>", narx: "30 000" };
  const karta = document.querySelector("#karta");
  karta.innerHTML = `
    <h3 class="taom-nomi"></h3>
    <p><b class="narx"></b> so'm</p>
  `;
  karta.querySelector(".taom-nomi").textContent = taom.nomi;
  karta.querySelector(".narx").textContent = taom.narx;
  console.log(karta.querySelector(".taom-nomi").textContent);
</script>

Konsolda:

text
Manti <i>yangi</i>

innerHTML ga faqat bizning qolipimiz tushdi. Taom nomi (u ham tashqaridan kelgan bo'lishi mumkin) textContent bilan qo'yildi — <i> kursivga aylanmadi, harflar bo'lib qoldi. Bu qolip real loyihalarda juda ko'p uchraydi. Uni <template> tegi bilan yanada qulay qilishni Ko'p elementni samarali chizish darsida ko'ramiz.

7.2 Ekranlash funksiyasi

Ikkinchi yo'l — begona matnni HTML satriga qo'yishdan oldin ekranlash: beshta maxsus belgini entity'ga almashtirish. Jadvalni HTML xavfsizligi darsida ko'rgansiz. JavaScript'da u replaceAll bilan yoziladi:

html
<p id="izoh"></p>
<script>
  function ekranla(matn) {
    return matn
      .replaceAll("&", "&amp;")
      .replaceAll("<", "&lt;")
      .replaceAll(">", "&gt;")
      .replaceAll('"', "&quot;")
      .replaceAll("'", "&#39;");
  }

  const begona = '<img src="x" onerror="console.log(1)"> & choy';
  const xavfsiz = ekranla(begona);
  console.log(xavfsiz);
  const izoh = document.querySelector("#izoh");
  izoh.innerHTML = `<b>Izoh:</b> ${xavfsiz}`;
</script>

Konsolda:

text
&lt;img src=&quot;x&quot; onerror=&quot;console.log(1)&quot;&gt; &amp; choy

Ekranda "Izoh:" qalin, hujumchining matni esa harflar bo'lib ko'rinadi — xuddi textContent dagidek. 1 konsolga chiqmadi.

Tartibga e'tibor bering: & birinchi almashtiriladi. Nega? Agar oxirida almashtirsangiz, oldingi qadamlar yasagan &lt; ichidagi & ham &amp; ga aylanadi va ekranda < o'rniga &lt; yozuvi chiqib qoladi. Bu xavfsizlik teshigi emas, lekin matn buziladi. Ikki marta ekranlash ham xuddi shu xatoni beradi.

Maslahat: Ekranlash funksiyasini o'zingiz yozish — o'rganish uchun yaxshi. Real loyihada iloji boricha birinchi yo'l (textContent) yoki tayyor vosita ishlatiladi. O'zi yozgan "tozalovchi"da bitta belgini unutish oson. Ayniqsa, ma'lumot href yoki src ga tushsa, ekranlash yetmaydi: javascript: bilan boshlangan havola baribir ishlaydi. Bu holatlarni XSS va DOM xavfsizligi darsida ko'ramiz.

7.3 setHTML — brauzerning o'z tozalovchisi

Uchinchi yo'l — Sanitizer API (sanitize — "tozalash"). Uning asosiy metodi element.setHTML(satr). U innerHTML kabi HTML qo'yadi, lekin oldin xavfli qismlarni olib tashlaydi: <script>, onerror kabi on... atributlar, javascript: havolalar.

html
<div id="izoh"></div>
<script>
  const izoh = document.querySelector("#izoh");
  const begona =
    '<b>Osh</b> zo\'r! <img src="x" onerror="console.log(1)">';
  if ("setHTML" in izoh) {
    izoh.setHTML(begona);
    console.log(izoh.innerHTML);
  } else {
    console.log("Bu brauzerda setHTML yo'q");
  }
</script>

Konsolda:

text
<b>Osh</b> zo'r!

<b> qoldi — qalin "Osh" ekranda. img esa butunlay olib tashlandi, 1 chiqmadi. Bu natija Chrome'da olingan. "setHTML" in izoh — "bu elementda shunday metod bormi?" degan tekshiruv (in operatori). Brauzeringizda u bo'lmasa, konsolda "Bu brauzerda setHTML yo'q" chiqadi.

Qachon chiqdi va qayerda bor (web-features, 2026-yil oktabr holati):

Brauzer setHTML
Chrome, Edge 146-versiyadan
Firefox 148-versiyadan
Safari (iPhone ham) yo'q

Demak, setHTML hali Baseline emas (Veb standartlari). O'zbekistonda iPhone ko'p — Safari'siz yechimga tayanib bo'lmaydi. Shuning uchun bugun uni yolg'iz ishlatmang. Hozircha amalda ko'proq DOMPurify degan kutubxona ishlatiladi — u ham xuddi shu ishni qiladi va hamma brauzerda ishlaydi. Kutubxona ulashni keyinroq o'rganamiz, hozir bilish shart emas.

Yana bir nom uchrashi mumkin: setHTMLUnsafe. Nomidagi "Unsafe" (xavfli) so'zi bejiz emas — u tozalamaydi, innerHTML ga o'xshaydi. Yangi kodda adashtirmang.

Tekshirib ko'ring: Dasturchi shunday "himoya" yozdi: izoh.replaceAll("<script>", ""), keyin innerHTML. Hujumchi buni qanday chetlab o'tadi?

Javob

<script> umuman shart emas: <img src="x" onerror="..."> yoki <svg onload="..."> ham kodni ishga tushiradi. Bundan tashqari <SCRIPT> (katta harf) yoki <scr<script>ipt> kabi hiylalar bor — birinchi <script> olib tashlanganda ikkinchisi "yopishib" yangi teg hosil qiladi. "Xavfli so'zlarni o'chirish" yondashuvi ishlamaydi. To'g'ri yo'l — textContent yoki ishonchli tozalovchi.

8. Qaysi birini tanlash kerak?

Diagrammani yuqoridan o'qing:

flowchart TD
  S{"Nima qo'yamiz?"}
  S -->|faqat matn| T["textContent"]
  S -->|HTML| B{"Kim yozgan?"}
  B -->|o'zimiz, kod ichida| I["innerHTML"]
  B -->|begona: mehmon, server| Q{"Tuzilmani o'zimiz yasay olamizmi?"}
  Q -->|ha| C["qolip + textContent"]
  Q -->|yo'q| D["tozalovchi: DOMPurify, setHTML"]

Kundalik ishda 90% holatda textContent yetadi. innerHTML — faqat o'zingiz yozgan, ichida begona hech narsa bo'lmagan HTML uchun.

9. Ko'p uchraydigan xatolar

9.1 input ga textContent yozish

html
<input id="ism" value="Ali" aria-label="Ism">
<script>
  const ism = document.querySelector("#ism");
  ism.textContent = "Vali";
  console.log(ism.value, "|", ism.textContent);
</script>

Konsolda:

text
Ali | Vali

Maydonda hamon "Ali". input — bo'sh element, uning ichida matn ko'rsatiladigan joy yo'q. Uning ko'rinadigan qiymati — value xususiyati: ism.value = "Vali". Xato chiqmaydi — textContent ko'rinmas joyga yozildi. value atributi va xususiyatining farqini keyingi darsda ko'ramiz.

9.2 Narxni almashtirib, qalinlikni yo'qotish

p.textContent = ... ichidagi hamma elementni o'chiradi («Yozish» bo'limidagi misol). Faqat o'zgaradigan qismni span yoki b ga o'rang va o'shani toping.

9.3 "O'zimizniki" deb begona ma'lumotni innerHTML ga berish

"Bu ma'lumot o'zimizning serverimizdan keladi, xavfsiz". Server ma'lumotni qayerdan oldi? Agar uni biror foydalanuvchi yozgan bo'lsa (izoh, ism, manzil) — u begona. Server uni ekranlamagan bo'lishi mumkin. Brauzerda ham himoyani qo'yish — ikkinchi devor.

9.4 innerHTML += sikl ichida

Har aylanishda butun ro'yxat qayta quriladi va oldingi elementlar o'zgaruvchilari "uziladi" («innerHTML += tuzog'i» bo'limi). Ro'yxat qo'shish — createElement va append bilan.

9.5 Ekranlashda & ni oxirida almashtirish

"<" → &lt; → &amp;lt;. Ekranda &lt; yozuvi chiqadi. & doim birinchi.

10. Mashqlar

1-mashq (oson): Narxni yangilang

Ko'k choy narxi o'zgardi. Faqat narxni (.narx) 6 000 qiling, qolgan matn va qalinlik saqlansin. Keyin butun qatorning matnini konsolga chiqaring. Kutilgan natija: Ko'k choy — 6 000 so'm.

html
<p id="choy">Ko'k choy — <b class="narx">5 000</b> so'm</p>
<script>
  // shu yerga yozing
</script>
Yechim
html
<p id="choy">Ko'k choy — <b class="narx">5 000</b> so'm</p>
<script>
  const choy = document.querySelector("#choy");
  choy.querySelector(".narx").textContent = "6 000";
  console.log(choy.textContent);
</script>

Konsolda:

text
Ko'k choy — 6 000 so'm

Butun p ga yozsangiz, natija matni bir xil bo'lardi, lekin narx qalin bo'lmay qolardi. 6 000 — mashq uchun narx, «Bahor» menyusida ko'k choy 5 000 so'm.

2-mashq (o'rta): Xavfsiz ro'yxat

ismlar massividagi bron qilgan mehmonlar ismini #bronlar ro'yxatiga chiqaring. Bitta ism — hujumchiniki. Talablar: XSS qatori konsolga chiqmasin; oxirida konsolga Jami: 3 ta bron yozilsin.

Ishora: «textContent bilan» bo'limidagi qolip — createElement("li"), textContent, append — for...of ichida.

html
<h3>Bugungi bronlar</h3>
<ul id="bronlar"></ul>
<script>
  const ismlar = [
    "Malika",
    "Bobur",
    '<img src="x" onerror="console.log(\'XSS\')">Sardor',
  ];
  // shu yerga yozing
</script>
Yechim
html
<h3>Bugungi bronlar</h3>
<ul id="bronlar"></ul>
<script>
  const ismlar = [
    "Malika",
    "Bobur",
    '<img src="x" onerror="console.log(\'XSS\')">Sardor',
  ];
  const bronlar = document.querySelector("#bronlar");
  for (const ism of ismlar) {
    const li = document.createElement("li");
    li.textContent = ism;
    bronlar.append(li);
  }
  console.log(`Jami: ${bronlar.children.length} ta bron`);
</script>

Konsolda:

text
Jami: 3 ta bron

Uchinchi qatorda hujumchining yozuvi harflar bo'lib ko'rinadi. bronlar.children.length o'rniga ismlar.length ham yozsa bo'ladi — lekin DOM'dan sanash "sahifada haqiqatan nechta element bor?" degan savolga javob beradi.

3-mashq (qiyin): Ekranlash va qolip

Taom kartasini innerHTML bilan yasang: h3 da nom, p da narx va " so'm". Nom tashqaridan keladi va unda HTML bo'lishi mumkin. Ikki xil yo'l bilan yeching: (a) «Ekranlash funksiyasi» bo'limidagi ekranla bilan; (b) qolip + textContent bilan. Ikkala holatda ham konsolga h3 ning textContent ini chiqaring — natija bir xil bo'lishi kerak: Lag'mon <b>achchiq</b>.

html
<p>Ikki karta — ikki yo'l:</p>
<article id="a"></article>
<article id="b"></article>
<script>
  const taom = { nomi: "Lag'mon <b>achchiq</b>", narx: "28 000" };
  // (a) — #a ga, (b) — #b ga
</script>
Yechim
html
<p>Ikki karta — ikki yo'l:</p>
<article id="a"></article>
<article id="b"></article>
<script>
  function ekranla(matn) {
    return matn
      .replaceAll("&", "&amp;")
      .replaceAll("<", "&lt;")
      .replaceAll(">", "&gt;")
      .replaceAll('"', "&quot;")
      .replaceAll("'", "&#39;");
  }
  const taom = { nomi: "Lag'mon <b>achchiq</b>", narx: "28 000" };

  // (a) ekranlash
  const a = document.querySelector("#a");
  a.innerHTML = `
    <h3>${ekranla(taom.nomi)}</h3>
    <p>${ekranla(taom.narx)} so'm</p>
  `;
  console.log(a.querySelector("h3").textContent);

  // (b) qolip + textContent
  const b = document.querySelector("#b");
  b.innerHTML = "<h3></h3><p><span></span> so'm</p>";
  b.querySelector("h3").textContent = taom.nomi;
  b.querySelector("span").textContent = taom.narx;
  console.log(b.querySelector("h3").textContent);
</script>

Konsolda:

text
Lag'mon <b>achchiq</b>
Lag'mon <b>achchiq</b>

Ikkalasi bir xil ko'rinadi. Farq — xatoga chidamlilikda. (a) da ekranla ni bitta joyda unutsangiz, teshik ochiladi. (b) da begona ma'lumot umuman HTML satriga tushmaydi — unutadigan narsa yo'q. Narx ham ekranlandi: hozir u bizniki, lekin ertaga serverdan kelishi mumkin.

Portfolio footer'ida © 2026 Ismingiz. yozilgan. Yangi yil kelsa, oltita sahifada qo'lda almashtirish kerak. Buni JavaScript'ga topshiramiz.

  1. Oltita sahifaning footer'ida yilni span ga o'rang: © <span id="yil">2026</span> Ismingiz. HTML'dagi 2026 — zaxira: JavaScript ishlamasa ham yil ko'rinadi.
  2. asosiy.js ga yilniYangila funksiyasini qo'shing: #yil ni topsin, bo'lmasa jim tugasin (erta qaytish), bor bo'lsa — textContent ga joriy yilni yozsin.
  3. Joriy yil: new Date().getFullYear(). Date — sana va vaqt bilan ishlaydigan tayyor obyekt, Sayoz va chuqur nusxalash darsida getFullYear ni ko'rgansiz. Sanalarni Date asoslari darsida chuqur o'rganamiz.
  4. Funksiyani fayl oxirida, oldingi chaqiruvlardan keyin chaqiring: har yangi funksiya va uning chaqiruvi — faylning eng pastiga. npm run check, commit: JS: footer yilini avtomatik yangila.
Yechim

asosiy.js ga:

js
function yilniYangila() {
  const yil = document.querySelector("#yil");
  if (yil === null) {
    return;
  }
  yil.textContent = new Date().getFullYear();
}

yilniYangila();

Footer (har sahifada):

html
<footer>
  <p>© <span id="yil">2026</span> Aziz Karimov.</p>
</footer>

Mini-sinov — joriy yil konsolda va footer'da:

html
<footer>
  <p>© <span id="yil">2026</span> Aziz Karimov.</p>
</footer>
<script>
  function yilniYangila() {
    const yil = document.querySelector("#yil");
    if (yil === null) {
      return;
    }
    yil.textContent = new Date().getFullYear();
  }
  yilniYangila();
  console.log(typeof document.querySelector("#yil").textContent);
</script>

Konsolda:

text
string

getFullYear() son qaytaradi, textContent uni satrga aylantirdi — «Son, bo'sh satr va null» bo'limidagidek. Bu yerda innerHTML kerak emas: yil — oddiy matn. Footer'dagi yilni hozir faqat kompyuteringiz soatini o'zgartirib tekshirish mumkin — muhim emas, kod bir xil ishlaydi.

11. Real ishda

  • Har bir dinamik sahifa — savat soni, bildirishnoma matni, qidiruv natijalari — textContent bilan yangilanadi. Kod ko'rigida (code review) innerHTML ko'rinsa, birinchi savol: "bu yerga begona ma'lumot tushishi mumkinmi?"
  • Freymvorklar (React, Vue — kursda alohida o'rganamiz) matnni sukut bo'yicha ekranlaydi — ularda ham "HTML qo'yish" uchun maxsus, ataylab qo'rqinchli nomli yo'l bor (React'da dangerouslySetInnerHTML). Nomi "xavfli" — bugungi saboq aynan shu.
  • Linterlar — kodni avtomatik tekshiradigan vositalar (kursda alohida o'rganamiz) — innerHTML ga o'zgaruvchi berilganini ogohlantirish bilan belgilay oladi.
  • Brauzer himoyalari: Content Security Policy (CSP) va Trusted Types innerHTML ga begona satr tushishini brauzer darajasida to'sishi mumkin (XSS va DOM xavfsizligi, Brauzer xavfsizlik modeli).
  • Intervyuda tez-tez so'raladi: "textContent, innerText va innerHTML farqi?", "XSS nima va undan qanday himoyalanasiz?" Bugun ikkalasiga javob berishni o'rgandingiz.

Xulosa

  • textContent — elementning hamma matni; unga yozish ichidagi hamma narsani bitta matn bilan almashtiradi va har doim xavfsiz.
  • innerText — ekranda ko'rinadigan matn (CSS'ni hisobga oladi), sekinroq.
  • innerHTML satrni HTML deb o'qiydi; += hamma elementlarni qayta yaratadi; <script> ishlamaydi, lekin onerror ishlaydi.
  • outerHTML elementni almashtiradi — eski o'zgaruvchi uzilgan elementga qaraydi.
  • XSS: begona satr HTML deb o'qilganda hujumchining kodi ishlaydi. Himoya: textContent, qolip + textContent, ekranlash (& birinchi) yoki tozalovchi.
  • setHTML — Chrome 146+ va Firefox 148+ da bor, Safari'da yo'q: hali Baseline emas.

Keyingi dars: Atributlar, xususiyatlar va dataset — elementning matnini emas, atributlarini o'zgartiramiz va nega input da value ikki xil ekanini ko'ramiz.

Manbalar

  • MDN: "Node: textContent property", "HTMLElement: innerText property", "Element: innerHTML property" — developer.mozilla.org
  • MDN: "Element: setHTML() method", "HTML Sanitizer API" — developer.mozilla.org
  • OWASP: "Cross Site Scripting Prevention Cheat Sheet" — cheatsheetseries.owasp.org
  • web-features: "sanitizer" — github.com/web-platform-dx/web-features
Ulashish:Telegram'da

Izohlar (0)

Izoh yozish uchun kiring.

  • Hozircha izoh yo'q. Birinchi bo'ling!
JavaScript'da matnni o'zgartirish: textContent, innerHTML va XSS xavfi — IlmHamroh