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

Работа с HTML в JavaScript

Разработчику Архитектору

Работа с HTML в JavaScript

JavaScript обладает мощным инструментом для взаимодействия со структурой веб-страницы, который называется Document Object Model или сокращенно DOM. Этот механизм представляет собой дерево объектов, где каждый элемент HTML-кода становится объектом с набором свойств и методов. Скрипт может изменять внешний вид страницы, добавлять новые блоки, удалять существующие элементы и реагировать на действия пользователя без перезагрузки всей страницы.

DOM как стандарт и дерево узлов

Document Object Model (DOM) — платформенный API браузера, а не часть ECMAScript. Его описывают спецификации W3C и WHATWG (Living Standard). Браузер при разборе HTML строит дерево узлов — корень — document, внутри — элементы (Element), текст (Text), комментарии, атрибуты как объекты Attr.

Узел / объектРоль
Documentвесь документ, точка входа (document)
Elementтеги (div, button, …)
Textтекстовые фрагменты внутри элементов
DocumentFragmentлёгкий контейнер для пакетных вставок

Скрипт читает дерево (querySelector, children) и меняет его (createElement, append, remove, classList, textContent). Любое изменение структуры или стилей может затронуть этапы отрисовки (layout, paint) — поэтому для SPA важны батчинг обновлений и виртуальный DOM во фреймворках.

BOM (Browser Object Model) — окружение вкладки — window, location, history, navigator. Это тоже не ECMAScript; см. объектная модель браузера.

Связка HTML + CSSOM + DOM участвует в построении render tree; роль JavaScript в пайплайне браузера — в применении в вебе.

Перед скриптами соберите страницу, с которой будете работать — HTML-страницы целиком (каркас, форма, семантика). Дальше — JavaScript DOM — 30 приёмов. Стили "только на CSS" из каталога Uiverse / Galaxy ниже дополняются скриптом, когда нужно состояние между сессиями или сложное поведение.


Когда достаточно CSS, когда нужен JavaScript

Многие элементы интерфейса в учебных сниппетах (архив Galaxy) работают без JavaScript. Скрипт подключают, когда одних псевдоклассов и :has() недостаточно.

ЗадачаЧасто хватает CSSОбычно нужен JS
Hover-tooltip с data-tooltipПанель на touch, позиционирование у края экрана
Switch / checkbox / radio (вид)appearance: none, :checkedОтправка формы, валидация, связь с API
Раскрывающийся поиск:focus, :validПодсказки, debounce, запрос к серверу
Тёмная тема на странице:has() + checkboxЗапомнить выбор в localStorage
Skeleton / spinner@keyframes, aria-busyПодмена на реальный контент после fetch
Toast / alert✅ статичный блокОчередь, таймер закрытия, focus
ModalЧастично :targetFocus trap, Escape, portal, scroll lock
Таблица с сортировкой✅ клик по заголовку, перестройка DOM
Вкладки (tabs):target или radioСложная логика, lazy load панелей

Прогрессивное улучшение: сначала семантичный HTML (формы), затем CSS (Типовые элементы интерфейса), затем JS для поведения и данных. Типизация UI в React — TypeScript и React.

Пример: сохранить тему после перезагрузки:

const key = 'theme-dark';
const input = document.querySelector('.switch__input[role="switch"]');
if (!input) throw new Error('Switch not found');

const saved = localStorage.getItem(key) === '1';
input.checked = saved;
input.addEventListener('change', () => {
localStorage.setItem(key, input.checked ? '1' : '0');
});

Замена skeleton на контент после загрузки:

async function loadCard(container) {
container.setAttribute('aria-busy', 'true');
const res = await fetch('/api/card');
const data = await res.json();
container.removeAttribute('aria-busy');
container.innerHTML = `
<h3>${data.title}</h3>
<p>${data.text}</p>
`;
}

Краткая шпаргалка — методы DOM

DOM — то, через что JavaScript управляет HTML — ищет элементы, создаёт узлы, меняет разметку и реагирует на действия пользователя. Например, document.querySelector() быстро возвращает первый элемент по CSS-селектору, а elem.appendChild() добавляет узел в дерево и позволяет динамически собирать страницу.

Ниже — шесть групп ключевых API. Подробные примеры — в разделах этой главы; события — События и обработка событий в браузере.

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

APIВозвращает
document.getElementById('id')Один элемент или null
document.getElementsByClassName('cls')Живая HTMLCollection
document.getElementsByName('name')Коллекция (часто поля формы)
document.getElementsByTagName('tag')Живая коллекция по имени тега
document.querySelector('selector')Первый подходящий элемент
document.querySelectorAll('selector')NodeList всех совпадений (статический список)

querySelector / querySelectorAll — универсальный способ: любой CSS-селектор. getElementById — самый быстрый, если известен уникальный id.

2. Создание и вставка узлов

APIНазначение
document.createElement('div')Новый пустой элемент
document.createTextNode('текст')Текстовый узел без тегов
parent.appendChild(child)Добавить ребёнка в конец
parent.removeChild(child)Удалить дочерний узел
parent.replaceChild(newNode, oldNode)Заменить одного ребёнка другим
parent.insertBefore(newNode, ref)Вставить перед узлом ref
node.remove()Удалить сам узел (современная альтернатива removeChild)

3. Содержимое и стили элемента

Для разметки <p>Текст с <strong>акцентом</strong></p>:

