Создание элементов
Здесь будет встроен видео-разбор темы (5–7 минут, YouTube / RuTube / VK Видео) — тем, кто лучше воспринимает на слух, текст ниже не обязателен.
Всё, что вы видите на странице, кто-то создал: либо парсер, разобравший HTML-файл, либо ваш код. document.createElement — второй путь: элемент рождается в памяти, пустым и невидимым, и ждёт, пока его вставят в документ.
Метод принимает имя тега строкой и возвращает настоящий DOM-узел: с полным набором свойств, методов и будущих обработчиков. Внутри у него пусто, родителя нет, на экране его тоже нет — но это уже полноценный <button>, а не заготовка.
Регистр имени тега не важен: createElement("DIV") и createElement("div") дают один и тот же элемент. Пишите строчными — так принято, а tagName всё равно вернёт заглавные буквы, это наследие HTML.
Ошибки не будет: браузер создаст HTMLUnknownElement — узел без своего поведения, но вставляемый и стилизуемый. А вот SVG так не сделать: createElement всегда работает в пространстве имён HTML, для графики нужен createElementNS.
Дальше элемент обычный: textContent кладёт внутрь текст, className и id — свойства, setAttribute и dataset — всё остальное. Порядок не важен, всё это происходит в памяти, до всякой вставки.
Соблазн написать innerHTML вместо всего этого велик, и разница принципиальна: textContent кладёт строку как текст, innerHTML разбирает её как разметку. Одна и та же строка даёт в первом случае ноль дочерних элементов, во втором — настоящий тег.
Присваивание innerHTML не правит существующие узлы, а выбрасывает их и парсит содержимое заново. Внешне разметка та же, но узлы другие — вместе со старыми узлами исчезают их обработчики и любые ссылки на них в вашем коде.
Текст в DOM живёт не «внутри» элемента абстрактно, а в отдельном узле — Text. Обычно его создают за вас: присвоили textContent — браузер сам сделал текстовый узел. Но его можно собрать и руками: document.createTextNode возвращает узел с nodeType 3, без тега и без атрибутов.
Главное свойство такого узла: строка в нём остаётся строкой навсегда. Что бы вы туда ни положили — угловые скобки, амперсанды, кусок чужого HTML, — это будет показано как символы. Когда данные пришли от пользователя или из сети, это ровно то, что нужно.
| Способ | Что делает со строкой | Когда брать |
|---|---|---|
| elem.textContent = s | Заменяет всё содержимое одним текстовым узлом | Почти всегда, когда нужен текст |
| elem.append(s) | Добавляет текстовый узел в конец, не трогая остальное | Когда текст соседствует с элементами |
| document.createTextNode(s) | Возвращает узел, вставку делаете сами | Когда узел нужен как значение: сохранить, переместить |
| elem.innerHTML = s | Разбирает строку как разметку | Только для разметки, которую написали вы сами |
Созданный элемент не принадлежит документу. Он существует как значение в переменной, у него нет родителя, его не найдёт querySelector, и браузеру нечего рисовать: на экран попадает только то, что висит в дереве документа. Такой узел называют отсоединённым (detached), и проверяется это одним свойством — isConnected.
У отсоединённого узла нет и геометрии: он нигде не расположен, поэтому все измерения дают нули, а правила CSS к нему не применяются — каскаду не от чего считать, у элемента нет предков в документе.
Отсюда классическая ошибка: создали элемент, тут же померили ширину, получили 0 и полчаса ищете виноватого в CSS. Мерить можно только после вставки — до неё мерить попросту нечего.
У невидимости есть выгодная сторона. Пока элемент вне документа, любые операции внутри него ничего не стоят браузеру: пересчитывать нечего, перерисовывать нечего. Поэтому список из ста пунктов собирают в памяти и вставляют одним движением, а не добавляют по пункту в живую страницу.
Формально ничто не запрещает вставить пустой <ul> первым и дополнять его уже в документе — результат будет тот же. Но привычка «сначала собрал, потом вставил» бесплатна, а выигрывает всегда. О самих способах вставки — append, prepend, before, after — подробно в следующей статье.
document.createElement("div")возвращает полноценный элемент, живущий в памяти: без родителя, без места в документе и без пикселей на экране.- Наполняют его как обычный узел —
textContent,className,setAttribute;innerHTMLотличается тем, что разбирает строку как разметку и пересобирает содержимое заново. createTextNodeделает текстовый узел (nodeType3) отдельным значением: строка в нём навсегда остаётся текстом, что и нужно для чужих данных.- Пока
isConnectedравен false, элемент не найдётся селектором и не имеет размеров — измерять его бессмысленно до вставки. - Собирать поддерево целиком и вставлять одним вызовом дешевле, чем добавлять узлы по одному в живую страницу.
1/3Элемент создан через createElement, но ещё не вставлен. Что вернёт его parentNode?
2/3Что выведет console.log(p.children.length) после p.textContent = "<b>привет</b>"?
3/3Зачем нужен document.createTextNode, если есть textContent?
Соберите карточку
Напишите функцию createCard(title, text), которая создаёт и возвращает структуру <div class="card"><h3>заголовок</h3><p>текст</p></div>. Внутрь функции вставка в документ не входит — она только собирает узел. Проверьте, что до вставки карточка не найдена на странице, а после — найдена.
Собираем три элемента и связываем их до вставки: заголовок и абзац кладём в контейнер, контейнер возвращаем наружу.
append принимает сразу несколько узлов, поэтому заголовок и абзац добавляются одним вызовом. Функция ничего не знает о месте вставки — за это отвечает вызывающий код, и такую функцию проще переиспользовать.
Покажите разметку как текст
В переменной userInput лежит строка, пришедшая от пользователя. Выведите её в <div id="log"> так, чтобы теги внутри строки остались видимыми символами, а не превратились в элементы. Проверьте результат по числу дочерних элементов.
Любой из двух путей годится — важно лишь не отдавать строку парсеру:
Короче — log.textContent = userInput: результат тот же, браузер сам создаст текстовый узел. Ошибочный вариант здесь один — innerHTML: он разберёт строку как разметку, и в документе появится настоящий <img> с битым адресом.