Перейти к содержимому

DOM модель

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

Устройство DOM и подготовка

Элементы HTML образуют вложенную структуру:

<section id="profile">
    <h2>Анна</h2>
    <p>Изучаю веб-разработку</p>
</section>

Здесь section является родителем элементов h2 и p. Текст внутри них тоже представлен узлами. Схема показывает эту структуру без текстовых узлов с межтеговыми пробелами:

section#profile
    h2
        текст: Анна
    p
        текст: Изучаю веб-разработку

Объект document представляет весь документ. Изменения DOM не записываются в HTML-файл: после перезагрузки браузер заново читает исходные файлы и выполняет скрипты.

Откройте папку website с визиткой и сохраните её копию. Для экспериментов подготовьте practice.html, assets/css/practice.css и assets/js/practice.js. Код визитки оставьте в index.html, assets/css/style.css и assets/js/script.js.

В practice.html подготовьте полный документ:

<!DOCTYPE html>
<html lang="ru">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Практика DOM</title>
    <link rel="stylesheet" href="assets/css/practice.css">
    <script src="assets/js/practice.js" defer></script>
</head>
<body>
    <h1>Практика DOM</h1>
</body>
</html>

Внешний скрипт с defer выполняется после разбора HTML, когда элементы уже доступны. Примеры запускайте по одному, заменяя содержимое <body>, practice.css и practice.js. В .js записывайте только JavaScript, без тегов <script>.

В LiveCodes изменяйте код во вкладке JS и сравнивайте результат с заданием. После правки кода пример запускается заново.

Если редактор не загрузился, раскройте «Код примера» и перенесите HTML, CSS и JS в тренировочные файлы. Изменения из редактора сохраняйте в локальные файлы самостоятельно.

Откройте инструменты разработчика через меню браузера или F12. Во вкладке Elements (в Firefox: Инспектор) виден текущий DOM, включая изменения JavaScript. Во вкладке Console появляются сообщения и ошибки скрипта.

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

Чтобы изменить элемент, сначала получите ссылку на него. Методы querySelector и querySelectorAll используют CSS-селекторы:

Вызов Результат
document.getElementById("profile-name") Элемент с указанным id или null. Знак # в аргументе не нужен
document.querySelector(".topic") Первый подходящий элемент или null
document.querySelectorAll(".topic") Коллекция NodeList из всех подходящих элементов. Если совпадений нет, её length равна 0

Свойство textContent позволяет прочитать или заменить текст элемента. При записи вложенные элементы тоже заменяются текстом. Ссылку на элемент можно хранить в const: изменение его свойств не является переприсваиванием переменной.

В примере заголовок изменится на «Анна: учебная страница», под списком появится количество тем, а их названия попадут в консоль редактора. Добавьте в HTML третий <li class="topic">DOM</li> и проверьте новое количество.

Поиск заголовка и списка тем

Код примера

Коллекцию NodeList можно перебрать циклом for...of. В отличие от массива, у неё нет метода push. Результат querySelectorAll статический: добавленные позже элементы не попадают в уже полученную коллекцию.

Почему возникает ошибка при обращении к textContent

Обращение к textContent у null вызовет TypeError. Сверьте селектор и подключение скрипта с defer. Если элемент может отсутствовать, добавьте проверку:

const heading = document.querySelector("#optional-heading");
if (heading !== null) {
    heading.textContent = "Дополнительный раздел";
}

Свойство innerHTML разбирает строку как HTML. Для пользовательского ввода используйте textContent, чтобы введённые теги отображались как текст, а не становились разметкой.

Внутри <main> визитки добавьте раздел «Учебный прогресс» с абзацем id="progress-summary". В script.js сформируйте сообщение из имени в profile и результата summarizeTopics(topics): завершённые и общие занятия, остаток, процент и статус. Выведите сообщение в абзац через textContent. Для пустого массива покажите «Нет тем» без процента.