СвойствоЧто затрагивает
outerHTMLСам тег <p> и всё внутри (при записи — замена всего элемента)
innerHTMLТолько внутреннюю разметку: Текст с <strong>акцентом</strong>
textContentВесь текст без разбора тегов: Текст с акцентом
elem.styleИнлайн-CSS (color, display, …)

innerHTML разбирает строку как HTML — с пользовательским вводом это риск XSS; для чистого текста безопаснее textContent. Разбор — в свойствах элементов.

4. Родители, дети и соседи

APIНазначение
elem.children / elem.childNodesДети: только элементы / все узлы (включая текст и пробелы)
firstElementChild / lastElementChildПервый и последний дочерний элемент
firstChild / lastChildПервый и последний узел (часто — пробел или \n в разметке)
parentNode / parentElementРодитель
previousSibling / nextSiblingСоседний узел на том же уровне
previousElementSibling / nextElementSiblingСоседний элемент (без текстовых узлов между тегами)
nodeName, nodeType, nodeValueИмя узла; тип (1 — Element, 3 — Text); значение (для текста)

Все перечисленные поля — свойства, не функции. hasChildNodes() — исключение:

  • метод;
  • возвращающий true;
  • если есть дети.

5. Атрибуты

APIНазначение
elem.getAttribute('title')Прочитать значение
elem.setAttribute('title', 'подсказка')Записать или создать
elem.hasAttribute('disabled')Проверить наличие
elem.removeAttribute('hidden')Удалить атрибут

Для частых полей (href, src, value, checked) есть одноимённые свойства элемента — они синхронизируются с DOM. Подробнее — работа с атрибутами.

6. Классы (classList)

APIНазначение
elem.classList.add('active')Добавить класс
elem.classList.remove('active')Убрать
elem.classList.toggle('open')Переключить (есть — убрать, нет — добавить)
elem.classList.contains('active')Проверка (true / false)
elem.classList.replace('old', 'new')Заменить один класс другим
elem.classList.lengthЧисло классов на элементе

Строка elem.className задаёт весь список классов сразу и перезаписывает прежние. classListDOMTokenList: к классам можно обращаться по индексу и перебирать в цикле. Подробнее — управление классами.

События

ГруппаТипы для addEventListenerКогда
Мышьclick, mouseover, mouseoutКлик; курсор вошёл или вышел
Клавиатураkeydown, keyup, keypressНажатие и отпускание (keypress устарел)
Формыchange, focus, blur, inputЗначение изменилось; фокус; ввод в поле

document.addEventListener(...) удобен для делегирования — один обработчик на список кнопок. Атрибуты onclick, onchange в HTML дублируют те же типы; в скрипте предпочтительнее addEventListener.

Play ITЗагрузка интерактивного демо…

Следующий шаг

Готовые фрагменты с разбором — JavaScript DOM — 30 приёмов в Лаборатории.

Запросы к API из браузера (fetch, axios, POST, токен) — Fetch / axios — типовые запросы.

Компонентный UI на React (счётчик, формы, списки) — React — компоненты-рецепты.

Когда поиск элементов через querySelector понятен, соберите цельный виджет:

Кнопка "Поделиться" с addEventListener и navigator.shareWeb Share API.

Ленивая подгрузка и реакция на размер блока — Наблюдатели DOM; проверка полей формы — Валидация форм; окно и навигация — BOM; свои HTML-теги — Web Components.

Для успешной работы скрипта критически важно правильное расположение кода внутри файла. Браузер обрабатывает HTML-код сверху вниз последовательно. Если тег <script> расположен в верхней части документа, до того как браузер встретит описание элементов div, button или input, то попытка найти эти элементы вернет пустое значение. Это происходит потому, что к моменту выполнения скрипта соответствующие узлы дерева еще не созданы.

Код ITЗагрузка примера кода…

Разбор:

  • Скрипт расположен в <head>, поэтому он выполняется до создания кнопки в <body>.
  • document.getElementById('myButton') в этот момент не находит элемент и возвращает null.
  • console.log(element) показывает диагностический результат и помогает заметить проблему порядка загрузки.
  • Главный смысл примера: доступ к DOM зависит от того, успел ли браузер распарсить нужный участок HTML.
  • Если далее вызвать метод у null, приложение получит runtime-ошибку (Cannot read properties of null).

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

Код ITЗагрузка примера кода…

Разбор:

  • Здесь кнопка объявлена раньше скрипта, поэтому DOM-элемент уже существует к моменту выполнения JS.
  • const element = document.getElementById('myButton') теперь возвращает объект кнопки, а не null.
  • Лог в консоли помогает убедиться, что ссылка на элемент получена корректно.
  • Такое расположение <script> в конце body — классический безопасный способ работы с DOM без дополнительных обработчиков загрузки.
  • Пример показывает практическую связь между порядком HTML-кода и корректностью JS-логики.

Альтернативным подходом является использование атрибута defer у тега скрипта. Этот атрибут сообщает браузеру отложить выполнение кода до момента полной загрузки и парсинга всего HTML-документа. При этом скрипт выполняется асинхронно, не блокируя отображение контента.

<script src="main.js" defer></script>

Разбор:

  • Атрибут defer говорит браузеру скачать файл main.js сразу, но выполнить его после полного разбора HTML.
  • Это убирает гонку между JS и DOM: код видит уже созданные элементы страницы.
  • Одновременно рендер страницы не блокируется, что улучшает скорость отображения интерфейса.
  • Подход особенно удобен для внешних скриптов, когда код хранится в отдельном файле.

