Создание элементов и списки
Страница из данных
В настоящих приложениях список товаров, сообщений или задач не написан в HTML заранее — он приходит как массив данных, и JavaScript сам строит из него элементы. Добавили товар в корзину — в массиве стало на один объект больше, и на странице появилась строка.
createElement
Новый элемент создаётся «в воздухе» и пока не виден:
const li = document.createElement("li"); // <li></li>
li.textContent = "Купить хлеб";
li.classList.add("task");
li.dataset.id = "7";
Чтобы он появился, его нужно вставить в элемент, который уже на странице.
Куда вставить
const list = document.querySelector("#todo");
list.append(li); // в конец list
list.prepend(li); // в начало list
list.before(li); // перед list (соседом)
list.after(li); // после list
list.append(a, b, "текст"); // несколько сразу, строки становятся текстом
Один элемент не может стоять в двух местах: если вставить уже вставленный li ещё раз, он
переедет на новое место.
Удалить и очистить
li.remove(); // удалить элемент со страницы
list.replaceChildren(); // удалить всех детей
list.innerHTML = ""; // то же самое, тоже часто встречается
Отрисовка списка из массива
Самый частый приём: функция render, которая очищает контейнер и строит его заново по массиву.
Изменились данные — вызвали render() ещё раз.
const tasks = ["Помыть посуду", "Сделать уроки", "Погулять"];
function render() {
const list = document.querySelector("#todo");
list.replaceChildren();
for (const text of tasks) {
const li = document.createElement("li");
li.textContent = text;
list.append(li);
}
}
render();
tasks.push("Почитать");
render();
Перестраивать всё заново проще, чем искать и менять отдельные пункты, и для небольших списков это быстро. Именно эту идею — «интерфейс это функция от данных» — потом доводят до совершенства React и Vue.
Почему не строкой через innerHTML
Можно собрать разметку строкой:
list.innerHTML = tasks.map((t) => `<li>${t}</li>`).join("");
Это коротко, но опасно: если текст пришёл от пользователя и содержит теги, они выполнятся (XSS).
createElement + textContent всегда безопасны — теги в тексте так и останутся текстом. Строки с
innerHTML допустимы только для своей, заранее известной разметки.
Кнопка внутри созданного элемента
Обработчики можно вешать на элементы сразу после создания — замыкание запомнит, к какому пункту относится кнопка:
function addTask(text) {
const li = document.createElement("li");
li.append(text + " ");
const done = document.createElement("button");
done.textContent = "✓";
done.addEventListener("click", () => li.classList.toggle("done"));
li.append(done);
document.querySelector("#todo").append(li);
}
У каждого li своя кнопка, и каждая кнопка помнит свой li.
Итог
document.createElement("тег")— новый элемент, он не виден, пока не вставлен.append/prepend/before/after— вставить;remove()— удалить;replaceChildren()— очистить.- Список из массива: очистить контейнер и создать элементы в цикле — функция
render(). - Пользовательский текст — только через
textContent, не черезinnerHTML.