Для тем HTML 4 из 4, CSS 3 из 6 и JavaScript 0 из 5 на странице должны появиться общие значения 7 из 15, 47% и остаток 8. Измените данные одной темы, обновите страницу и проверьте, что текст пересчитался.

События, классы и свойства

Событие сообщает о действии: click о нажатии, input об изменении ввода. Метод element.addEventListener("click", handler) назначает функцию-обработчик. Передаётся сама функция, без вызова: браузер вызовет её при нажатии. В примере обработчик записан стрелочной функцией () => { ... }.

Для изменения оформления используйте classList: add("highlighted") добавляет класс, remove("highlighted") удаляет, а toggle("highlighted") переключает. toggle возвращает true, если класс появился, и false, если исчез. Имя класса передаётся без точки.

Нажмите кнопку дважды. После первого нажатия карточка получит фон и обводку, после второго вернётся к исходному виду. Проверьте изменение атрибута class в инспекторе.

Переключение выделения карточки

Код примера

Метод setAttribute("имя", "значение") задаёт HTML-атрибут. В примере aria-pressed сообщает вспомогательным технологиям, нажата ли кнопка-переключатель. String(...) превращает логическое значение в строку. Для чтения атрибута есть getAttribute, для удаления removeAttribute.

У многих атрибутов есть соответствующие DOM-свойства, например link.href или input.id. Но свойство и атрибут не всегда совпадают: input.value содержит текущий ввод, а input.getAttribute("value") читает исходное значение из разметки.

Добавьте к разделу прогресса визитки кнопку выделения с type="button" и собственный CSS-класс. В script.js назначьте обработчик, который переключает класс и обновляет aria-pressed. Проверьте кнопку мышью и с клавиатуры: Tab переводит на неё фокус, Enter или пробел вызывает нажатие.

Создание и удаление элементов

document.createElement("li") создаёт новый элемент, но ещё не помещает его на страницу. Сначала можно заполнить его текст и свойства, затем вставить в родителя через appendChild. Метод element.remove() удаляет выбранный элемент из DOM.

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

Список с добавлением и удалением

Код примера

Поиск можно ограничить родителем: list.querySelector("li:last-child") выбирает последний пункт внутри list. Псевдокласс :last-child означает последний дочерний элемент. В пустом списке результат равен null, поэтому перед удалением нужна проверка.

Функция updateCount вызывается при запуске и после каждого изменения списка. Повторный поиск через querySelectorAll даёт текущее количество пунктов. Счётчик nextTopic задаёт номер новой темы и при удалении не уменьшается.

В тренировочных файлах измените удаление так, чтобы удалялся первый пункт. Подберите селектор и измените подпись кнопки. Проверьте порядок пунктов, счётчик и пустой список.

Формы и проверка ввода

Форма <form> объединяет поля и кнопку отправки. Атрибут action задаёт адрес получателя, а method способ отправки. JavaScript может отменить отправку и обработать данные прямо на странице.

Свяжите каждое поле с подписью <label>: её for должен совпадать с id поля. Атрибут name задаёт имя поля при отправке. Для кнопки отправки используйте type="submit", для остальных кнопок формы указывайте type="button".

Для многострочного текста используется <textarea></textarea>. В отличие от <input>, ему нужен закрывающий тег. Текущий текст обоих полей читается через .value.

Встроенная проверка браузера работает без JavaScript:

Атрибут Проверка
required Поле обязательно. Для текстового поля строка из пробелов не считается пустой
type="email" Базовый формат адреса электронной почты, но не существование почтового ящика
minlength="10" Минимальная длина введённого пользователем текста
maxlength="200" Ограничение длины пользовательского ввода
type="number", min, max, step Числовой ввод, границы и допустимый шаг

minlength не делает поле обязательным: для запрета пустой строки нужен required. Дополнительные правила, например запрет имени из пробелов, проверяются в JavaScript.