Третий распространённый способ — обернуть инициализацию в обработчик DOMContentLoaded. Событие срабатывает, когда браузер полностью построил DOM (разобрал HTML в дерево узлов). Картинки и стили к этому моменту могут ещё грузиться — для них есть отдельное событие load у window (см. BOM).

Скрипт можно оставить в <head> или в начале <body>: код регистрирует функцию сразу, а выполняет её только после готовности разметки.

<script>
document.addEventListener('DOMContentLoaded', function () {
const button = document.getElementById('myButton');
button.addEventListener('click', () => {
console.log('Кнопка нажата');
});
});
</script>

<body>
<button id="myButton">Нажми</button>
</body>

Разбор:

  • addEventListener('DOMContentLoaded', …) не блокирует разбор HTML — браузер продолжает строить страницу.
  • К моменту вызова колбэка элемент #myButton уже существует, даже если тег <script> стоит выше кнопки в файле.
  • Внутри обработчика удобно искать узлы (querySelector), вешать события и менять классы — всё, что требует готового DOM.

Стрелочная функция вместо function () { … } допустима; для нескольких инициализаций вынесите тело в function initApp() { … } и передайте её по имени. Подробнее о подключении скриптов — Подключение JavaScript к HTML.

СпособКогда DOM уже готовГде держать <script>
В конце <body>Да, если скрипт после разметкиПеред </body>
defer на внешнем файлеДа, после парсинга HTML<head> или <body>
DOMContentLoadedДа, в колбэке событияЛюбое место до или после разметки

Свойство document предоставляет доступ ко всем методам поиска элементов. Методы работают по принципу поиска в дереве DOM и возвращают конкретные объекты или коллекции объектов. Выбор способа поиска зависит от уникальности элемента и его назначения на странице.


Основные свойства объекта document

document.body

  • Ссылка на элемент <body> текущего документа.
  • Используется как контейнер для всех видимых элементов страницы.
  • Позволяет быстро получить доступ к телу документа без поиска по DOM-дереву.

document.title

  • Содержит текст внутри тега <title>.
  • Изменение этого свойства обновляет заголовок во вкладке браузера.

document.URL

  • Возвращает полный адрес текущей страницы.

document.domain

  • Содержит доменное имя сайта.
  • Используется для управления безопасностью между поддоменами.

document.cookie

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

document.head

  • Ссылка на элемент <head> документа.
  • Используется для динамического добавления мета-тегов или стилей.

document.documentElement

  • Ссылка на корневой элемент <html>.
  • Является первым потомком объекта document.

Объект document.body служит основным контейнером контента страницы. Прямая ссылка на тело документа упрощает навигацию по структуре, позволяет добавлять новые элементы прямо в конец документа. Дает доступ ко всем дочерним элементам через итерацию.

Пример добавления элемента в body

const newParagraph = document.createElement('p');
newParagraph.textContent = 'Элемент добавлен в конец тела документа';
document.body.appendChild(newParagraph);

Пример изменения стилей body

document.body.style.backgroundColor = '#f0f0f0';
document.body.style.fontFamily = 'Arial, sans-serif';

Поиск по идентификатору (ID)

Идентификатор (id) — это уникальный атрибут, который должен присутствовать только один раз на всей странице. Он служит точкой входа для быстрого и надежного поиска конкретного элемента. Браузер использует внутренние оптимизации для мгновенного нахождения элемента с заданным ID.

Метод getElementById принимает строковое значение идентификатора и возвращает единственный найденный элемент. Если элемент с таким ID не найден, метод вернет null. Это наиболее производительный способ поиска среди всех доступных методов.

const headerElement = document.getElementById('pageHeader');
headerElement.style.color = 'blue';

В этом коде мы находим элемент с атрибутом id="pageHeader" и изменяем цвет его текста на синий. Если бы на странице было два элемента с одинаковым ID, метод все равно вернул бы первый найденный, но такое поведение считается ошибкой проектирования разметки.

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

const panel = document.getElementById('step-1');
panel.id = 'step-2'; // быстрее и привычнее в скриптах
// panel.setAttribute('id', 'step-2'); // тот же эффект

После смены id старый селектор #step-1 перестаёт находить элемент; ссылки из CSS (#step-1 { … }) и якоря (href="#step-1") тоже перестанут работать, пока вы не обновите разметку или стили. На одной странице id по-прежнему должен быть уникальным. Динамическую смену id используют редко — чаще меняют классы или data-*-атрибуты, не ломая уникальные якоря.

<div id="mainTitle">Заголовок страницы</div>
<button onclick="changeColor()">Изменить цвет</button>

<script>
function changeColor() {
const title = document.getElementById('mainTitle');
if (title !== null) {
title.style.backgroundColor = 'yellow';
} else {
console.error('Элемент не найден');
}
}
</script>

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


Поиск по классу (Class)

Классы (class) используются для группировки элементов, которые имеют общие стили или поведение. В отличие от ID, один класс может быть присвоен множеству элементов на странице. Метод getElementsByClassName возвращает живую коллекцию всех элементов с указанным классом.

Коллекция работает как массив, но имеет свои особенности. Она не поддерживает методы массивов напрямую, такие как map или filter. Для работы с элементами нужно использовать цикл for или преобразовать коллекцию в настоящий массив. Коллекция обновляется автоматически при изменении структуры страницы, поэтому она называется живой.

