Основы JavaScript для Front-end: DOM и базовый React

Курс познакомит с базовыми концепциями JavaScript и их применением во фронтенд-разработке. Основной упор — работа с DOM, событиями, запросами к API и практические задачи, а также обзор ключевых принципов React на базовом уровне.

1. Введение в JavaScript и настройка среды

Введение в JavaScript и настройка среды

JavaScript — основной язык, который делает веб-страницы интерактивными: реагирует на клики, отправляет запросы на сервер, изменяет содержимое страницы без перезагрузки. В этом курсе мы сфокусируемся на практических front-end задачах: работа с DOM, события, базовые запросы и небольшой обзор React.

Что такое JavaScript во front-end

JavaScript в браузере выполняется внутри движка (например, V8 в Chrome) и работает рядом с HTML и CSS:

  • HTML описывает структуру страницы
  • CSS отвечает за внешний вид
  • JavaScript управляет поведением: слушает события, изменяет DOM, запускает логику приложения
  • Важно: JavaScript в браузере — это не только сам язык, но и набор возможностей браузера (Web API): таймеры, работа с адресной строкой, сеть, хранилища и DOM.

    !Общая картина: где находится JavaScript и как он связан с DOM и API браузера

    Где и как запускается JavaScript

    На практике вы будете запускать код в двух основных средах.

    | Среда | Где используется | Плюсы | Когда особенно удобно | |---|---|---|---| | Браузер | Веб-страницы | Есть DOM, события, DevTools | Почти весь front-end курс | | Node.js | Вне браузера (в ОС) | Удобно для инструментов и сборки, быстро запускать скрипты | Настройка проектов, зависимости, будущая работа с React |

    Полезные источники:

  • MDN: JavaScript
  • Node.js
  • Инструменты разработчика в браузере (DevTools)

    DevTools — это встроенная среда отладки в браузере. В рамках курса чаще всего будет нужна вкладка Console.

    Что стоит уметь сразу:

  • Выводить значения в консоль: console.log()
  • Смотреть ошибки (они тоже появляются в Console)
  • Проверять типы и значения прямо во время выполнения
  • Пример кода для консоли (можно вставить и выполнить):

    Документация:

  • Chrome DevTools
  • Редактор кода: Visual Studio Code

    Для комфортной разработки используйте Visual Studio Code.

  • Установить: Visual Studio Code
  • Минимальные рекомендации по настройке:

  • Включить автосохранение (по желанию)
  • Настроить форматирование кода
  • Использовать расширения (но не ставить десятки без необходимости)
  • Полезные расширения:

  • Prettier — Code formatter
  • ESLint
  • Prettier приводит код к единому стилю, а ESLint помогает находить потенциальные ошибки и плохие практики.

    Структура учебного проекта

    Даже маленькому проекту полезна понятная структура. Для начала достаточно такой:

    Позже (когда начнем работу с DOM) добавятся файлы страницы и стилей, а ближе к React — появятся зависимости и сборка.

    Базовые правила современного JavaScript

    Используйте let и const

  • const — значение не переназначается (предпочтительно по умолчанию)
  • let — значение можно переназначить
  • Не используйте var в новых проектах: у него исторически сложное поведение с областями видимости, и в современной разработке он почти не нужен.

    Подключайте строгий режим осознанно

    Строгий режим помогает раньше замечать ошибки. В учебных файлах можно включать его так:

    Примечание: если вы используете JavaScript-модули, строгий режим там включается автоматически. К модулям мы вернемся, когда начнем структурировать код на несколько файлов.

    Установка Node.js и проверка окружения

    Node.js нужен не только для запуска JavaScript вне браузера, но и для установки инструментов проекта (в будущем — для React).

  • Установите Node.js с официального сайта: Node.js
  • Проверьте установку в терминале:
  • npm — менеджер пакетов, с его помощью вы будете устанавливать библиотеки и инструменты.

    Документация:

  • npm Docs
  • Как запускать JavaScript на старте обучения

    В этом курсе вы будете регулярно использовать два способа.

    Запуск в браузере через консоль

    Подходит для коротких экспериментов: проверить выражение, тип, работу метода, быстро посмотреть результат.

    Запуск файла через Node.js

    Создайте файл main.js и запустите:

    Это удобно для тренировки синтаксиса и базовой логики без привязки к DOM. Как только начнем DOM, основной средой снова станет браузер.

    Отладка: что делать, если код не работает

    Практический чеклист:

  • Посмотрите ошибки в консоли (сообщение, строка, файл)
  • Добавьте временный console.log() перед проблемным местом
  • Проверьте, что вы открыли тот файл/папку проекта в редакторе
  • Убедитесь, что сохраняете файл перед запуском
  • Позже мы перейдем к отладке через брейкпоинты в DevTools, когда начнем активно работать с событиями и DOM.

    Что будет дальше

    В следующей статье мы начнем писать front-end код, который взаимодействует со страницей: разберем, что такое DOM, как находить элементы, менять текст и стили, а также как обрабатывать события пользователя.

    2. Типы данных, переменные и базовые операции

    Типы данных, переменные и базовые операции

    В прошлой статье мы настроили окружение и разобрались, где запускается JavaScript: в браузере (с DOM и событиями) и в Node.js (для запуска файлов и инструментов). Теперь закрепим фундамент: какие бывают значения в JavaScript, как их хранить в переменных и как выполнять базовые операции. Это напрямую пригодится, когда мы начнем менять DOM (текст, классы, атрибуты) и позже будем передавать данные в компоненты React.

    Переменные: где живут данные

    Переменная связывает имя и значение. В современном JavaScript почти всегда используют const и let.

  • const — нельзя переназначить значение переменной.
  • let — можно переназначить.
  • Важно: const запрещает переназначение переменной, но не всегда запрещает изменение содержимого объекта или массива.

    Документация:

  • MDN: const
  • MDN: let
  • Типы данных: какие значения бывают

    В JavaScript значения делятся на примитивы и объекты.

    !Карта типов данных в JavaScript и их группировка

    Примитивные типы

    Примитивы — это простые значения (не набор свойств).

    | Тип | Пример | Где часто встречается во front-end | |---|---|---| | string | "Привет" | Текст на странице, значения инпутов | | number | 42, 3.14 | Счетчики, цены, координаты | | boolean | true, false | Флаги: показ/скрытие, валидность | | null | null | Осознанное “ничего нет” (пустое значение) | | undefined | undefined | Значение еще не задано | | bigint | 10n | Очень большие целые числа (редко во front-end) | | symbol | Symbol("id") | Уникальные ключи (редко на старте) |

    База, которая будет использоваться постоянно в курсе: string, number, boolean, null, undefined.

    Документация:

  • MDN: Типы данных
  • Объекты

    Объекты — это значения, которые могут хранить свойства (пары ключ-значение).

    Самые частые формы:

  • Объект {} — для структурированных данных.
  • Массив [] — для списков.
  • Функция function или () => {} — для действий.
  • Примеры:

    Как узнать тип: оператор typeof

    Для быстрой проверки типа используется typeof.

    Особенность: typeof null возвращает "object" по историческим причинам. На практике null проверяют отдельно.

  • value === null
  • value === undefined
  • Документация:

  • MDN: typeof
  • null и undefined: в чем разница

    Эти значения похожи, но смысл у них разный.

  • undefined обычно означает: значение не задано.
  • null обычно означает: значение задано, но оно пустое намеренно.
  • Пример из front-end логики:

    Базовые операции с числами и строками

    Арифметика

    Стандартные операторы:

  • + сложение
  • - вычитание
  • * умножение
  • / деление
  • % остаток от деления
  • Полезно помнить про специальные значения:

  • NaN — результат некорректной числовой операции.
  • Infinity — например, результат деления на 0.
  • Строки и шаблонные литералы

    Строки можно склеивать через +, но во front-end удобнее шаблонные строки с обратными кавычками.

    Это особенно полезно, когда вы будете формировать текст для DOM.

    Приведение типов: когда JavaScript “помогает” слишком сильно

    JavaScript умеет автоматически приводить типы в некоторых операциях. Это удобно, но иногда создает неожиданные результаты.

    Число и строка

    Если хотя бы один операнд строка, + часто превращается в конкатенацию.

    Практическое правило для front-end: значения из текстовых полей (input.value) приходят строкой, и перед математикой их нужно преобразовать.

    Полезные преобразования:

  • Number("12") превращает строку в число (или дает NaN, если нельзя).
  • String(42) превращает число в строку.
  • Boolean(value) превращает значение в true или false по правилам “истинности”.
  • Документация:

  • MDN: Number
  • MDN: Boolean
  • MDN: String
  • “Истинность” значений

    В условиях (if, тернарный оператор) JavaScript приводит значение к true или false.

    Часто встречающиеся ложные значения:

  • false
  • 0
  • "" (пустая строка)
  • null
  • undefined
  • NaN
  • Это важно, когда вы проверяете ввод пользователя или наличие данных перед обновлением DOM.

    Сравнения: === важнее, чем ==

    В JavaScript есть два семейства сравнений:

  • === и !==строгое сравнение (без приведения типов).
  • == и !=нестрогое сравнение (с приведением типов).
  • Во front-end коде почти всегда используйте ===.

    Документация:

  • MDN: Операторы равенства
  • Логические операторы: &&, ||, !

    Логика нужна для условий, фильтрации и безопасных проверок.

  • && — “и”
  • || — “или”
  • ! — “не”
  • Практический кейс для интерфейса: показывать кнопку, только если пользователь вошел и у него есть права.

    Массивы и объекты: базовые операции, которые пригодятся для DOM

    Массивы

    Массив хранит список значений.

    Типичные операции:

  • push добавить в конец
  • pop удалить с конца
  • includes проверить наличие
  • Объекты

    Объект — набор свойств.

    Это базовая форма данных, из которой удобно “рисовать” интерфейс: например, подставлять user.name в DOM.

    Мини-связка с React: данные и неизменяемость

    Когда мы доберемся до React, появится важная привычка: не менять массивы и объекты напрямую, а создавать новые версии.

    Идея простая:

  • изменяемые структуры (object, array) можно случайно “испортить” в нескольких местах кода
  • React опирается на сравнение ссылок, поэтому новый объект часто означает “данные изменились”
  • Пример подхода “создать новый объект”:

    Пока что это просто знакомство с идеей. В дальнейшем мы вернемся к этому, когда будем обсуждать компоненты и состояние.

    Что дальше

    Теперь у вас есть базовый словарь: переменные, типы, сравнения, логические операции и простые структуры данных. В следующей теме мы начнем применять это к front-end практике: искать элементы на странице, читать и менять их содержимое, а затем реагировать на действия пользователя через события.

    3. Условия, циклы и функции для практики на UI

    Условия, циклы и функции для практики на UI

    В прошлых темах вы разобрались с типами данных, переменными, сравнениями и базовыми операциями. Теперь соберем из этого рабочий набор для интерфейсов: условия, циклы и функции.

    Эти три темы постоянно встречаются во front-end:

  • условия решают, что показывать и как реагировать на ввод
  • циклы помогают отрисовывать списки и обрабатывать набор элементов
  • функции позволяют переиспользовать логику и структурировать код (это особенно важно перед React)
  • !Как условия и функции связываются в типичном UI-сценарии

    Условия в UI

    Условие отвечает на вопрос: выполняем этот код или нет? Основной инструмент — if.

    if / else и ветвление

    else if используется, когда вариантов несколько:

    Документация: MDN: if...else

    Правда и ложь в условиях

    В условиях JavaScript приводит значение к true или false. Вы уже встречали ложные значения: "", 0, null, undefined, NaN, false.

    Во front-end это важно для проверок перед обновлением UI:

    Тернарный оператор для коротких решений

    Тернарный оператор удобен для простого выбора из двух вариантов:

    Важно: если внутри получается много логики — лучше обычный if или вынести в функцию.

    Защитные проверки и ранний выход из функции

    В UI-обработчиках часто удобно делать guard clause — проверку на старте и выход, если условие не выполнено.

    Так код читается проще: сначала отсеиваем невалидные случаи, затем идет основной сценарий.

    Циклы: когда нужно повторять действие

    Циклы помогают, когда у вас:

  • список данных, который нужно отобразить
  • набор элементов на странице, к которым нужно применить действие
  • повторяемая операция (например, несколько раз попытаться получить корректный ввод)
  • for: когда нужен счетчик

    Части цикла:

  • let i = 0 — стартовое значение счетчика
  • i < 3 — условие продолжения (пока оно true, цикл идет)
  • i += 1 — шаг после каждой итерации
  • Документация: MDN: for

    for...of: удобно для массивов и NodeList

    Для массивов часто читается проще:

    В браузере for...of часто используют и для коллекций элементов, найденных через querySelectorAll (это вернем и закрепим в DOM-темах).

    Документация: MDN: for...of

    while: когда неизвестно число повторов заранее

    while полезен, но в UI важно не делать бесконечных циклов, которые замораживают страницу.

    Документация: MDN: while

    break и continue

  • break — остановить цикл полностью
  • continue — перейти к следующей итерации
  • Функции: основной способ собирать UI-логику

    Функция — это переиспользуемый блок кода, который можно вызвать по имени.

    Параметры и return

    Колбэки: функции как данные

    Во front-end вы постоянно передаете функции другим функциям. Классический пример — обработчики событий.

    Документация: MDN: addEventListener

    Мини-практика: обновление UI через функции, условия и циклы

    Ниже пример подхода, который будет полезен и в обычном DOM-коде, и в React: хранить состояние в переменной и иметь отдельную функцию render, которая обновляет интерфейс.

    Предположим, что на странице уже есть элементы:

  • элемент с id countText для отображения числа
  • кнопки с id incBtn и decBtn
  • элемент с id errorText для сообщений об ошибке
  • Код:

    Что здесь важно:

  • условие защищает UI от невалидного состояния
  • функции разделяют ответственность: setCount меняет данные и инициирует обновление, render рисует
  • DOM меняется в одном местеrender), а не размазан по всему коду
  • Где здесь могут быть циклы

    Счетчик сам по себе не требует циклов, но на практике UI часто содержит списки. Идея выглядит так: берем массив данных, создаем элементы в цикле, добавляем в контейнер.

    Пример с массивом строк (без вставки HTML-строк, только через createElement):

    Документация: MDN: document.querySelector

    Мостик к React

    В React те же идеи встречаются постоянно:

  • условия — для условного отображения (показывать ошибку или нет)
  • циклы (часто через методы массива) — для рендера списков
  • функции — основа всего: компоненты и обработчики событий
  • Главная привычка, которая пригодится дальше: обновление UI запускается из изменения данных, а сама отрисовка вынесена в отдельную часть (у нас это была функция render). В React этим занимается механизм рендера компонентов.

    Что дальше

    В следующей части курса мы углубимся в DOM: как устроено дерево элементов, как находить элементы надежно, менять текст, классы и атрибуты, а также как правильно работать с событиями, чтобы строить интерактивные интерфейсы.

    4. Объекты и массивы в задачах фронтенда

    Объекты и массивы в задачах фронтенда

    После тем про типы данных, условия, циклы и функции пора закрепить то, без чего почти не бывает интерфейсов: объекты и массивы. Во front-end они отвечают за данные, из которых вы “рисуете” UI: профиль пользователя, список задач, карточки товаров, настройки формы.

    Ключевая идея: UI проще строить, когда данные имеют понятную структуру, а обновление данных делается аккуратно (особенно когда позже появится React).

    !Связь структуры данных (массив объектов) и отрисовки списка в интерфейсе

    Объекты

    Объект — это структура “ключ-значение”. В интерфейсах объекты описывают сущности: пользователя, товар, одну задачу в todo-листе.

    Создание и доступ к полям

    Два способа доступа:

  • user.name удобно, когда ключ известен заранее
  • user[key] нужно, когда ключ хранится в переменной
  • Изменение и добавление полей

    Важно: если объект объявлен через const, его поля менять можно — нельзя только переназначить саму переменную.

    Удаление поля

    delete существует, но в UI-коде его используют редко (часто проще создать новый объект без поля).

    Документация:

  • MDN: Объекты
  • Массивы

    Массив — упорядоченный список значений. В UI массивы чаще всего означают списки, которые нужно показать: комментарии, товары, задачи.

    Базовые операции

    Полезные методы на старте:

  • push, pop — добавить/убрать в конце
  • unshift, shift — добавить/убрать в начале (обычно реже)
  • includes — проверить наличие
  • indexOf — найти индекс
  • Документация:

  • MDN: Array
  • Массив объектов: самая частая форма данных в интерфейсе

    Типичный пример: todo-лист.

    Что здесь удобно:

  • массив = коллекция
  • объект = один элемент коллекции со всеми нужными полями
  • Итерация по массивам для UI

    В прошлой теме вы видели циклы for и for...of. Для UI задач часто удобно сочетать их с созданием DOM-элементов.

    Рендер списка через for...of и createElement

    Предположим, на странице есть контейнер #listRoot.

    Идея, которая пригодится дальше: данные отдельно, отрисовка отдельно. Мы передаем данные в renderTasks, а DOM обновляем в одном месте.

    Частые задачи с объектами и массивами во front-end

    Поиск элемента по id

    Когда пользователь кликает “выполнено”, “удалить”, “редактировать”, вы почти всегда работаете с id.

    Документация:

  • MDN: Array.prototype.find
  • Фильтрация (например, показать только активные)

    Документация:

  • MDN: Array.prototype.filter
  • Преобразование данных для отображения

    Например, сделать массив строк для списка.

    Документация:

  • MDN: Array.prototype.map
  • Мутация и иммутабельность: почему это важно для UI и React

    Есть два подхода к обновлению объектов и массивов.

  • Мутация — изменить существующий объект/массив
  • Иммутабельный подход — создать новую версию данных
  • В обычном DOM-коде мутация иногда допустима, но чем сложнее интерфейс, тем выгоднее привычка: создавать новую версию данных. В React это почти обязательное правило, потому что обновления завязаны на то, что ссылка на объект/массив поменялась.

    Пример: отметить задачу как выполненную (без мутации массива)

    Что здесь происходит:

  • map создает новый массив
  • для изменяемой задачи мы создаем новый объект { ...t, done: ... }
  • остальные элементы возвращаем как есть
  • Документация:

  • MDN: Spread syntax
  • Пример: добавить задачу (без мутации)

    Пример: удалить задачу (без мутации)

    Деструктуризация: удобное чтение данных

    Деструктуризация помогает аккуратно извлекать поля, особенно когда рисуете UI.

    Документация:

  • MDN: Деструктурирующее присваивание
  • Безопасный доступ к полям: optional chaining и значения по умолчанию

    Во front-end часто бывает, что данные еще не пришли, или часть полей отсутствует. Вместо цепочек проверок можно использовать optional chaining ?..

    Если нужно подставить значение по умолчанию, используйте ?? (nullish coalescing): он срабатывает только для null и undefined.

    Документация:

  • MDN: Optional chaining
  • MDN: Nullish coalescing
  • Данные из DOM: строки, dataset и приведение типов

    В DOM вы часто получаете строки, даже если “по смыслу” это число.

    Типичный паттерн: хранить id в data-* атрибуте.

    Документация:

  • MDN: HTMLElement.dataset
  • Сохранение массивов/объектов: JSON и localStorage

    localStorage хранит только строки. Поэтому объекты и массивы обычно сохраняют через JSON.

    Что важно:

  • JSON.stringify(value) превращает объект/массив в строку
  • JSON.parse(text) превращает строку обратно в объект/массив
  • getItem может вернуть null, поэтому нужна проверка
  • Документация:

  • MDN: JSON.stringify
  • MDN: JSON.parse
  • MDN: Window.localStorage
  • Мостик к React

    В React данные почти всегда выглядят как массивы объектов и объекты состояния.

    Что из этой темы напрямую переносится в React:

  • иммутабельные обновления: setState(next) обычно получает новые массивы/объекты
  • рендер списков: tasks.map(...) превращается в набор компонентов
  • работа с id: в React при рендере списков используется key, и обычно это тот же id
  • Мы подробно закрепим это, когда начнем основы React, но привычки стоит тренировать уже сейчас.

    Что дальше

    Следующий логичный шаг — применить объекты и массивы к DOM на практике: научиться надежно находить элементы, навешивать события, читать ввод пользователя и обновлять интерфейс без хаоса в коде. Это будет основой для любого интерактивного front-end приложения и хорошей подготовкой к React.

    5. DOM: поиск элементов, изменения и стили

    DOM: поиск элементов, изменения и стили

    DOM (Document Object Model) — это представление HTML-страницы в виде дерева объектов, с которым JavaScript может работать: находить элементы, менять текст, классы, атрибуты, создавать новые узлы и удалять старые.

    Эта тема логично продолжает прошлые: теперь ваши условия, функции, объекты и массивы начинают напрямую влиять на интерфейс. Вы будете хранить данные в переменных и структурах данных, а затем отражать их в DOM.

    !DOM как дерево элементов и точки, где JavaScript “подключается” к узлам

    Полезная справка: MDN: Введение в DOM

    Как безопасно подключать скрипт к странице

    Когда JavaScript пытается найти элементы, которых еще нет в DOM, вы получите null.

    Рабочие подходы:

  • Подключать скрипт так, чтобы он выполнялся после загрузки разметки (часто это решается настройкой проекта или порядком подключения).
  • Либо ждать событие загрузки DOM.
  • Пример ожидания готовности DOM:

    Документация: MDN: DOMContentLoaded

    Поиск элементов в DOM

    Поиск элементов — первый шаг почти в любой UI-задаче: прочитать значение инпута, повесить обработчик на кнопку, обновить текст, переключить класс.

    querySelector и querySelectorAll

    Это самые универсальные методы: они принимают CSS-селектор.

  • document.querySelector(selector) возвращает первый найденный элемент или null.
  • document.querySelectorAll(selector) возвращает коллекцию (обычно NodeList), даже если элементов 0.
  • Документация:

  • MDN: Document.querySelector
  • MDN: Document.querySelectorAll
  • getElementById и другие методы

    Иногда удобно использовать специализированные методы:

  • document.getElementById("id") быстро ищет по id.
  • Документация: MDN: Document.getElementById

    Поиск внутри конкретного элемента

    Частый прием: сначала найти контейнер, а затем искать внутри него. Это делает код устойчивее, когда на странице несколько похожих блоков.

    Проверка на null

    querySelector может вернуть null. Поэтому либо делайте проверку, либо используйте ранний выход (подход из темы про функции и условия).

    Чтение и изменение содержимого

    textContent

    textContent — самый надежный способ читать и менять текст.

    Почему это удобно во front-end:

  • вы явно работаете именно с текстом
  • меньше неожиданных эффектов
  • Документация: MDN: Node.textContent

    Значения полей формы: value

    Для input, textarea, select основное — свойство value.

    Документация:

  • MDN: HTMLInputElement.value
  • MDN: HTMLTextAreaElement.value
  • Атрибуты и data-*

    Атрибуты — это то, что обычно видно в разметке (например, id, href, disabled). В JavaScript вы можете читать и задавать их.

    getAttribute и setAttribute

    Документация:

  • MDN: Element.getAttribute
  • MDN: Element.setAttribute
  • dataset для связи UI и данных

    Для UI-логики часто нужно хранить идентификатор элемента: taskId, productId. Удобный паттерн — data-* атрибуты.

    Если у элемента есть data-id="42", то в JS это будет element.dataset.id.

    Документация: MDN: HTMLElement.dataset

    Классы и стили

    В интерфейсе чаще всего вы управляете отображением через классы: показываете/скрываете блок, подсвечиваете ошибку, меняете состояние кнопки.

    Управление классами через classList

    classList позволяет добавлять, удалять и переключать классы.

    Полезные методы:

  • classList.add("a")
  • classList.remove("a")
  • classList.toggle("a")
  • classList.toggle("a", condition)
  • Документация: MDN: Element.classList

    Инлайновые стили через style

    Иногда нужно быстро задать стиль напрямую. Это работает, но в реальных проектах чаще предпочитают классы.

    Документация: MDN: HTMLElement.style

    Что выбирать: классы или style

    Практическое правило:

  • если это состояние (ошибка, активный пункт, скрыто) — используйте классы
  • если это значение, зависящее от данных (например, ширина прогресса) — иногда удобен style
  • Пример “зависит от данных”:

    Важно:

  • данные обновляются иммутабельно: tasks = tasks.map(...)
  • DOM обновляется в одном месте
  • Мостик к React

    То, что вы сделали руками в DOM, в React обычно выражается так:

  • состояние хранится в useState
  • рендер — это возвращаемая разметка компонента
  • классы и стили зависят от данных (условия и выражения)
  • списки рисуются через map
  • Если вы привыкнете мыслить так: сначала данные, потом отображение, переход к React будет гораздо проще.

    Что дальше

    В следующей теме логично закрепить DOM через события: как правильно обрабатывать клики и ввод, как получать event, как использовать делегирование событий для списков, и как строить интерактивные компоненты без хаоса в коде.

    6. События, формы и валидация на клиенте

    События, формы и валидация на клиенте

    Интерактивный front-end держится на трех вещах:

  • события (клики, ввод, отправка формы)
  • формы (как получить данные пользователя)
  • валидация на клиенте (как быстро подсказать пользователю, что нужно исправить)
  • В прошлых темах вы научились находить элементы и менять DOM. Теперь добавляем реакцию на пользователя: вы будете подписываться на события через addEventListener, получать объект события event, читать значения полей и предотвращать стандартное поведение браузера.

    Что такое событие

    Событие — это сигнал от браузера: “пользователь кликнул”, “в поле ввели текст”, “форма отправлена”, “нажали клавишу”. Вы подписываетесь на событие и передаете функцию-обработчик.

    Базовая схема:

  • Находим элемент.
  • Подписываемся на событие.
  • В обработчике читаем данные и обновляем UI.
  • Документация:

  • MDN: addEventListener
  • MDN: Event
  • Подписка на события через addEventListener

    Предположим, на странице есть кнопка #saveBtn и текстовый блок #status.

    Практические правила:

  • Старайтесь объявлять обработчики отдельными функциями, если логика больше пары строк.
  • Не забывайте проверять элементы на null, если есть риск, что селектор ничего не найдет.
  • Объект события event: что в нем полезного

    В обработчик браузер передает объект event.

    Полезные поля и идеи:

  • event.type — тип события ("click", "input", "submit").
  • event.targetгде событие началось (часто это конкретная кнопка/иконка внутри).
  • event.currentTargetна чем висит обработчик.
  • Документация:

  • MDN: Event.target
  • MDN: Event.currentTarget
  • target и currentTarget: почему это важно

    Это критично для списков и делегирования событий.

    Пример идеи:

  • Обработчик навешан на контейнер списка.
  • Пользователь кликает по кнопке внутри одного элемента списка.
  • event.target указывает на реальный элемент клика.
  • event.currentTarget указывает на контейнер.
  • Всплытие событий и stopPropagation

    По умолчанию многие события всплывают: сначала срабатывает обработчик на самом вложенном элементе, затем выше по дереву.

    !Диаграмма объясняет всплытие и разницу между target и currentTarget

    Иногда всплытие полезно (делегирование), иногда мешает (когда клик по вложенной кнопке не должен запускать обработчик на родителе).

    event.stopPropagation() останавливает всплытие.

    Документация:

  • MDN: Event.stopPropagation
  • Делегирование событий для списков

    Если у вас список из 100 элементов, можно:

  • повесить 100 обработчиков
  • повесить 1 обработчик на контейнер и определять, по чему кликнули
  • Второй вариант часто проще и быстрее в обслуживании.

    Предположим, у контейнера #listRoot внутри есть кнопки с data-action="remove" и data-id="...".

    Ключевые моменты:

  • Используем dataset как мост между DOM и данными.
  • В обработчике сначала делаем защитные проверки и ранние return.
  • Обновляем данные (лучше иммутабельно), а затем вызываем render (паттерн из прошлых тем).
  • Формы: submit, input, change

    Форма — это основной способ собрать данные пользователя.

    Событие submit и preventDefault

    При отправке формы браузер обычно:

  • пытается отправить данные
  • может перезагрузить страницу (зависит от настроек)
  • В SPA-подходе (и в учебных проектах) чаще нужно обработать отправку в JavaScript и не перезагружать страницу.

    event.preventDefault() отменяет стандартное действие браузера.

    Документация:

  • MDN: Event.preventDefault
  • MDN: submit event
  • Событие input: реагируем на ввод сразу

    input срабатывает почти на каждое изменение значения поля.

    Документация:

  • MDN: input event
  • Событие change: когда значение “зафиксировано”

    change часто используют для:

  • select
  • чекбоксов
  • случаев, когда нужно реагировать не на каждую букву, а на завершение изменения
  • Документация:

  • MDN: change event
  • Как собрать данные формы: FormData

    Если полей много, вручную читать каждое input.value неудобно. В браузере есть FormData, которая собирает данные формы.

    Пояснения:

  • new FormData(form) читает значения полей формы по их именам.
  • fd.get("name") может вернуть null, поэтому подставляем пустую строку.
  • Явно приводим к строке через String(...).
  • Документация:

  • MDN: FormData
  • Валидация на клиенте: зачем и какие есть уровни

    Клиентская валидация нужна, чтобы:

  • дать быстрый фидбек пользователю
  • уменьшить количество очевидно неверных отправок
  • Но важно помнить:

  • клиентская валидация не заменяет серверную
  • пользователь может отключить JavaScript или отправить запрос в обход UI
  • Минимальная ручная валидация

    Базовый подход:

  • На submit собрать значения.
  • Проверить правила.
  • Если ошибка — показать сообщение и не продолжать.
  • Идея, которая пригодится в React:

  • держать значения формы в одном объекте values
  • держать ошибки в одном объекте errors
  • рендерить UI из этих данных
  • Constraint Validation API: встроенная валидация браузера

    Браузер умеет валидировать поля по ограничениям и дает полезные методы.

    Полезные свойства и методы:

  • input.validity — набор флагов, почему поле невалидно
  • input.checkValidity() — проверка, валидно ли поле
  • form.checkValidity() — проверка всей формы
  • input.setCustomValidity(message) — задать собственное сообщение
  • Пример: кастомная проверка “пароли совпадают”.

    Документация:

  • MDN: Constraint validation
  • MDN: HTMLInputElement.setCustomValidity
  • MDN: HTMLFormElement.checkValidity
  • Практический паттерн: состояние формы + render

    Чтобы код не превращался в хаос, удобно придерживаться подхода:

  • данные формы храним в объекте
  • ошибки храним в объекте
  • в одном месте обновляем UI
  • Этот подход напрямую переносится в React, где values и errors обычно живут в useState, а render — это JSX-разметка.

    Мостик к React

    В React все те же идеи:

  • обработчики событий — это функции, которые вы передаете в onClick, onChange, onSubmit
  • вместо ручного render() вы меняете состояние (setValues, setErrors), и React сам перерисовывает компонент
  • preventDefault() в onSubmit используется так же
  • делегирование событий обычно нужно реже, потому что списки рендерятся через данные, но понимание target/currentTarget все равно помогает
  • Что дальше

    Теперь вы умеете строить полноценные интерактивные элементы: реагировать на действия пользователя, собирать данные формы и валидировать ввод. Следующий шаг — применить эти навыки в более “компонентном” стиле и перейти к базовым основам React: компоненты, props, состояние и рендер списков.

    7. Асинхронность, Fetch API и основы React

    Асинхронность, Fetch API и основы React

    В прошлых темах вы научились управлять интерфейсом через DOM: находить элементы, менять текст и классы, обрабатывать события, собирать данные форм и валидировать их. Следующий шаг для реальных front-end задач — научиться работать с асинхронностью: ждать ответы сервера, показывать состояние загрузки, обрабатывать ошибки, а затем перейти к компонентному подходу через базовые идеи React.

    Что такое асинхронность во front-end

    Во front-end часто встречаются операции, которые не дают результат сразу:

  • запросы к серверу
  • таймеры
  • чтение файлов (например, в input type file)
  • ожидание событий пользователя
  • Если написать код как будто результат доступен сразу, UI будет вести себя неправильно. Поэтому в JavaScript есть механизмы, которые позволяют:

  • запустить операцию
  • продолжить выполнение кода
  • получить результат позже
  • !Иллюстрация показывает, почему JS может «ждать» сеть и не блокировать страницу

    Полезные источники:

  • MDN: Асинхронный JavaScript
  • MDN: Event loop
  • Колбэки как простейший способ «сделать позже»

    Колбэк — это функция, которую вызывают позже.

    Пример с таймером:

    Важно:

  • setTimeout не блокирует выполнение
  • переданная функция выполнится позже
  • Документация:

  • MDN: setTimeout
  • Promise: стандартная модель асинхронных результатов

    Promise — объект, который представляет результат асинхронной операции.

    У промиса есть состояния:

  • pending — ожидание
  • fulfilled — успешно
  • rejected — ошибка
  • С промисами работают через .then() и .catch().

    Почему важна проверка response.ok:

  • fetch считает запрос успешным, если сеть сработала
  • но сервер может вернуть 404/500, и это не станет автоматической ошибкой
  • POST-запрос: отправка данных на сервер

    Для отправки JSON обычно задают:

  • method: "POST"
  • заголовок Content-Type: "application/json"
  • body: JSON.stringify(data)
  • Документация:

  • MDN: AbortController
  • Практический UI-паттерн: loading, error, data

    В DOM-темах вы использовали паттерн состояние + render. Для запросов почти всегда появляется три состояния:

  • loading — показываем загрузку
  • error — показываем сообщение
  • data — показываем результат
  • Ниже пример на чистом DOM (идея важнее конкретной разметки).

    В JSX это выглядит привычнее, но важно запомнить идею: props приходят извне и не должны изменяться самим компонентом.

    Документация:

  • React: Passing Props to a Component
  • State и useState: внутренние данные компонента

    State — данные, которые живут внутри компонента и меняются со временем.

    Хук useState возвращает:

  • текущее значение
  • функцию для обновления
  • Пример счетчика:

    Ключевая мысль:

  • вы не вызываете render() вручную
  • вы вызываете setCount(...), и React сам обновляет UI
  • Документация:

  • React: State
  • События в React: onClick, onChange, onSubmit

    В React обработчики похожи по смыслу на addEventListener, но задаются прямо в разметке.

    Идея:

  • в DOM вы делали element.addEventListener("click", handler)
  • в React вы пишете onClick={handler}
  • Документация:

  • React: Responding to Events
  • Условный рендеринг и списки

    Условия

    В React вы часто показываете блок только при условии.

    Документация:

  • React: Conditional Rendering
  • Списки

    Списки рендерят через map. Для каждого элемента нужен key.

    Документация:

  • React: Rendering Lists
  • Запросы в React: useEffect + fetch

    Чтобы загрузить данные при открытии компонента, обычно используют useEffect.

  • useEffect запускает побочный эффект (например, запрос)
  • пустой массив зависимостей [] означает: выполнить при монтировании компонента
  • Пример концепции loading/error/data уже в React:

    Что здесь важно на базовом уровне:

  • fetch остается тем же
  • состояние хранится в useState
  • запуск при старте компонента делаем через useEffect
  • Документация:

  • React: Synchronizing with Effects
  • Как эта тема связывает DOM и React

    Связка получается такая:

  • в DOM-подходе вы управляли интерфейсом вручную через render() и изменения узлов
  • в React вы управляете интерфейсом через состояние, а обновление DOM берет на себя React
  • Если вы уже привыкли к паттерну состояние + render из прошлых уроков, то React будет восприниматься как более удобная и масштабируемая версия этой же идеи.