Formalar va boshqaruv elementlari

Fokuslash: focus/blur

Element foydalanuvchi u bilan ishlashga tayyorlanganda "fokus" oladi โ€” masalan, matn kiritish maydoniga bosilganda. Fokuslash va uni yo'qotish ("blur") ma'lumot kiritilib bo'lgan payni belgilash, tekshirishlarni ishga tushirish yoki interfeys holatini o'zgartirish uchun ishlatiladi. Ushbu darsda fokus bilan bog'liq metodlar, hodisalar va delegatsiyani chuqur o'rganamiz.

focus/blur hodisalari

focus hodisasi element fokus olganda, blur hodisasi esa uni yo'qotganda (masalan, foydalanuvchi boshqa joyga bosganda) ishga tushadi. Ular yordamida qiymatni tekshirish yoki interfeysni o'zgartirish mumkin. Masalan, email maydonini fokus yo'qolganda tekshiramiz:

<style>
  .invalid { border-color: red; }
  #error { color: red }
</style>

Email: <input type="email" id="input">
<div id="error"></div>

<script>
  input.onblur = function() {
    // manzil noto'g'ri bo'lsa xatolikni ko'rsatamiz
    if (!input.value.includes('@')) {
      input.classList.add('invalid');
      error.innerHTML = 'Iltimos, to'g'ri email kiriting.';
    }
  };

  input.onfocus = function() {
    // foydalanuvchi qayta kiritmoqchi bo'lsa, xatolikni o'chiramiz
    if (this.classList.contains('invalid')) {
      this.classList.remove('invalid');
      error.innerHTML = '';
    }
  };
</script>

Bu yerda onblur foydalanuvchi maydonni tark etganda qiymatni tekshiradi, onfocus esa u qaytib kelganda avvalgi xatolik belgisini olib tashlaydi.

focus/blur metodlari

elem.focus() va elem.blur() metodlari elementga fokusni dasturiy ravishda berish yoki undan olib qo'yish imkonini beradi. Masalan, qiymat noto'g'ri bo'lsa, foydalanuvchini maydondan chiqarmaymiz โ€” fokusni qaytaramiz:

Email: <input type="email" id="input">
<input type="text" style="width:220px" placeholder="noto'g'ri kiriting, keyin bu yerga o'ting">

<script>
  input.onblur = function() {
    if (!this.value.includes('@')) {
      // xatolikni ko'rsatamiz
      this.classList.add('error');
      // ...va fokusni qaytaramiz
      input.focus();
    } else {
      this.classList.remove('error');
    }
  };
</script>
Fokusni bunday "majburiy ushlab qolish" (foydalanuvchini maydondan chiqarmaslik) UX nuqtai nazaridan yomon amaliyot hisoblanadi. Foydalanuvchi keyinroq to'g'rilashni xohlashi mumkin. Xatolikni ko'rsatish yaxshiroq, lekin foydalanuvchini majburan ushlab turmaslik kerak. Bundan tashqari, JavaScript sabab bo'lgan focus() chaqiruvi ba'zi brauzerlarda blur hodisasi ichida ishlamasligi ham mumkin.
Ko'p hodisalar odatda JavaScriptdan focus()/blur() chaqirilganda ham ishga tushadi. Lekin qiziq holat bor: agar onblur ishlovchisi ichida yana focus() chaqirsangiz, brauzer ba'zan uni e'tiborsiz qoldirishi mumkin.

Istalgan elementga fokus: tabindex

Standart holatda ko'p elementlar fokus qabul qilmaydi. Fokus qabul qiladigan elementlar โ€” foydalanuvchi ular bilan ishlashi mumkin bo'lganlar: <input>, <button>, <a href> va boshqalar. Aksincha, <div>, <span>, <table> kabi elementlar standart holatda fokuslanmaydi โ€” ular uchun elem.focus() ishlamaydi va focus/blur hodisalari yuz bermaydi.

