Урок 03

Изменение DOM

Создание элементов

const div = document.createElement('div');
const text = document.createTextNode('Привет');
const fragment = document.createDocumentFragment();

Добавление

parent.appendChild(el);        // в конец
parent.append(el, 'текст');    // в конец, несколько
parent.prepend(el);            // в начало
parent.before(el);             // до
parent.after(el);              // после
parent.replaceChild(newEl, oldEl);

append и prepend принимают строки и узлы:

list.append('текст', anotherElement);

Удаление

parent.removeChild(el);
parent.remove();          // сам себя

Перемещение

newParent.appendChild(existingEl); // переместит, а не скопирует

Клонирование

const clone = el.cloneNode();      // без детей
const deep = el.cloneNode(true);   // с детьми

Текст

el.textContent = 'новый текст'; // безопасно
el.innerText = 'новый текст';   // учитывает стили
el.innerHTML = '<b>HTML</b>';   // парсит HTML

Разница:

  • textContent — быстрее, не парсит HTML
  • innerText — учитывает CSS (скрытые элементы)
  • innerHTML — парсит HTML, опасен с пользовательскими данными

XSS через innerHTML

const userInput = '<img src=x onerror="alert(1)">';
el.innerHTML = userInput; // выполнит скрипт

Используй textContent или санитизацию.

Атрибуты

el.setAttribute('href', '/new');
el.getAttribute('href');
el.removeAttribute('href');

el.id = 'new-id';
el.className = 'new-class';
el.href = '/new'; // для ссылок

Стили

el.style.color = 'red';
el.style.fontSize = '16px';
el.style.cssText = 'color: red; font-size: 16px';

el.style.removeProperty('color');

Через классы:

el.classList.add('active');
el.classList.remove('active');
el.classList.toggle('active');

Создание структуры

const card = document.createElement('div');
card.className = 'card';

const title = document.createElement('h3');
title.textContent = 'Заголовок';

const body = document.createElement('p');
body.textContent = 'Текст';

card.append(title, body);
document.body.append(card);

Шаблоны

<template id="card-template">
    <div class="card">
        <h3></h3>
        <p></p>
    </div>
</template>
const template = document.getElementById('card-template');
const clone = template.content.cloneNode(true);

clone.querySelector('h3').textContent = 'Заголовок';
clone.querySelector('p').textContent = 'Текст';

document.body.append(clone);

DocumentFragment

Контейнер без родителя. Вставляется одним действием:

const fragment = document.createDocumentFragment();

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

list.appendChild(fragment); // одно обращение к DOM

Быстрее, чем 100 отдельных вставок.

Производительность

Каждое изменение DOM вызывает reflow и repaint. Минимизируй:

// Плохо: 100 reflow
for (let i = 0; i < 100; i++) {
    list.append(createItem(i));
}

// Хорошо: 1 reflow
const fragment = document.createDocumentFragment();
for (let i = 0; i < 100; i++) {
    fragment.append(createItem(i));
}
list.append(fragment);

Итоги

  • Создание через createElement
  • append, prepend, before, after
  • remove для удаления
  • textContent безопаснее innerHTML
  • template для шаблонов
  • DocumentFragment для массовых вставок