В DOM чаще всего меняют содержимое элемента двумя способами:
textContent- задает текстinnerHTML- задает HTML-разметку строкой
С виду они похожи, но ведут себя принципиально по-разному, особенно с точки зрения безопасности.
1) textContent: только текст
textContent записывает в элемент текст. Любые символы < и > будут восприняты как обычные символы, а не как HTML.
const box = document.querySelector(".box");
box.textContent = "<strong>Bold</strong>";
console.log(box.textContent); // "<strong>Bold</strong>"
Это безопасный вариант по умолчанию.
Когда использовать textContent
- вывод текста из данных (формы, сети, файла)
- обновление счетчиков, подписей, заголовков
- любая ситуация, где разметка не нужна
2) innerHTML: вставка HTML
innerHTML воспринимает строку как HTML и создает элементы внутри.
const box = document.querySelector(".box");
box.innerHTML = "<strong>Bold</strong>";
После этого внутри .box появится тег <strong>.
Когда использовать innerHTML
- когда действительно нужно вставить разметку одной строкой
- когда шаблон прост и данные полностью контролируются
3) Главная разница: интерпретация HTML
Сравнение:
const box1 = document.querySelector(".box1");
const box2 = document.querySelector(".box2");
box1.textContent = "<em>Hello</em>";
box2.innerHTML = "<em>Hello</em>";
В box1 появится текст <em>Hello</em>, а в box2 появится наклонный текст “Hello”.
4) Почему innerHTML опасен
Если в innerHTML попадает строка откуда-то извне (ввод, данные из API), можно случайно выполнить “вредный HTML” (XSS).
Пример опасной вставки:
const box = document.querySelector(".box");
const userInput = "<img src=x onerror=\"console.log('XSS')\">";
box.innerHTML = userInput;
В реальном приложении вместо console.log могут быть более неприятные действия.
Практичное правило: если строка может прийти извне, innerHTML не использовать.
5) Безопасная альтернатива: создавать элементы
Если нужно добавить структуру и текст, безопаснее создать элементы и заполнить текст через textContent.
const box = document.querySelector(".box");
const strong = document.createElement("strong");
strong.textContent = "Bold";
box.innerHTML = "";
box.append(strong);
Текст безопасно экранируется автоматически.
6) Разница с точки зрения поведения
innerHTML полностью пересобирает содержимое внутри элемента. Это означает:
- существующие узлы удаляются и создаются заново
- может пропасть состояние (например, выделение текста)
- обработчики событий на внутренних элементах могут перестать работать
Пример ситуации:
const list = document.querySelector(".list");
list.innerHTML = "<li>One</li><li>Two</li>";
list.innerHTML = "<li>Three</li>";
После второй строки первые <li> уничтожаются.
Если нужно дополнять список, обычно лучше append, prepend, insertAdjacentHTML.
7) insertAdjacentHTML: иногда удобнее, но те же риски
insertAdjacentHTML вставляет HTML без перезаписи всего блока, но безопасность такая же: внешние данные нельзя вставлять напрямую.
const list = document.querySelector(".list");
list.insertAdjacentHTML("beforeend", "<li>Item</li>");
Частые ошибки
1) Использовать innerHTML для обычного текста
const title = document.querySelector("h1");
title.innerHTML = "Hello";
Так работает, но смысла нет. Текст безопаснее через textContent.
const title = document.querySelector("h1");
title.textContent = "Hello";
2) Вставлять пользовательский ввод в innerHTML
Опасно:
const box = document.querySelector(".box");
const value = "<b>Hi</b>";
box.innerHTML = value;
Безопаснее:
const box = document.querySelector(".box");
const value = "<b>Hi</b>";
box.textContent = value;
Что стоит запомнить
textContentвставляет текст и безопасен по умолчанию.innerHTMLвставляет HTML и может быть опасным при внешних данных.- Если нужна структура, безопаснее создавать элементы и задавать текст через
textContent. innerHTMLпересоздает содержимое блока, что может ломать состояние и события.