При нажатии кнопки отправки браузер сначала проверяет ограничения HTML. Если найдена ошибка, он показывает подсказку и не вызывает событие submit. Если ограничения соблюдены, обработчик submit получает объект события. Вызов event.preventDefault() отменяет стандартную отправку и переход на другую страницу.

В примере обработчик читает имя через .value и выводит приветствие. Введите имя и нажмите кнопку: текст появится без перезагрузки страницы.

Чтение значения из формы

Код примера

role="status" позволяет вспомогательным технологиям объявлять появившееся сообщение. Описывайте ошибку текстом, а не только цветом.

Сравните пустое поле и поле из нескольких пробелов. Метод строки .trim() удаляет крайние пробелы, а .focus() переводит фокус на поле. Дополните обработчик: вместо приветствия для имени из пробелов должно появляться сообщение об ошибке.

Для тренировки создайте в practice.html форму обратной связи с именем, адресом электронной почты и сообщением. Все поля обязательны. Имя не должно состоять из пробелов, сообщение после удаления крайних пробелов должно содержать не менее 10 символов. При корректном вводе покажите данные на странице, при ошибке укажите проблемное поле.

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

Счётчик символов и выбор значений

Событие input срабатывает при изменении поля пользователем, включая вставку и удаление текста. Поэтому оно подходит для счётчика символов.

В примере отображается длина названия заметки через .value.length. Функция обновления вызывается при вводе и один раз при запуске.

Длина названия заметки

Код примера

aria-describedby связывает поле с пояснением по его id. При загрузке длина названия равна 5. Замените его своим текстом, вставьте несколько слов и очистите поле. Проверьте, что длина обновляется во всех случаях. Пробелы тоже учитываются.

Длина строки и программное изменение поля

Свойство .length и атрибут maxlength считают кодовые единицы UTF-16. Некоторые видимые символы занимают две единицы, поэтому для проверки границ используйте обычные русские или латинские буквы.

Присваивание field.value = "текст" из скрипта не вызывает input. После него функцию обновления счётчика нужно вызвать явно.

У полей type="checkbox" и type="radio" свойство .value содержит значение варианта, а .checked показывает, выбран ли он. Селектор :checked отбирает отмеченные элементы. Radio с одинаковым name образуют группу с единственным выбором. Для оформления группы используйте <fieldset> с подписью <legend>.

В тренировочный <body> поместите группу вариантов:

<fieldset>
    <legend>Способы связи</legend>
    <label><input type="checkbox" name="contactMethod" value="Почта"> Почта</label>
    <label><input type="checkbox" name="contactMethod" value="Телефон"> Телефон</label>
</fieldset>

Подпись может оборачивать поле: в такой записи for не нужен. Селектор [name="contactMethod"]:checked выбирает отмеченные варианты этой группы. Добавьте кнопку и вывод выбранных способов связи. Если отметок нет, должно появиться «Ничего не выбрано».

Для radio поиск выбранного элемента может вернуть null. Для группы checkbox проверяйте количество отмеченных вариантов: required на каждом поле потребует отметить их все.

Даже у type="number" свойство .value является строкой. Проверьте пустоту до преобразования: Number("") даст 0. Затем используйте Number(...), Number.isFinite(age) и проверку границ. Для дробных чисел задайте step="any": стандартный шаг равен 1.

Динамически созданные поля имеют те же свойства, что и поля из HTML: type, name, id, required. У подписи свойство .htmlFor соответствует атрибуту for. Значения id должны быть уникальными, а name у полей одной группы может повторяться.

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

Самостоятельная работа: анкета

Создайте анкету в survey.html, assets/css/survey.css и assets/js/survey.js. Подключите скрипт с defer. Проверяйте данные и выводите результат на странице без отправки на сервер.