const buttons = document.getElementsByClassName('action-btn');
console.log(buttons.length); // Количество кнопок с классом action-btn

for (let i = 0; i < buttons.length; i++) {
buttons[i].style.cursor = 'pointer';
}

В этом примере мы проходимся по всем кнопкам с классом action-btn и устанавливаем курсор в виде руки. Обратите внимание на индексацию: коллекция начинается с нуля, как и обычный массив.

<button class="action-btn">Первая кнопка</button>
<button class="action-btn">Вторая кнопка</button>
<button class="other-class">Другая кнопка</button>

<script>
const allButtons = document.getElementsByClassName('action-btn');

for (let btn of allButtons) {
btn.addEventListener('click', () => {
alert('Клик по кнопке!');
});
}
</script>

Здесь мы используем современный синтаксис цикла for...of, который позволяет перебирать элементы коллекции напрямую. Это делает код более читаемым и понятным.

Также существует метод querySelectorAll, который возвращает статическую NodeList (необновляемую коллекцию). Это часто предпочтительнее, когда работа идет с фиксированным набором элементов.

const staticList = document.querySelectorAll('.static-item');
// Список не изменится, даже если добавить новый элемент с классом .static-item

Поиск по тегу (Tag)

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

const paragraphs = document.getElementsByTagName('p');
console.log(paragraphs.length); // Количество параграфов на странице

for (let i = 0; i < paragraphs.length; i++) {
paragraphs[i].classList.add('highlighted');
}

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

<p>Первый текст</p>
<p>Второй текст</p>
<div>Не параграф</div>

<script>
const texts = document.getElementsByTagName('p');

for (let p of texts) {
p.innerText += ' - обработано';
}
</script>

Здесь мы модифицируем содержимое каждого параграфа, добавляя к нему текст. Метод чувствителен к регистру, поэтому передача 'P' вместо 'p' не даст результата.


Поиск по селектору CSS

Метод querySelector является самым универсальным способом поиска элементов. Он принимает любой допустимый CSS-селектор и возвращает первый найденный элемент. Если элемент не найден, возвращается null. Этот метод работает быстрее и гибче, чем специализированные методы, так как использует встроенный движок парсинга CSS браузера.

const mainContent = document.querySelector('#main-content');
const firstLink = document.querySelector('a');
const nestedItem = document.querySelector('.container .item:first-child');

В первом примере мы ищем элемент с ID main-content. Во втором — первый ссылочный элемент на странице. В третьем — первый дочерний элемент с классом item, находящийся внутри контейнера .container.

<div class="wrapper">
<span class="target">Текст</span>
<span class="target">Еще текст</span>
</div>

<script>
const target = document.querySelector('.wrapper .target');
target.style.fontWeight = 'bold';
</script>

Здесь мы находим первый элемент с классом target, который находится внутри .wrapper. Если бы мы использовали querySelectorAll, то получили бы список всех таких элементов.

Метод querySelectorAll возвращает статический список всех совпадений. Он поддерживает сложные селекторы, включая псевдоклассы и атрибуты.

const inputs = document.querySelectorAll('input[type="text"]:not(:disabled)');
inputs.forEach(input => {
input.focus();
});

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


Поиск по имени атрибута (Name)

Атрибут name часто используется в формах для идентификации полей ввода. Метод getElementsByName возвращает живую коллекцию всех элементов с указанным именем. Этот метод специфичен для форм и редко используется вне контекста обработки данных формы.

const emailFields = document.getElementsByName('email');
console.log(emailFields[0].value); // Значение первого поля

for (let field of emailFields) {
field.required = true;
}

В этом примере мы получаем все поля с именем email и делаем их обязательными для заполнения. Коллекция индексируется как массив, поэтому доступ к первому элементу осуществляется через [0].

Код ITЗагрузка примера кода…

Здесь мы добавляем валидацию для всех полей с именем email. При потере фокуса проверяется длина введенного текста.


Свойства элементов

Каждый найденный элемент обладает набором свойств, позволяющих читать и изменять его содержимое и состояние. Сводка outerHTML, innerHTML и textContent — в шпаргалке (п. 3); ниже — подробности и innerText.

Свойство innerText возвращает видимый текст элемента, игнорируя скрытые элементы и стили. Оно учитывает визуальное представление и может работать медленнее других свойств из-за необходимости пересчета стилей. Изменение innerText заменяет весь текст внутри элемента.

const title = document.getElementById('pageTitle');
console.log(title.innerText); // "Добро пожаловать"

title.innerText = "Новый заголовок";

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

const article = document.querySelector('article');
const fullText = article.textContent;
// Содержит текст даже из элементов style или script

Свойство innerHTML возвращает HTML-разметку внутри элемента как строку. Изменение этого свойства позволяет вставлять новый HTML-код, который будет интерпретирован браузером. Использование innerHTML требует осторожности, так как оно может привести к уязвимостям безопасности при работе с пользовательским вводом.

const container = document.getElementById('content');
console.log(container.innerHTML); // "<p>Текст</p><span>Еще</span>"

container.innerHTML = '<h2>Новый заголовок</h2><p>Новый контент</p>';

В этом примере мы полностью заменяем содержимое контейнера новым HTML-блоком. Браузер создает новые элементы на основе переданной строки.