Buni tabindex HTML-atributi o'zgartiradi. tabindex qiymatiga ega istalgan element fokuslanadigan bo'ladi. Bundan tashqari, atribut qiymati foydalanuvchi Tab bosganda o'tish tartibini ham belgilaydi:

<ul>
  <li tabindex="1">Bir</li>
  <li tabindex="0">Nol</li>
  <li tabindex="2">Ikki</li>
  <li tabindex="-1">Minus bir</li>
</ul>

<!-- Tab bosish tartibi: 1 -> 2 -> 0 (va boshqa odatiy elementlar) -->
<!-- "Minus bir" Tab bilan olinmaydi, lekin focus() bilan olinadi -->
Amalda tabindex="0" eng foydali qiymatdir: u elementni fokuslanadigan qiladi, lekin tabiiy o'tish tartibini buzmaydi. JavaScriptda ham xuddi shunday: elem.tabIndex = 0 yozish mumkin.

Delegatsiya: focusin/focusout

focus va blur hodisalarining muhim xususiyati bor: ular ko'tarilmaydi (bubble). Ya'ni ular ota-elementga tarqalmaydi. Bu delegatsiyani qiyinlashtiradi โ€” masalan, butun forma uchun bitta ishlovchi qo'yib, ichidagi maydonlarni kuzatib bo'lmaydi:

<form onfocus="this.className='focused'">
  <input type="text" name="name" value="Ismingiz">
  <input type="text" name="surname" value="Familiyangiz">
</form>

<!-- Bu ISHLAMAYDI, chunki input fokuslansa,
     focus hodisasi form'ga ko'tarilmaydi -->

Buni ikki yo'l bilan hal qilish mumkin.

Birinchi yo'l โ€” focus/blur ko'tarilmasa ham, ular tutish fazasida (capturing phase) yuqoridan pastga o'tadi. Shuning uchun addEventListenerni uchinchi argumenti true bilan (capturing) ishlatamiz:

<form id="form">
  <input type="text" name="name" value="Ismingiz">
  <input type="text" name="surname" value="Familiyangiz">
</form>

<script>
  // capturing fazasida ushlaymiz (uchinchi argument true)
  form.addEventListener('focus', () => form.classList.add('focused'), true);
  form.addEventListener('blur', () => form.classList.remove('focused'), true);
</script>

Ikkinchi yo'l โ€” focusin va focusout hodisalaridan foydalanish. Ular focus/blur bilan aynan bir xil, lekin farqi shundaki, ular ko'tariladi (bubble):

<form id="form">
  <input type="text" name="name" value="Ismingiz">
  <input type="text" name="surname" value="Familiyangiz">
</form>

<script>
  // focusin/focusout ko'tariladi, shuning uchun oddiy delegatsiya
  form.addEventListener('focusin', () => form.classList.add('focused'));
  form.addEventListener('focusout', () => form.classList.remove('focused'));
</script>
Muhim jihat: focusin/focusoutni faqat addEventListener orqali ulash kerak, on<event> xossasi (masalan, elem.onfocusin) orqali emas โ€” bu ishlamaydi. Aksincha, focus/blurni capturing orqali ushlash ham, focusin/focusoutni ishlatish ham to'g'ri yechim.

Validatsiya misoli

Fokus hodisalarining amaliy qo'llanilishi โ€” forma maydonlarini foydalanuvchi ular bilan ishlab bo'lgach tekshirish. Delegatsiya orqali butun forma uchun bir joyda validatsiya yozamiz:

<form id="form">
  Login: <input name="login">
  Parol: <input name="password" type="password">
</form>

<script>
  // focusout ko'tarilgani uchun formada bitta ishlovchi bilan
  // har qanday maydonni tekshira olamiz
  form.addEventListener('focusout', function(event) {
    let input = event.target;

    if (input.value.trim() === '') {
      input.style.borderColor = 'red';
    } else {
      input.style.borderColor = '';
    }
  });
</script>

Bu yerda event.target โ€” fokusni yo'qotgan aynan o'sha maydon. Bitta ishlovchi barcha maydonlar uchun ishlaydi โ€” bu delegatsiyaning kuchi.

Xulosa