IlmHamroh
JavaScript Full-stack/9-qism. JavaScript brauzerda va debugging25/29-dars21 daqiqa
Mundarija (32)

Constraint Validation API: forma tekshiruvini JavaScript'dan boshqarish

Qisqacha: Brauzer required, pattern, min/max bo'yicha tekshirgan natijani JavaScript'ga ochiq beradi. Har maydonning validity obyekti qaysi qoida buzilganini aytadi: valueMissing, patternMismatch va boshqalar. Metodlardan checkValidity() jim tekshiradi, reportValidity() esa xabar pufagini ko'rsatadi. O'z matningiz uchun — setCustomValidity("matn"): u maydonni xato qiladi va pufakka shu matnni (masalan, o'zbekcha xabarni) qo'yadi, bo'sh satr "" esa xatoni olib tashlaydi.

Bu darsda

  • validity orqali maydonda aynan qaysi qoida buzilganini aniqlaysiz.
  • checkValidity() va reportValidity() farqini bilasiz va invalid hodisasini tinglaysiz.
  • setCustomValidity bilan brauzer xabarlarini o'zbekchaga almashtirasiz va o'z qoidangizni qo'shasiz.
  • novalidate bilan pufaklarni o'chirib, barcha xatolarni bir joyda — xatolar xulosasida ko'rsatasiz.
  • Portfolio'dagi aloqa formasi o'zbekcha xabarlar bilan gapiradi.

Oldin bilishingiz kerak: FormData, Brauzer validatsiyasi, input va change: jonli tekshirish, Hodisa tarqalishi: capture va bubble.

1. Nega bu kerak?

Brauzer validatsiyasi darsida bitta muammo ochiq qolgan edi. «Bahor» sayti o'zbekcha, Ali esa formani bo'sh yuborganda pufakda "Please fill out this field." yoki "Заполните это поле." ni ko'radi. Brauzer xabarlari sahifa tilida emas, brauzerning o'z tilida chiqadi. O'shanda "matnni o'zgartirishning yagona yo'li — JavaScript" degan edik.

Ikkinchi muammo — ikki dars oldin, input va change darsida, ism uzunligini va kishi sonini JavaScript'da qaytadan tekshirdik: qiymat.trim().length < 2, odam > 20. Lekin bu qoidalar HTML'da allaqachon yozilgan edi — minlength="2", max="20". Endi har qoida ikki joyda yashaydi. Biri o'zgarsa, ikkinchisi unutiladi. Telefon tekshiruvimiz esa umuman chala chiqdi: +998abcdefghi ham o'tib ketardi.

Brauzer bu tekshiruvni allaqachon bajaryapti — va to'g'ri bajaryapti. Bizga faqat uning natijasini o'qish va xabarini almashtirish imkoni kerak. Shu imkon — Constraint Validation API (cheklovlar bo'yicha tekshiruv API'si). Constraint (cheklov) — maydonga qo'yilgan qoida: required, pattern, max va boshqalar.

Hayotdan o'xshatish: choyxonada ovqat sifatini tekshiradigan sanitariya inspektori. U allaqachon tekshirib, jurnalga yozib qo'ygan. Jasur akaga inspektorni qayta ishga olish shart emas — jurnalni o'qish va kamchilikni mehmonga tushunarli tilda aytish kifoya.

2. validity — qaysi qoida buzildi

2.1 Har maydonning tekshiruv jurnali

Har bir forma maydonida validity xususiyati bor — ValidityState (to'g'rilik holati) obyekti. Unda o'nta mantiqiy qiymat (true/false) — har qoida uchun bittadan — va umumiy valid.

Bu o'nta qiymatni bayroq deb ataymiz. Xuddi stadiondagi hakam bayrog'i kabi: ko'tarilgan bo'lsa (true) — qoida buzilgan, tushirilgan bo'lsa (false) — hammasi joyida.

html
<form id="bron">
  <label>Ism <input name="ism" required minlength="2"
                    value="A"></label>
  <label>Telefon <input name="telefon" type="tel" required
    pattern="\+998 ?\d{2} ?\d{3} ?\d{2} ?\d{2}"
    value="90 000 00 00"></label>
  <label>Kishi <input name="odam" type="number" required
                      min="1" max="20" value="25"></label>
  <label>Vaqt <input name="vaqt" type="time" required
                     min="10:00" max="22:00" value="23:00"></label>
  <label>Email <input name="email" type="email" value="ali"></label>
</form>
<script>
  const bayroqlar = [
    "valueMissing", "typeMismatch", "patternMismatch",
    "tooLong", "tooShort", "rangeUnderflow", "rangeOverflow",
    "stepMismatch", "badInput", "customError",
  ];
  const forma = document.querySelector("#bron");
  console.log(forma.elements.ism.validity);

  for (const maydon of forma.elements) {
    const buzilgan = [];
    for (const bayroq of bayroqlar) {
      if (maydon.validity[bayroq]) {
        buzilgan.push(bayroq);
      }
    }
    console.log(maydon.name, maydon.validity.valid, buzilgan);
  }
</script>

Konsolda:

text
ValidityState {}
ism true (0) []
telefon false (1) ['patternMismatch']
odam false (1) ['rangeOverflow']
vaqt false (1) ['rangeOverflow']
email false (1) ['typeMismatch']

Birinchi qatorga hayron bo'lmang. FormData darsidagi FormData {} ni eslang: u ham bo'sh ko'rinardi, lekin ichi to'la edi. ValidityState {} ham xuddi shunday — uning qiymatlari oddiy xususiyat emas, Chrome konsoli ularni ko'rsatmaydi. Shuning uchun har bayroqni nomma-nom so'radik — maydon.validity[bayroq], kvadrat qavs bilan, chunki nom o'zgaruvchida.

Qolgan qatorlar: har maydonda aynan qaysi qoida buzilgani. Brauzer "xato" deb umumiy aytmaydi — sababini ham aytadi. Endi biz ham mehmonga sababini ayta olamiz.

2.2 Bayroqlar jadvali

Bayroq Qachon true Qaysi atribut
valueMissing Majburiy maydon bo'sh required
typeMismatch Ko'rinish turga mos emas type="email", url
patternMismatch Shablonga mos emas pattern
tooShort / tooLong Juda qisqa / uzun minlength / maxlength
rangeUnderflow Chegaradan kichik min
rangeOverflow Chegaradan katta max
stepMismatch Qadamga tushmaydi (2.5 kishi) step
badInput Brauzer qiymatni tushunmadi (2e) number, sana
customError Kod xato qo'ygan setCustomValidity
valid Hammasi false — maydon to'g'ri —

2.3 Ism nega true?

Diqqat qiling: ism "A", minlength="2" — lekin ism true, tooShort yo'q! Bu xato emas. Brauzer validatsiyasi darsida aytilgan nozik qoida: minlength va maxlength faqat mehmon maydonga o'zi yozganda tekshiriladi. HTML'dagi value="A" ham, JavaScript yozgan maydon.value = "A" ham bu qoidani ishga tushirmaydi. Mehmon tegmagan qiymat uchun brauzer uni ayblamaydi.

Mehmon o'zi yozsa, qoida darhol ishlaydi. Buni «O'zbekcha xabarlar» bo'limidagi formada ko'rasiz: ism maydoniga bitta harf yozib yuborishga urinsangiz, tooShort xabari chiqadi.

Tekshirib ko'ring: <input type="number" name="odam" min="1" max="20" step="1"> maydoniga mehmon 0, 2.5, 21 va bo'sh qiymat yozdi (required yo'q). Har biri uchun qaysi bayroq true?

Javob

0 — rangeUnderflow. 2.5 — stepMismatch (qadam 1, ya'ni faqat butun son). 21 — rangeOverflow. Bo'sh — hech biri: required bo'lmasa, bo'sh maydon to'g'ri hisoblanadi, valid — true.

3. checkValidity(), reportValidity() va invalid hodisasi

3.1 Jim va ovozli tekshiruv

validity — tayyor natija. Formani yoki maydonni "hozir tekshir" deb so'rash uchun ikki metod bor:

html
<form id="bron">
  <label>Ism <input name="ism" required></label>
  <label>Kishi <input name="odam" type="number"
                      required min="1" max="20"></label>
</form>
<button type="button" id="jim">checkValidity()</button>
<button type="button" id="ovozli">reportValidity()</button>
<script>
  const forma = document.querySelector("#bron");

  forma.addEventListener("invalid", (event) => {
    console.log("invalid:", event.target.name);
  }, true);

  document.querySelector("#jim").addEventListener("click", () => {
    console.log("checkValidity:", forma.checkValidity());
  });
  document.querySelector("#ovozli").addEventListener("click", () => {
    console.log("reportValidity:", forma.reportValidity());
    console.log("fokus:", document.activeElement.name);
  });
</script>

«checkValidity()» ni bosing — konsolda uch qator chiqadi, ekranda esa hech narsa o'zgarmaydi:

text
invalid: ism
invalid: odam
checkValidity: false

Endi «reportValidity()» ni bosing. Konsolda yana ikkita invalid, keyin reportValidity: false va fokus: ism. Ekranda esa ism maydoni yonida brauzer pufagi paydo bo'ldi va fokus o'sha maydonga o'tdi.

checkValidity() reportValidity()
Natija true / false true / false
invalid hodisasi Har xato maydonda Har xato maydonda
Pufak va fokus Yo'q — jim Birinchi xato maydonda

Ikkalasi ham formada ham, alohida maydonda ham ishlaydi: forma.checkValidity() — butun forma, ism.checkValidity() — faqat ism. Formaning o'zida validity yo'q (forma.validity — undefined), lekin bu ikki metod bor.

Qachon qaysi biri? checkValidity() — kod uchun savol: "yuborsam bo'ladimi?", "keyingi qadamga o'tkazaymi?". reportValidity() — mehmonga ko'rsatish kerak bo'lganda: masalan, ko'p qadamli formada "Keyingi" tugmasi bosilganda joriy qadamni tekshirish.

3.2 invalid ko'tarilmaydi

Kodga qayting: invalid tinglovchisining uchinchi argumenti — true. Hodisa tarqalishi darsidagi capture bosqichi. Nega kerak?

invalid — kam uchraydigan hodisalardan biri: u ko'tarilmaydi (bubble qilmaydi). Formaga oddiy tinglovchi qo'ysangiz, maydondagi invalid unga yetib kelmaydi. Ikki yo'l bor: har maydonga alohida tinglovchi qo'yish yoki formada capture bosqichida ushlash. Ikkinchisi — bitta tinglovchi, delegatsiya kabi. Uchinchi argumentni olib tashlab sinab ko'ring: konsolda faqat checkValidity: false qoladi.

invalid hodisasi faqat ikki metod chaqirilganda emas, mehmon formani yuborishga uringanda ham keladi — brauzer har xato maydon uchun uni chiqaradi va shundan keyin birinchisiga pufak ko'rsatadi. Bu bizga xabarni pufak chiqishidan oldin almashtirish imkonini beradi.

Tekshirib ko'ring: Ko'p qadamli bron formasida "Keyingi" tugmasi bor. Mehmon xato qilgan bo'lsa, unga pufak ko'rinishi va fokus xato maydonga o'tishi kerak. Qaysi metodni chaqirasiz — checkValidity() yoki reportValidity()?

Javob

reportValidity(). U ham true/false qaytaradi, lekin bundan tashqari birinchi xato maydonda pufak ko'rsatadi va fokusni o'sha yerga o'tkazadi. checkValidity() esa jim: mehmon nega o'tolmayotganini bilmay qolardi.

4. setCustomValidity — o'z xabaringiz

4.1 Qanday ishlaydi

setCustomValidity(matn) — maydonga "kod topgan xato" ni qo'yadi:

  • Bo'sh bo'lmagan matn → maydon xato (customError: true), pufakda shu matn chiqadi.
  • Bo'sh satr "" → kod qo'ygan xato olib tashlanadi.

Serverdan "bu vaqtda stol band" degan javob keldi deylik. Brauzer buni bilmaydi, kod esa biladi:

html
<label>Vaqt <input id="vaqt" type="time" value="19:00"></label>
<script>
  const vaqt = document.querySelector("#vaqt");
  console.log(vaqt.validity.valid);

  vaqt.setCustomValidity("Soat 19:00 da bo'sh stol yo'q.");
  console.log(vaqt.validity.valid, vaqt.validity.customError);
  console.log(vaqt.validationMessage);

  vaqt.value = "20:00";
  console.log(vaqt.validity.valid);

  vaqt.setCustomValidity("");
  console.log(vaqt.validity.valid);
</script>

Konsolda:

text
true
false true
Soat 19:00 da bo'sh stol yo'q.
false
true

To'rtinchi qator — darsning eng muhim joyi. Qiymat 20:00 ga o'zgardi, lekin maydon hali ham xato. Kod qo'ygan xato o'zi yo'qolmaydi — uni faqat kod olib tashlaydi (setCustomValidity("")). Bu qoidani unutish — eng ko'p uchraydigan xato: mehmon hammasini tuzatdi, forma esa yuborilmaydi va pufakda eski xabar turaveradi.

validationMessage — pufakda ko'rinadigan matn. Kod xato qo'ymagan bo'lsa, unda brauzerning o'z xabari turadi — brauzer tilida. Natijadagi «Tahrirlab ko'rish» bilan setCustomValidity qatorlarini olib tashlab, required qo'shing va value ni bo'shating: konsolda brauzeringiz tilidagi "maydonni to'ldiring" xabari chiqadi. Biz uni bu yerga yozmadik — har kompyuterda boshqacha.

4.2 O'zbekcha xabarlar

Endi asosiy vazifa. Rejamiz:

  1. Bayroqqa qarab o'zbekcha matn tanlaydigan funksiya — xabarniTop.
  2. Maydon xabarini yangilaydigan funksiya — xabarniYangila: avval eski xabarni tozalaydi (""), keyin brauzer nima deyishini tekshiradi va kerak bo'lsa o'zbekcha matnni qo'yadi.
  3. Uni ikki paytda chaqirish: invalid da (yuborishga urinish — pufakdan oldin) va input da (mehmon tuzatyapti — eski xabar qolib ketmasin).

Ikki yo'l bitta funksiyaga kelib tutashadi:

flowchart TD
  Y["Mehmon yozdi: input"] --> T["Eski xabarni tozalash"]
  B["Yuborish bosildi: invalid"] --> T
  T --> V{"Brauzer qoidalari bajarildimi?"}
  V -->|"Ha"| OK["Xabar yo'q, maydon to'g'ri"]
  V -->|"Yo'q"| X["O'zbekcha xabar qo'yiladi"]
  X --> P["Pufak va :user-invalid shu xabar bilan"]

Nimaga qarang: qaysi yo'ldan kelmasin, avval tozalash. Shu tufayli eski xabar hech qachon qolib ketmaydi.

html
<style>
  form { display: grid; gap: 4px; max-width: 320px; }
  input { padding: 6px; border: 2px solid #5b6660; }
  input:user-invalid { border-color: #b3261e; }
  input:user-valid { border-color: #2e7d32; }
  button { margin-top: 8px; padding: 8px; }
</style>
<form action="https://example.com/bron" method="post">
  <label for="ism">Ismingiz (2–50 belgi)</label>
  <input id="ism" name="ism" required minlength="2" maxlength="50">
  <label for="telefon">Telefon (+998 90 000 00 00)</label>
  <input id="telefon" name="telefon" type="tel" required
         pattern="\+998 ?\d{2} ?\d{3} ?\d{2} ?\d{2}"
         title="+998 va 9 ta raqam: +998 90 000 00 00">
  <label for="odam">Necha kishi (1–20)</label>
  <input id="odam" name="odam" type="number"
         required min="1" max="20">
  <label for="vaqt">Soat nechada (10:00–22:00)</label>
  <input id="vaqt" name="vaqt" type="time" required
         min="10:00" max="22:00">
  <button type="submit">Bron qilish</button>
</form>
<script>
  const forma = document.querySelector("form");

  function xabarniTop(maydon) {
    const v = maydon.validity;
    if (v.valueMissing) {
      return "Bu maydonni to'ldiring.";
    }
    if (v.tooShort) {
      return `Kamida ${maydon.minLength} ta belgi yozing ` +
        `(hozir ${maydon.value.length} ta).`;
    }
    if (v.patternMismatch) {
      return `Format: ${maydon.title}.`;
    }
    if (v.rangeUnderflow) {
      return `Eng kamida ${maydon.min} bo'lsin.`;
    }
    if (v.rangeOverflow) {
      return `Ko'pi bilan ${maydon.max} bo'lsin.`;
    }
    if (v.stepMismatch || v.badInput) {
      return "Butun son kiriting.";
    }
    return "Qiymatni tekshiring.";
  }

  function xabarniYangila(maydon) {
    maydon.setCustomValidity("");
    if (!maydon.validity.valid) {
      maydon.setCustomValidity(xabarniTop(maydon));
    }
  }

  forma.addEventListener("invalid", (event) => {
    const maydon = event.target;
    xabarniYangila(maydon);
    console.log(maydon.name, "—", maydon.validationMessage);
  }, true);

  forma.addEventListener("input", (event) => {
    xabarniYangila(event.target);
  });

  forma.addEventListener("submit", (event) => {
    event.preventDefault();
    console.log("Forma to'g'ri, yuborish mumkin");
  });
</script>

Formani bo'sh qoldirib «Bron qilish» ni bosing. Ism yonidagi pufakda endi "Bu maydonni to'ldiring." turadi — brauzer qaysi tilda bo'lishidan qat'i nazar. Konsolda esa barcha to'rtta xato maydonning o'zbekcha xabari chiqadi (pufak faqat birinchisini ko'rsatadi):

text
ism — Bu maydonni to'ldiring.
telefon — Bu maydonni to'ldiring.
odam — Bu maydonni to'ldiring.
vaqt — Bu maydonni to'ldiring.

Endi qiymatlar bilan sinang:

  • ismga bitta harf → "Kamida 2 ta belgi yozing (hozir 1 ta).";
  • telefonga 90 000 00 00 → "Format: +998 va 9 ta raqam: +998 90 000 00 00.";
  • kishi soniga 25 → "Ko'pi bilan 20 bo'lsin.";
  • vaqtga 23:00 → "Ko'pi bilan 22:00 bo'lsin.".

Hammasini to'g'ri to'ldirsangiz, konsolda "Forma to'g'ri, yuborish mumkin" chiqadi.

Kodning uch muhim joyi:

  • Xabar matnlari HTML'dan olinadi. maydon.minLength, maydon.max, maydon.title — HTML atributlarining JavaScript xususiyatlari. Qoida bitta joyda — HTML'da — qoldi. max="20" ni 30 ga o'zgartirsangiz, xabar ham o'zi "Ko'pi bilan 30" bo'ladi.
  • xabarniYangila avval tozalaydi. setCustomValidity("") dan keyin validity faqat brauzerning o'z qoidalarini ko'rsatadi. Tozalamasak, eski xabar customError bo'lib qolib ketardi va maydon hech qachon to'g'ri bo'lmasdi.
  • Brauzer qoidalari o'chirilmadi. required, pattern, min/max ishlayveradi, pufak ham, :user-invalid ham. Biz faqat matnni almashtirdik.

Ramkalarga qarang: maydondan chiqqaningizda u qizil yoki yashil bo'ldi. Bu — CSS'dagi :user-invalid va :user-valid (Holat pseudo-klasslari). setCustomValidity qo'ygan xato ham ular uchun xato: JavaScript va CSS bitta manbaga — validity ga — qaraydi.

Maslahat: Chrome ekran o'quvchiga ham shu manbadan "noto'g'ri" holatini beradi. Accessibility daraxtida (DevTools → Elements → Accessibility) tekshirdik: mehmon tegib chiqqan xato maydonda invalid: true paydo bo'ladi, tegilmaganda — false. Ya'ni :user-invalid qoidasi Malika uchun ham ishlaydi, aria-invalid ni qo'lda qo'yish shart emas.

4.3 O'z qoidangiz: faqat probellar

Brauzer validatsiyasi darsidan eslang: uchta probeldan iborat ism required dan o'tadi. Brauzerda bunday qoida yo'q — demak, uni biz qo'shamiz. xabarniYangila ga bitta shart:

js
function xabarniYangila(maydon) {
  maydon.setCustomValidity("");
  const faqatProbel =
    maydon.value !== "" && maydon.value.trim() === "";
  if (faqatProbel) {
    maydon.setCustomValidity("Faqat probel yetmaydi, matn yozing.");
    return;
  }
  if (!maydon.validity.valid) {
    maydon.setCustomValidity(xabarniTop(maydon));
  }
}

Shu yo'l bilan istalgan qoidani qo'shish mumkin: "sana o'tib ketmagan bo'lsin", "ikki parol bir xil bo'lsin". Kod tekshiradi va setCustomValidity bilan brauzer tizimiga "ulab" qo'yadi. Shundan keyin u brauzer qoidasi kabi ishlaydi: forma yuborilmaydi, pufak chiqadi, :user-invalid qizartiradi.

Tekshirib ko'ring: Nega xabarniYangila ni faqat invalid da chaqirish yetarli emas? input tinglovchisini olib tashlasak nima bo'ladi?

Javob

Chrome'da sinab ko'rdik. Ali bo'sh formani yuboradi — invalid keladi va ismga "Bu maydonni to'ldiring." qo'yiladi. Ali "Ali" deb yozadi, lekin kod qo'ygan xato o'zi yo'qolmaydi: maydon hali ham xato va ramkasi qizil. Ali «Bron qilish» ni bosadi — forma yana yuborilmaydi. Shu urinishda invalid xabarni tozalaydi, shuning uchun faqat ikkinchi bosishda forma ketadi.

Mehmon uchun bu — to'g'ri yozilgan maydon ustida qizil ramka va "ishlamaydigan" tugma. input tinglovchisi xabarni yozish paytida yangilab boradi va bu chalkashlikni butunlay yo'q qiladi.

5. novalidate va xatolar xulosasi

5.1 Pufak yetmaganda

Pufak qulay, lekin cheklangan: bir vaqtda faqat bitta xato ko'rinadi, uni bezab bo'lmaydi, bir necha soniyada yo'qoladi. Uzun formada mehmon xatolarni birma-bir "ovlaydi". Qulay va accessible forma darsida yaxshiroq yo'lni ko'rgan edik — forma tepasida xatolar xulosasi: barcha xatolar ro'yxati, har biri maydonga havola. O'shanda "fokusni xulosaga ko'chirish uchun JavaScript kerak" degan edik.

Buning uchun formaga novalidate qo'yamiz (Brauzer validatsiyasi): brauzer pufak ko'rsatmaydi va formani to'xtatmaydi — submit hodisasi doim keladi. Qoidalar esa joyida: checkValidity() va validity ishlayveradi. Tekshirish va ko'rsatish endi to'liq bizning qo'limizda.

html
<style>
  form { display: grid; gap: 4px; max-width: 320px; }
  input { padding: 6px; border: 2px solid #5b6660; }
  input:user-invalid { border-color: #b3261e; }
  #xatolar { border: 2px solid #b3261e; padding: 0 12px; }
  #xatolar a { color: #b3261e; }
</style>
<div id="xatolar" tabindex="-1" hidden>
  <h2></h2>
  <ul></ul>
</div>
<form action="https://example.com/bron" method="post" novalidate>
  <label for="ism">Ismingiz (2–50 belgi)</label>
  <input id="ism" name="ism" required minlength="2" maxlength="50">
  <label for="odam">Necha kishi (1–20)</label>
  <input id="odam" name="odam" type="number"
         required min="1" max="20">
  <button type="submit">Bron qilish</button>
</form>
<script>
  const forma = document.querySelector("form");
  const xulosa = document.querySelector("#xatolar");
  const sarlavha = xulosa.querySelector("h2");
  const royxat = xulosa.querySelector("ul");
  const xabarlar = {
    ism: "Ismingizni yozing: kamida 2 harf.",
    odam: "Kishi sonini 1 dan 20 gacha yozing.",
  };

  forma.addEventListener("submit", (event) => {
    event.preventDefault();
    royxat.textContent = "";
    let soni = 0;

    for (const maydon of forma.elements) {
      if (!maydon.checkValidity()) {
        soni = soni + 1;
        const havola = document.createElement("a");
        havola.href = `#${maydon.id}`;
        havola.textContent = xabarlar[maydon.name];
        const qator = document.createElement("li");
        qator.append(havola);
        royxat.append(qator);
      }
    }

    if (soni === 0) {
      xulosa.hidden = true;
      console.log("Forma to'g'ri — serverga yuborish mumkin");
      return;
    }
    sarlavha.textContent = `Formada ${soni} ta xato bor`;
    xulosa.hidden = false;
    xulosa.focus();
  });
</script>

Formani bo'sh qoldirib «Bron qilish» ni bosing:

  1. Forma tepasida qizil ramkali blok chiqdi: "Formada 2 ta xato bor" va ikki havola.
  2. Fokus blokka o'tdi — xulosa.focus(). Ekran o'quvchi Malika birinchi bo'lib "Formada 2 ta xato bor" ni eshitadi.
  3. "Ismingizni yozing" havolasini bosing — fokus ism maydoniga o'tadi, mehmon darhol yozadi.
  4. Ikkala maydon ramkasi ham qizardi: novalidate bo'lsa ham, yuborishga urinishdan keyin :user-invalid ishlaydi (Chrome 154 da tekshirdik).

Kodning ikki nozik joyi:

  • tabindex="-1" — div odatda fokus olmaydi. -1 uni Tab tartibiga qo'shmaydi, lekin focus() ga ruxsat beradi (Klaviatura va fokus boshqaruvi).
  • royxat.textContent = "" — har yuborishda eski ro'yxat tozalanadi, aks holda xatolar ikki marta yozilardi.

5.2 Qaysi yo'lni tanlash

Pufak + setCustomValidity novalidate + o'z ko'rinishingiz
Kod hajmi Kam Ko'proq
Bir vaqtda ko'rinadigan xato Bitta Hammasi
Ko'rinishi Brauzerniki, bezab bo'lmaydi O'zingiz bezaysiz
Til Sizning matningiz Sizning matningiz

Kichik forma (2–4 maydon) — birinchi yo'l yetarli. Uzun forma, ro'yxatdan o'tish, to'lov — ikkinchisi. Ikkalasida ham qoidalar HTML'da qoladi va JavaScript ularni validity orqali o'qiydi.

Tekshirib ko'ring: Xatolar xulosasi bor formadan novalidate ni olib tashlasak, mehmon bo'sh formani yuborganda xulosa chiqadimi?

Javob

Yo'q. novalidate siz brauzer formani o'zi to'xtatadi va pufak ko'rsatadi — xato formada submit hodisasi umuman kelmaydi. Bizning xulosa chizadigan kodimiz esa aynan submit tinglovchisida turibdi, u ishga tushmaydi.

6. Hujumchi nigohi

Bu darsdagi hamma narsa — mijozdagi tekshiruv. Hujumchi uchun:

  • setCustomValidity qo'ygan xatoni DevTools konsolida bitta qator bilan olib tashlaydi: document.querySelector("#ism").setCustomValidity("").
  • novalidate li formada esa tekshiruvning o'zi bizning submit tinglovchimizda. Tinglovchini o'chirish — DevTools'ning Event Listeners yorlig'ida bitta "Remove".
  • curl uchun bu formaning umuman ahamiyati yo'q.

Demak, «faqat probellar» kabi o'z qoidangizni qo'shsangiz, uni serverda ham yozing. Eng yaxshi amaliyot — qoidalarni bir marta yozib, ham brauzerda, ham serverda ishlatish. Buni backend qismida, Validatsiya: nima uchun, qayerda va qanday darsida ko'ramiz. Brauzer tekshiruvi mehmonning vaqti uchun, server tekshiruvi — xavfsizlik uchun.

7. Ko'p uchraydigan xatolar

7.1 setCustomValidity("") unutildi

Mehmon xatoni tuzatdi, forma esa yuborilmaydi, pufakda eski matn. Tuzatish: har tekshiruvni setCustomValidity("") bilan boshlang («O'zbekcha xabarlar» bo'limidagi xabarniYangila kabi).

7.2 Xabarni submit ichida qo'yish

js
// ❌ hech qachon ishlamaydi
forma.addEventListener("submit", () => {
  ism.setCustomValidity("Ismni yozing.");
});

Forma xato bo'lsa, submit umuman kelmaydi — brauzer uni invalid bosqichida to'xtatadi va o'z xabarini ko'rsatadi. To'g'ri forma esa endi xato bo'lib qoladi va keyingi urinishda to'xtaydi. Tuzatish: xabarni invalid va input da qo'ying.

7.3 invalid ni oddiy tinglovchi bilan ushlash

forma.addEventListener("invalid", ...) — uchinchi argumentsiz — hech narsa ushlamaydi: invalid ko'tarilmaydi. Tuzatish: capture — addEventListener("invalid", fn, true) — yoki har maydonga alohida tinglovchi.

7.4 Har input da reportValidity()

Har harfda pufak chiqadi va fokus xato maydonga sakraydi — mehmon boshqa maydonga o'ta olmay qoladi. Tuzatish: input da faqat setCustomValidity (jim), ko'rsatishni brauzer yoki submit ga qoldiring.

7.5 Radio guruhida xabar qolib ketdi

Radio guruhida har radio alohida maydon, required esa butun guruhga tegishli. invalid har radio uchun keladi va xabar har biriga qo'yiladi. Mehmon ikkinchi radioni bosganda faqat o'shaning xabarini tozalasangiz, birinchisida customError qoladi. Chrome'da ikki holatni sinadik. invalid da xabarni shunchaki qo'yadigan kodda forma hech qachon ketmaydi. «O'zbekcha xabarlar» dagi xabarniYangila bilan esa birinchi bosish behuda ketadi, forma faqat ikkinchisida yuboriladi. Tuzatish: radio o'zgarganda butun guruhni yangilang — for (const r of forma.elements[maydon.name]) («Portfolio qadami» da shunday qilamiz).

7.6 ValidityState {} — "bo'sh obyekt"

console.log(maydon.validity) hech narsa ko'rsatmaydi. Tuzatish: kerakli bayroqni so'rang: maydon.validity.valueMissing yoki «Har maydonning tekshiruv jurnali» dagi kabi ro'yxat bilan.

8. Mashqlar

1-mashq (oson): Qaysi bayroq?

Bo'sh joyni bayroq nomi bilan to'ldiring:

  1. <input type="email" value="malika"> →
  2. <input required>, bo'sh →
  3. <input type="number" max="20">, mehmon 30 yozdi →
  4. <input pattern="\d{4}">, mehmon 12a4 yozdi →
  5. Kod setCustomValidity("Band") chaqirdi →
Yechim
  1. typeMismatch — @ yo'q.
  2. valueMissing.
  3. rangeOverflow — max dan katta.
  4. patternMismatch — shablonda 4 ta raqam, a raqam emas.
  5. customError — kod qo'ygan xato.

2-mashq (o'rta): Email uchun o'zbekcha xabar

«O'zbekcha xabarlar» bo'limidagi formaga ixtiyoriy email maydonini qo'shing (type="email", required siz). xabarniTop ga typeMismatch uchun shart yozing: "Email to'liq bo'lsin: ali@example.com". Bo'sh email xato bermasligini tekshiring.

Yechim

HTML'ga, vaqt maydonidan keyin:

html
<label for="email">Email (ixtiyoriy)</label>
<input id="email" name="email" type="email">

xabarniTop ichiga, valueMissing shartidan keyin:

js
if (v.typeMismatch) {
  return "Email to'liq bo'lsin: ali@example.com";
}

ali yozib yuborishga urining — pufakda o'zbekcha matn. Maydonni bo'shatsangiz — xato yo'q: required bo'lmagani uchun bo'sh email to'g'ri. xabarniYangila o'zgarmadi: u input va invalid da yangi maydonni ham o'zi qamrab oldi.

3-mashq (qiyin): Telefon yoki email

«Bahor» ga qo'ng'iroq qilish yoki xat yozish uchun aloqa kerak: telefon yoki email, kamida bittasi. Ikkala maydon ham required siz (har biri yolg'iz ixtiyoriy). Ikkalasi bo'sh bo'lsa — telefon maydonida "Telefon yoki emaildan kamida bittasini yozing." xabari, forma yuborilmasin. Bittasi to'ldirilishi bilan xato yo'qolsin.

Ishora: bu qoida ikki maydonga tegishli — har ikkalasining input ida tekshiring; xabarni setCustomValidity bilan telefonga qo'ying.

Yechim
html
<form action="https://example.com/bron" method="post">
  <label>Telefon <input name="telefon" type="tel"></label>
  <label>Email <input name="email" type="email"></label>
  <button type="submit">Yuborish</button>
</form>
<script>
  const forma = document.querySelector("form");
  const telefon = forma.elements.telefon;
  const email = forma.elements.email;

  function aloqaniTekshir() {
    const ikkalasiBosh =
      telefon.value.trim() === "" && email.value.trim() === "";
    telefon.setCustomValidity(ikkalasiBosh
      ? "Telefon yoki emaildan kamida bittasini yozing."
      : "");
  }

  aloqaniTekshir();
  telefon.addEventListener("input", aloqaniTekshir);
  email.addEventListener("input", aloqaniTekshir);

  forma.addEventListener("submit", (event) => {
    event.preventDefault();
    console.log("Aloqa bor:", telefon.value || email.value);
  });
</script>

Bo'sh holda «Yuborish» ni bosing — pufak telefon maydonida, o'zbekcha matn bilan. Emailga ali@example.com yozing va yana bosing — konsolda Aloqa bor: ali@example.com. aloqaniTekshir() sahifa ochilganda bir marta chaqiriladi: aks holda mehmon hech narsa yozmasdan bossa, forma o'tib ketardi. Xabarni "ikki maydondan biri" ga emas, aniq bitta maydonga qo'ydik — pufak va fokus o'sha yerga boradi.

4-mashq: Portfolio qadami — aloqa formasi o'zbekcha gapiradi

Portfolio aloqa formasida required maydonlar bor: ism, email, mavzu (radio) va xabar. Ularning xato xabarlari hozircha brauzer tilida. UI/UX asoslari darsidagi CSS esa :user-invalid maydonni va uning -yordam matnini qizartiradi.

  1. asosiy.js dagi aloqaFormasiniUla ichida har maydon uchun o'zbekcha xabar yozing: xabarlar obyekti, kalit — name.
  2. «Faqat probellar» qoidasini qo'shing (ism va xabar uchun).
  3. Xabarlarni invalid (capture) va input da yangilang. Radio guruhini butunligicha yangilashni unutmang.
  4. Tekshiring: bo'sh formani yuborish → o'zbekcha pufak; xato maydon qizil (CSS o'zgarmadi); tuzatilgach — yuboriladi.
  5. Commit: Aloqa: o'zbekcha xato xabarlari.
Yechim

aloqaFormasiniUla ichiga (submit tinglovchisidan oldin):

js
const xabarlar = {
  ism: "Ismingizni yozing.",
  email: "Email'ni to'liq yozing: ali@example.com",
  mavzu: "Mavzuni tanlang: ish taklifi yoki savol.",
  xabar: "Xabar matnini yozing.",
};

function xabarniYangila(maydon) {
  maydon.setCustomValidity("");
  const faqatProbel =
    maydon.value !== "" && maydon.value.trim() === "";
  if (faqatProbel) {
    maydon.setCustomValidity("Faqat probel yetmaydi, matn yozing.");
  } else if (!maydon.validity.valid) {
    maydon.setCustomValidity(xabarlar[maydon.name] ?? "Tekshiring.");
  }
}

function guruhniYangila(maydon) {
  if (maydon.type === "radio") {
    for (const radio of forma.elements[maydon.name]) {
      xabarniYangila(radio);
    }
  } else {
    xabarniYangila(maydon);
  }
}

forma.addEventListener("invalid", (event) => {
  guruhniYangila(event.target);
}, true);
forma.addEventListener("input", (event) => {
  guruhniYangila(event.target);
});

Natijani shu parchada tekshiring:

html
<style>
  :is(input, textarea):user-invalid {
    outline: 2px solid #b3261e;
  }
</style>
<form action="https://example.com/aloqa" method="post">
  <p><label for="ism">Ismingiz</label>
    <input id="ism" name="ism" required maxlength="60"></p>
  <fieldset>
    <legend>Qaysi mavzuda?</legend>
    <p><input id="mavzu-ish" name="mavzu" type="radio"
              value="ish" required>
      <label for="mavzu-ish">Ish taklifi</label></p>
    <p><input id="mavzu-savol" name="mavzu" type="radio"
              value="savol">
      <label for="mavzu-savol">Savol</label></p>
  </fieldset>
  <button type="submit">Yuborish</button>
</form>
<script>
  const forma = document.querySelector("form");
  const xabarlar = {
    ism: "Ismingizni yozing.",
    mavzu: "Mavzuni tanlang: ish taklifi yoki savol.",
  };
  function xabarniYangila(maydon) {
    maydon.setCustomValidity("");
    const faqatProbel =
      maydon.value !== "" && maydon.value.trim() === "";
    if (faqatProbel) {
      maydon.setCustomValidity(
        "Faqat probel yetmaydi, matn yozing.");
    } else if (!maydon.validity.valid) {
      const matn = xabarlar[maydon.name] ?? "Tekshiring.";
      maydon.setCustomValidity(matn);
    }
  }
  function guruhniYangila(maydon) {
    if (maydon.type === "radio") {
      for (const radio of forma.elements[maydon.name]) {
        xabarniYangila(radio);
      }
    } else {
      xabarniYangila(maydon);
    }
  }
  forma.addEventListener("invalid", (event) => {
    const maydon = event.target;
    guruhniYangila(maydon);
    console.log(maydon.id, "—", maydon.validationMessage);
  }, true);
  forma.addEventListener("input", (event) => {
    guruhniYangila(event.target);
  });
  forma.addEventListener("submit", (event) => {
    event.preventDefault();
    console.log("Yuborildi:", forma.elements.mavzu.value);
  });
</script>

Bo'sh formani yuboring — konsolda uchta o'zbekcha xabar (ism, mavzu-ish, mavzu-savol), pufak esa ism yonida. Ismga uchta probel yozing — pufak "Faqat probel yetmaydi". Ismni yozing, "Savol" ni tanlang va yuboring — Yuborildi: savol. guruhniYangila siz "Savol" ni tanlaganda "Ish taklifi" radiosida eski xabar qolardi va birinchi «Yuborish» behuda ketardi (Chrome'da sinadik) («Radio guruhida xabar qolib ketdi» xatosi).

9. Real ishda

  • O'zbek va rus tilidagi saytlar brauzer xabarlarining aralash tilini aynan shu API bilan yo'q qiladi. Kichik formalarda — setCustomValidity, kattalarida — novalidate va o'z ko'rinishi.
  • Forma kutubxonalari (React Hook Form, Zod — kursda alohida o'rganamiz, hozir bilish shart emas) ko'pincha novalidate bilan ishlaydi va xatolarni o'zi chizadi. Lekin required va type atributlari baribir yoziladi: JavaScript yuklanmasa ham, brauzer tekshiruvi qoladi.
  • Ko'p qadamli formalar (to'lov, ro'yxatdan o'tish) har qadamda reportValidity() chaqiradi: "Keyingi" tugmasi joriy qadam to'g'ri bo'lmaguncha o'tkazmaydi.
  • Intervyuda: "Brauzer xabarini qanday o'zgartirasiz?", "checkValidity va reportValidity farqi?", "novalidate qachon kerak?" — bugungi uchta bo'lim.

Xulosa

  • maydon.validity — qaysi qoida buzilganini aytadi: valueMissing, patternMismatch, rangeOverflow va yana yettita bayroq; valid — umumiy natija.
  • minlength/maxlength faqat mehmon yozgan qiymatni tekshiradi — HTML yoki kod yozgani tekshirilmaydi.
  • checkValidity() — jim, reportValidity() — pufak va fokus bilan; ikkalasi invalid chiqaradi, invalid esa ko'tarilmaydi (capture bilan ushlang).
  • setCustomValidity("matn") — xato va o'z xabaringiz; "" — tozalash. Har tekshiruvni tozalashdan boshlang.
  • Xabarni invalid va input da yangilang; radio guruhini butunligicha.
  • novalidate — pufaksiz, hammasini o'zingiz ko'rsatasiz: xatolar xulosasi va unga fokus.

Keyingi dars: Dinamik UI'da qulaylik (a11y) — JavaScript o'zgartirgan sahifani Malika ekran o'quvchida, Bobur esa faqat klaviatura bilan ham to'liq ishlata olsin: aria-live, fokus va aria-expanded.

Manbalar

  • MDN: "Constraint validation API", "ValidityState", "HTMLInputElement: setCustomValidity()" — developer.mozilla.org
  • MDN: "HTMLFormElement: checkValidity()", "reportValidity()", "HTMLInputElement: invalid event" — developer.mozilla.org
  • HTML Standard: "Constraints" (constraint validation API) — html.spec.whatwg.org
  • web-features (Baseline): constraint-validation — keng tarqalgan, 2021-yildan; user-pseudos — 2026-yil maydan keng tarqalgan
Ulashish:Telegram'da

Izohlar (0)

Izoh yozish uchun kiring.

  • Hozircha izoh yo'q. Birinchi bo'ling!
Constraint Validation API: forma tekshiruvini JavaScript'dan boshqarish — IlmHamroh