Свойство outerHTML возвращает сам элемент вместе с его открывающим и закрывающим тегами. Это удобно для клонирования или замены всего блока.

const button = document.querySelector('button');
const buttonCode = button.outerHTML;
console.log(buttonCode); // "<button id='btn'>Нажми</button>"

Для управления стилями используется свойство style. Оно позволяет устанавливать CSS-свойства в формате camelCase (например, backgroundColor вместо background-color).

const box = document.getElementById('box');
box.style.width = '100px';
box.style.backgroundColor = '#3498db';
box.style.marginTop = '20px';

Свойство className позволяет менять классы элемента целиком. Установка нового класса заменяет все предыдущие классы.

const item = document.querySelector('.item');
item.className = 'active highlighted';
// Теперь у элемента два класса: active и highlighted

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

const card = document.querySelector('.card');
card.classList.add('expanded');
card.classList.remove('collapsed');
card.classList.toggle('hidden'); // Добавляет, если нет, убирает, если есть
console.log(card.classList.contains('active')); // true или false

Свойство value используется для элементов ввода (input, textarea, select). Оно содержит текущее значение поля.

const input = document.querySelector('#username');
const userName = input.value;

input.value = 'Гость'; // Устанавливаем новое значение

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


Модификация и создание элементов HTML в JavaScript

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

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


Изменение стиля

Свойство style на элементе предоставляет прямой доступ к инлайн-стилям, заданным в атрибуте style HTML-тега.

Доступ к стилю осуществляется через имя свойства в формате camelCase (например, backgroundColor, а не background-color).

Значение присваивается в виде строки, включающей единицы измерения (например, "20px", "red").

Изменение значения через element.style перезаписывает только инлайн-стили. CSS-правила из файлов .css остаются активными, если они не переопределены.

Пример смены цвета фона

const element = document.querySelector('#myDiv');
element.style.backgroundColor = '#ff0000';
element.style.padding = '10px';
element.style.fontSize = '16px';

Пример сброса стиля

const element = document.querySelector('#myDiv');
element.style.backgroundColor = ''; // Убирает инлайн-стиль

Изменение содержимого текстовых блоков

Самый частый сценарий использования — замена текста внутри элемента. Для этого служат свойства innerText, textContent и innerHTML. Каждое из них имеет свою специфику работы и область применения.

  • textContent: Работает быстрее, возвращает чистый текст без учета CSS.
  • innerText: Учитывает CSS-стили. Не возвращает текст скрытых элементов. Выполняет больше вычислений.

Свойство innerText возвращает видимый текст узла, игнорируя скрытые элементы и учитывая CSS-стили. При записи оно преобразует введенную строку в текстовые узлы, удаляя при этом старый текст. Это свойство учитывает отступы и переносы строк так, как они отображаются визуально. Если вы хотите изменить заголовок или сообщение на странице, это будет наиболее безопасным и понятным вариантом.

const header = document.getElementById('mainTitle');
header.innerText = 'Добро пожаловать в систему';

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

Свойство textContent работает аналогично, но возвращает весь текст, включая тот, который скрыт стилями (например, через display: none). Оно также быстрее, так как не требует анализа стилей для определения видимости. Использование textContent предпочтительно, когда нужно скопировать полное содержимое узла или вставить текст, не затрагивая HTML-разметку.

const article = document.querySelector('.content');
article.textContent = 'Это новый текст статьи, который не содержит HTML тегов.';

Здесь мы вставляем чистый текст в статью. Если бы мы использовали innerHTML, браузер мог бы попытаться интерпретировать специальные символы как код, что привело бы к ошибкам или уязвимостям безопасности.

const paragraph = document.querySelector('p');
// Чтение текста
console.log(paragraph.textContent);

// Запись нового текста
paragraph.textContent = 'Новый текст параграфа';

Свойство innerHTML позволяет вставлять HTML-разметку внутрь элемента. Браузер парсит переданную строку и создает соответствующие дочерние узлы. Это мощный инструмент для создания сложных структур, но он требует осторожности при работе с данными от пользователей, так как может привести к внедрению вредоносного кода (XSS), в том числе к DOM-based XSS, если в innerHTML попадает фрагмент из location.hash или другого источника, который сервер не фильтровал (типы XSS).

const container = document.getElementById('newsFeed');
container.innerHTML = '<div class="item"><h3>Новость</h3><p>Текст новости</p></div>';

В этом коде мы добавляем новый блок новостей внутрь контейнера. Строка <div> будет распарсена, и внутри container появится новый элемент div с заголовком и параграфом. Важно помнить, что использование innerHTML очищает все предыдущие дочерние элементы узла.

Для управления стилями используется свойство style. Оно принимает имя CSS-свойства в формате camelCase (первое слово с большой буквы, если их несколько) и значение.

const box = document.querySelector('.highlight-box');
box.style.backgroundColor = '#ffeb3b';
box.style.color = '#000000';
box.style.fontSize = '20px';
box.style.borderRadius = '10px';

Этот фрагмент кода меняет внешний вид блока, делая его желтым с черным текстом и скругленными углами. Стили применяются инлайн, то есть попадают непосредственно в атрибут style элемента в HTML.


Управление классами и внешним видом

Часто требуется не просто изменить один инлайн-стиль, а переключить состояние элемента — сделать кнопку активной, показать или скрыть блок, заблокировать поле. Для этого в разметке заранее задают CSS-классы (.active, .hidden, .is-open), а скрипт добавляет или убирает их у узла.

