Learn JavaScript

textContent и innerHTML: разница и безопасность

В 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 пересоздает содержимое блока, что может ломать состояние и события.