Поле Требование
Имя Обязательно. Не должно быть пустым или состоять из пробелов
Возраст Обязательное конечное число больше 0 и не больше 120. Дробные значения допустимы
Пол Группа radio, выбор необязателен. При отсутствии выбора выводите «Не указан»
Интересы Checkbox с вариантами «Спорт», «Музыка», «Кино» и кнопка добавления собственных интересов
Собственные интересы Добавление необязательно, но каждое созданное поле должно быть заполнено и не состоять из пробелов
Комментарий Необязателен. Длина .value.length не больше 200, включая пробелы

Нужен хотя бы один интерес: отмеченный checkbox или заполненное собственное поле. Проверяйте все добавленные поля, даже если checkbox уже отмечен. Повторяющиеся интересы допустимы.

Рядом с комментарием показывайте количество введённых символов и остаток до лимита. Счётчик должен работать сразу после загрузки, при вводе, вставке и удалении. Ограничьте ввод длиной 200 и проверяйте длину также в JavaScript.

Каждое новое поле интереса должно иметь связанную подпись, уникальный id и получать фокус после добавления. Кнопка добавления не должна отправлять форму.

При ошибке покажите понятное сообщение и переведите фокус к проблемному полю. После успешной проверки выведите имя, возраст, пол, все интересы и комментарий с подписями. Пустой комментарий обозначьте как «Нет комментария», результат как «Данные проверены». Введённые теги должны отображаться текстом.

Повторная проверка должна обновлять результат, а не добавлять копию. Изменение ввода или добавление поля должно очищать старый результат и сообщение об ошибке. Введённые значения оставляйте в форме. Сохраните встроенную проверку браузера и дополните её правилами из таблицы.

Проверка и сдача

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

Действие или ввод Ожидаемое поведение анкеты
Пустое имя или только пробелы Ошибка
Возраст пустой, 0, -1 или 121 Ошибка
Возраст 0.5 или 120 Допустим
Попытка ввести нечисловой возраст Браузер отклоняет ввод или форма сообщает ошибку
Пол не выбран В результате «Не указан»
Нет выбранных и собственных интересов Ошибка с пояснением, что нужен хотя бы один интерес
Только «Музыка» или только собственный интерес «Шахматы» Оба варианта допустимы
Есть checkbox, но добавленное поле пусто или содержит пробелы Ошибка до заполнения этого поля
После первой проверки добавлены ещё два интереса Новые поля участвуют в следующей проверке и выводе
Комментарий пустой, длиной 199 или 200 Допустим. Остаток равен 200, 1 или 0 соответственно
Попытка ввести или вставить больше 200 букв Длина ограничена, счётчик соответствует фактическому тексту
В имени введено <b>Анна</b> Теги видны как текст, элемент b в результате не создаётся
Успешная проверка, изменение поля, повторная проверка Старый результат очищается, затем появляется один обновлённый результат
Ошибка, исправление поля, повторная проверка Сообщение об ошибке исчезает, корректные данные принимаются

Уменьшите ширину окна: подписи и сообщения должны оставаться читаемыми, поля не должны выходить за границы страницы. Пройдите форму клавишей Tab и нажмите Enter на кнопке проверки. Enter внутри textarea должен добавлять перенос строки.

  • Визитка сохранила оформление и навигацию. Сводка выводится на страницу, пустой массив тем обработан.
  • Кнопка выделения переключает CSS-класс и aria-pressed, работает с клавиатуры.
  • Анкета содержит все поля из таблицы и проверяет актуальные динамические поля.
  • Счётчик комментария верен при загрузке, вводе, вставке и удалении.
  • Пользовательские данные выводятся как текст. Ошибка и повторная проверка не оставляют старую сводку.
  • Все случаи из таблицы проверены. В консоли нет необработанных ошибок.
  • Можно объяснить различия между HTML и DOM, value и textContent, input и submit, встроенной и собственной проверкой.

Сдайте папку website с визиткой, страницей survey.html и подключёнными CSS/JS-файлами. Упражнения в practice.html предназначены для тренировки и отдельно не сдаются. Проверьте, что обе сдаваемые страницы открываются и работают локально.

Справка: введение в DOM, querySelectorAll, ограничения и валидация форм.