classList — API для CSS-классов

element.classList — специальное свойство DOM-элемента типа DOMTokenList. Оно не строка: у него есть методы для точечной работы с отдельными классами, не затрагивая остальные. Сводка методов — в шпаргалке (п. 6).

const card = document.querySelector('.card');

card.classList.add('expanded'); // добавить класс
card.classList.remove('collapsed'); // убрать класс
card.classList.toggle('hidden'); // есть — убрать, нет — добавить
card.classList.contains('active'); // true или false
card.classList.replace('draft', 'published'); // заменить один класс другим; вернёт true, если замена прошла
console.log(card.classList.length); // сколько классов сейчас на элементе

Доступ к классам "как к массиву". classList ведёт себя как массивоподобная коллекция: у него есть length и доступ по индексу (classList[0], classList[1]). Полноценного массива это не даёт — нет map / filter, но классы можно перебрать:

const tabs = document.querySelector('.tabs');

for (let i = 0; i < tabs.classList.length; i++) {
console.log(tabs.classList[i]);
}

for (const name of tabs.classList) {
console.log(name);
}

const names = Array.from(tabs.classList); // ['tabs', 'tabs--dark', …]

className — вся строка классов сразу. Свойство element.className читает и записывает все классы одной строкой через пробел. Запись перезаписывает прежний список — удобно, когда нужно задать фиксированный набор, но опасно, если на элементе уже были другие классы из HTML или фреймворка.

item.className = 'active highlighted'; // ровно два класса, остальные исчезнут
item.className += ' extra'; // «добавить» класс вручную — легко получить двойной пробел или дубликат
item.classList.add('extra'); // безопаснее для одного нового класса

Части имени класса (префикс, суффикс, номер шага) обычно не "режут" через строки, а меняют целиком: classList.remove('tab-1'); classList.add('tab-2'); или classList.replace('tab-1', 'tab-2'). Для произвольных данных лучше data-step="2" и селектор [data-step="2"], а не динамический id.

Видимость, активность и блокировка

ЗадачаТипичный приём в JSЗаметка
Скрыть блокelem.classList.add('hidden')Класс .hidden { display: none; } в CSS
Показать сноваelem.classList.remove('hidden')Стили из таблицы стилей, не инлайн
Переключить видимостьelem.classList.toggle('hidden')Один обработчик "показать/скрыть"
Скрыть семантическиelem.hidden = true / elem.hidden = falseБулево свойство; в HTML — атрибут hidden
Скрыть от скринридеровelem.setAttribute('aria-hidden', 'true')Не путать с hidden: нужен осмысленный контекст
Активная вкладка / пункт менюelem.classList.add('active')Сначала снять active с соседей, затем добавить текущему
Неактивная кнопкаbtn.disabled = trueДля <button>, <input>, <select>, <textarea>
Блокировка через разметкуbtn.setAttribute('disabled', '')То же состояние, что disabled = true

Пример переключения активной вкладки и скрытия панели:

document.querySelectorAll('.tab').forEach((tab) => {
tab.classList.remove('active');
});
document.querySelector('#tab-settings').classList.add('active');

const panel = document.querySelector('#panel-old');
panel.classList.add('hidden');
document.querySelector('#panel-settings').classList.remove('hidden');

Инлайн style.display = 'none' тоже скрывает элемент, но смешивает логику и оформление; для переключения состояний предпочтительнее класс из CSS. Свойство hidden и класс .hidden часто используют вместе с правилами доступности в типовых элементах интерфейса (CSS).

Ниже — те же методы classList с разбором по одному.

Метод add() добавляет один или несколько классов к элементу. Если класс уже существует, метод не вызовет ошибок, а просто оставит текущее состояние.

const button = document.querySelector('#submitBtn');
button.classList.add('active', 'loading');
// Теперь у кнопки есть два класса: active и loading

Метод remove() удаляет указанные классы. Это полезно для сброса состояний после завершения действий.

const card = document.querySelector('.product-card');
card.classList.remove('selected');
// Класс selected больше не применяется к карточке товара

Метод toggle() действует как переключатель. Если класс присутствует, он его удаляет. Если класса нет, он добавляет. Это идеальный инструмент для реализации кнопок "показать/скрыть" или меню.

const menu = document.querySelector('.sidebar-menu');
menu.classList.toggle('open');
// Если меню было закрыто, оно откроется. Если открыто — закроется.

Метод contains() проверяет наличие конкретного класса и возвращает логическое значение true или false. Это удобно использовать для условной логики перед выполнением действий.

const item = document.querySelector('.list-item');
if (item.classList.contains('disabled')) {
console.log('Элемент заблокирован');
} else {
item.classList.add('disabled');
}

Метод replace(oldClass, newClass) атомарно меняет один класс на другой. Возвращает true, если oldClass был на элементе и заменён, иначе falsenewClass не добавляется, если oldClass не найден).

const badge = document.querySelector('.badge');
badge.classList.replace('status-new', 'status-read');
// было: badge status-new → стало: badge status-read

toggle с принудительным состоянием — второй аргумент true — всегда добавить класс, false — всегда убрать (удобно, когда состояние уже известно из данных, а не из DOM).

menu.classList.toggle('open', isMenuOpen); // добавит open только если isMenuOpen === true

Комплексный пример — кнопка, которая показывает пароль и подсвечивает себя классом active:

