Урок 08

Работа с формами и элементами

Свойства input

input.value;       // текущее значение
input.placeholder;
input.disabled;
input.readOnly;
input.checked;     // для чекбоксов
input.files;       // для файлов

Значения

input.value = 'новое';
textarea.value = 'текст';
select.value = 'option1';
checkbox.checked = true;

Валидация в реальном времени

input.addEventListener('input', () => {
    if (input.value.length < 3) {
        input.classList.add('invalid');
        input.setCustomValidity('Слишком коротко');
    } else {
        input.classList.remove('invalid');
        input.setCustomValidity('');
    }
});

Форматирование ввода

phone.addEventListener('input', (event) => {
    let value = event.target.value.replace(/\D/g, '');
    value = value.replace(/(\d{3})(\d{3})(\d{2})(\d{2})/, '+7 ($1) $2-$3-$4');
    event.target.value = value;
});

Маска

function maskInput(input, mask) {
    input.addEventListener('input', () => {
        const digits = input.value.replace(/\D/g, '');
        let result = '';
        let i = 0;

        for (const char of mask) {
            if (char === '9') {
                if (i < digits.length) result += digits[i++];
                else break;
            } else {
                result += char;
            }
        }

        input.value = result;
    });
}

maskInput(document.querySelector('#phone'), '+7 (999) 999-99-99');

Автофокус

input.focus();
input.blur();

Выделение

input.select();                         // всё
input.setSelectionRange(0, 5);         // часть
input.selectionStart;                   // начало
input.selectionEnd;                     // конец

Скролл к элементу

el.scrollIntoView();

el.scrollIntoView({ behavior: 'smooth', block: 'center' });

Размеры элемента

el.offsetWidth;   // ширина с границами
el.offsetHeight;
el.clientWidth;   // без границ
el.clientHeight;
el.scrollWidth;   // полная ширина со скроллом
el.scrollHeight;

el.getBoundingClientRect(); // { top, left, width, height, ... }

Позиция

el.offsetTop;    // относительно родителя
el.offsetLeft;

el.getBoundingClientRect().top; // относительно окна

Прокрутка

window.scrollY;    // текущая позиция
window.scrollTo(0, 500);
window.scrollTo({ top: 500, behavior: 'smooth' });

el.scrollTop;      // для контейнера
el.scrollTop = 100;

Проверка видимости

function isVisible(el) {
    const rect = el.getBoundingClientRect();
    return (
        rect.top < window.innerHeight &&
        rect.bottom > 0 &&
        rect.left < window.innerWidth &&
        rect.right > 0
    );
}

IntersectionObserver

Современный способ отслеживать видимость:

const observer = new IntersectionObserver((entries) => {
    entries.forEach((entry) => {
        if (entry.isIntersecting) {
            console.log('виден');
        }
    });
});

observer.observe(el);

Создание таблицы

function renderTable(data) {
    const tbody = document.querySelector('tbody');
    tbody.innerHTML = '';

    const fragment = document.createDocumentFragment();

    for (const row of data) {
        const tr = document.createElement('tr');
        tr.dataset.id = row.id;

        for (const [key, value] of Object.entries(row)) {
            const td = document.createElement('td');
            td.textContent = value;
            tr.append(td);
        }

        fragment.append(tr);
    }

    tbody.append(fragment);
}

Создание списка

function renderList(items) {
    const list = document.querySelector('.list');
    list.innerHTML = '';

    const html = items
        .map((item) => `<li data-id="${item.id}">${item.name}</li>`)
        .join('');

    list.insertAdjacentHTML('beforeend', html);
}

Экранируй пользовательские данные — иначе XSS.

Безопасное создание

function escapeHtml(text) {
    const div = document.createElement('div');
    div.textContent = text;
    return div.innerHTML;
}

Классы

el.classList.add('active');
el.classList.remove('active');
el.classList.toggle('active', condition); // с условием
el.classList.replace('old', 'new');
el.classList.contains('active');

Итоги

  • value, checked, files — свойства элементов
  • setCustomValidity для своих сообщений
  • Маски для форматирования ввода
  • getBoundingClientRect для размеров
  • IntersectionObserver для видимости
  • Экранируй HTML при вставке