const toggleBtn = document.querySelector('#show-password');
const field = document.querySelector('#password');

toggleBtn.addEventListener('click', () => {
const hidden = field.type === 'password';
field.type = hidden ? 'text' : 'password';
toggleBtn.classList.toggle('active', hidden);
toggleBtn.setAttribute('aria-pressed', hidden ? 'true' : 'false');
});

Альтернативой работе с отдельными методами classList является присвоение строки свойству className — см. блок выше. Полная замена набора классов:

const nav = document.querySelector('nav');
nav.className = 'main-navigation dark-theme';
// Все старые классы удалены, остались только эти два

Добавление новых элементов в документ

Чтобы создать новую часть интерфейса, необходимо сначала создать сам элемент в памяти, а затем вставить его в дерево DOM. Процесс создания включает в себя генерацию объекта типа Element и последующую манипуляцию его свойствами.

Метод createElement() создает пустой элемент указанного тега. В этот момент элемент еще невидим для пользователя, так как он находится в оперативной памяти.

const newParagraph = document.createElement('p');
newParagraph.innerText = 'Этот параграф был создан скриптом';
newParagraph.classList.add('dynamic-content');

После создания элемента ему можно задать атрибуты, стили и содержимое. Затем используется метод appendChild() для помещения нового узла в конец списка детей родительского элемента.

const container = document.getElementById('textContainer');
container.appendChild(newParagraph);

Если нужно вставить элемент не в конец, а в конкретное место, используются методы insertBefore() или prepend() / append(). Метод prepend() добавляет элемент первым среди детей родителя.

const list = document.querySelector('ul');
const firstItem = list.firstChild; // может быть текстовый узел между тегами
const newLi = document.createElement('li');
newLi.textContent = 'В начало списка';
list.insertBefore(newLi, firstItem);

insertBefore(newNode, referenceNode) требует существующего ребёнка-ориентира; если ориентира нет, подойдёт appendChild. Для текста без обёртки в тег создают узел через createTextNode() и тоже вставляют через appendChild или insertBefore.

const header = document.querySelector('header');
const logo = document.createElement('img');
logo.src = '/logo.png';
logo.alt = 'Логотип';
header.prepend(logo); // Логотип станет первым элементом в header

Метод insertAdjacentElement() позволяет гибко позиционировать элементы относительно другого узла. Он принимает четыре параметра — beforebegin, afterbegin, beforeend, afterend.

const list = document.querySelector('ul');
const newItem = document.createElement('li');
newItem.innerText = 'Новый пункт списка';
list.insertAdjacentElement('beforeend', newItem); // Добавляет в конец списка

Также существует метод cloneNode(), который создает копию существующего узла. Это эффективно, когда нужно добавить несколько одинаковых элементов, например, пункты списка или карточки товаров.

const templateItem = document.querySelector('.template-item');
for (let i = 0; i < 5; i++) {
const clone = templateItem.cloneNode(true); // true означает глубокий клон со всеми детьми
document.body.appendChild(clone);
}

Глубокий клон (true) копирует весь поддерево, включая вложенные элементы и их атрибуты. Поверхностный клон (false) копирует только сам элемент без содержимого.


Удаление элементов из структуры

Управление памятью и оптимизация верстки требуют возможности удаления ненужных узлов. Метод remove() вызывает удаление элемента из его родительского узла.

const oldMessage = document.getElementById('temp-message');
if (oldMessage) {
oldMessage.remove();
}

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

Классический вариант — removeChild() у родителя (удобен, когда ссылка есть только на дочерний узел):

const item = document.getElementById('todo-3');
if (item?.parentNode) {
item.parentNode.removeChild(item);
}

replaceChild(newNode, oldNode) у родителя заменяет одного ребёнка другим (удобно, когда нужно сохранить позицию в списке):

const list = document.querySelector('#tasks');
const oldLi = list.querySelector('.done');
const newLi = document.createElement('li');
newLi.textContent = 'Обновлённая задача';
list.replaceChild(newLi, oldLi);

Метод replaceWith() заменяет текущий элемент на новый, передавая ему место в дереве. Новый элемент может быть другим узлом или даже строкой HTML, которая будет разобрана браузером.

const errorBlock = document.querySelector('.error-alert');
const successBlock = document.createElement('div');
successBlock.className = 'success-alert';
successBlock.innerText = 'Операция выполнена успешно';
errorBlock.replaceWith(successBlock);
// Блок ошибки исчезает, появляется блок успеха

Также можно использовать метод innerHTML = '' для очистки всего содержимого узла, но это удалит и все вложенные элементы, что иногда нежелательно.


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

Атрибуты хранят дополнительную информацию об элементе, такую как ссылки, имена полей форм или статусы. Сводка getAttribute / setAttribute / hasAttribute / removeAttribute — в шпаргалке (п. 5).

Метод getAttribute(name) возвращает значение атрибута. Если атрибут отсутствует, возвращается null.

const link = document.querySelector('a');
const hrefValue = link.getAttribute('href');
console.log(hrefValue); // Выведет ссылку, например https://example.com

Метод setAttribute(name, value) устанавливает или изменяет значение атрибута. Если атрибут не существует, он создается.

const input = document.querySelector('#username');
input.setAttribute('placeholder', 'Введите ваше имя');
input.setAttribute('required', ''); // Атрибут required активируется

Существуют специальные свойства для часто используемых атрибутов, такие как src, href, value, checked. Они работают автоматически и синхронизируются с DOM.

const image = document.querySelector('.avatar');
image.src = '/new-avatar.jpg'; // Меняет источник изображения
image.alt = 'Новый аватар пользователя'; // Меняет альтернативный текст

Для проверки наличия атрибута используется метод hasAttribute().

const btn = document.querySelector('#action-btn');
if (btn.hasAttribute('disabled')) {
console.log('Кнопка недоступна');
}

Работа с формами

Форма — это и HTML-разметка, и объект HTMLFormElement в DOM. Поля с атрибутом name попадают в тело запроса при submit; без name значение на сервер не уходит.

Коллекция document.forms

У document есть свойство forms — живая коллекция всех форм на странице. Доступ по индексу или по атрибуту name / id:

document.forms['loginForm'].username.value;
document.forms[0].elements['email'].value;
document.getElementById('email').value; // предпочтительнее

Современный код чаще использует getElementById или querySelector — они не зависят от порядка форм на странице. Коллекция elements внутри формы — все поля с name, включая button и fieldset.

Свойства и методы формы

APIНазначение
actionURL обработчика
methodGET или POST
enctypeкодировка тела (application/x-www-form-urlencoded, multipart/form-data, …)
length / elementsчисло и список именованных полей
submit()программная отправка (не вызывает onsubmit у кнопки submit)
reset()сброс к значениям из HTML
checkValidity()проверка всех полей — см. валидацию форм

События формы и полей

СобытиеКогда срабатывает
submitотправка формы (можно preventDefault())
resetсброс полей
inputкаждое изменение значения в поле
changeзначение зафиксировано (blur у text, сразу у checkbox/select)
focus / blurполе получило / потеряло фокус
invalidполе не прошло проверку при submit
const form = document.querySelector('#signup');

form.addEventListener('submit', (event) => {
event.preventDefault();
if (!form.checkValidity()) return;

const data = new FormData(form);
console.log(Object.fromEntries(data));
});

Поле может находиться вне <form>, если у него атрибут form="signup" — тогда оно всё равно участвует в submit формы с этим id.

Создание формы из JavaScript

Динамическое создание форм позволяет адаптировать интерфейс под запросы пользователя — поля, списки и кнопки собирают программно.

Код ITЗагрузка примера кода…

Свойство htmlFor связывает <label> с полем по id. Типы полей и их JS-свойства (files, checked, stepUp) — в валидации форм, раздел по типам input.


Выделение текста — Selection и Range

Пользовательское выделение на странице доступно через window.getSelection() — объект Selection. Программный фрагмент документа задают document.createRange() — объект Range.

Selection — что выделил пользователь

Свойство / методНазначение
toString()текст выделения
isCollapsedtrue, если курсор без диапазона
anchorNode, focusNodeначальный и конечный узлы
getRangeAt(0)первый Range в выделении
removeAllRanges()снять выделение
selectAllChildren(node)выделить содержимое узла
const sel = window.getSelection();
if (sel && !sel.isCollapsed) {
console.log(sel.toString());
}

В <input> и <textarea> выделение читают через selectionStart / selectionEnd и задают setSelectionRange(start, end)getSelection() там не работает.

const field = document.querySelector('#comment');
field.focus();
field.setSelectionRange(0, 5);
const fragment = field.value.substring(field.selectionStart, field.selectionEnd);

Range — произвольный фрагмент DOM

МетодНазначение
setStart(node, offset) / setEnd(node, offset)границы в текстовом узле
surroundContents(element)обернуть фрагмент в новый тег
deleteContents() / extractContents()удалить или вырезать
cloneContents()копия как DocumentFragment
const paragraph = document.querySelector('#article p');
const textNode = paragraph.firstChild;
const range = document.createRange();
range.setStart(textNode, 0);
range.setEnd(textNode, 5);

const mark = document.createElement('mark');
range.surroundContents(mark);

const sel = window.getSelection();
sel.removeAllRanges();
sel.addRange(range);

Типовой сценарий "Поделиться выделением" — selection.toString() и Clipboard API. Для rich-text редакторов surroundContents и extractContents заменяют прямое редактирование innerHTML.


Пример комплексного взаимодействия

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

Код ITЗагрузка примера кода…

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


Особенности производительности и оптимизации

Частое изменение DOM может замедлить работу страницы, так как браузер вынужден пересчитывать макет и перерисовывать экран. Для оптимизации рекомендуется накапливать изменения в памяти, используя DocumentFragment, а затем вставлять их в документ одним действием.

DocumentFragment — это легкий контейнер, который не отображается на странице. Элементы, добавленные в него, не вызывают перерисовку до момента их переноса в основной документ.

const fragment = document.createDocumentFragment();
const ul = document.querySelector('.items');

for (let i = 1; i <= 100; i++) {
const li = document.createElement('li');
li.innerText = 'Элемент ' + i;
fragment.appendChild(li);
}

ul.appendChild(fragment);
// Браузер выполнит одну перерисовку вместо ста

Этот подход значительно повышает производительность при создании больших списков или таблиц. Также стоит минимизировать количество обращений к DOM внутри циклов, сохраняя результаты поиска в переменные.

Изменение стилей лучше группировать или использовать CSS-классы, чтобы избежать частых операций чтения и записи свойств стиля, которые могут вызвать перерисовку (reflow). Переключение классов через classList обычно выполняется эффективнее, чем прямое изменение множества стилей в цикле.