Каждый разработчик рано или поздно сталкивается с тем, что интерфейс начинает подтормаживать. Часто причина — неэффективная работа с DOM. В этой статье разберём, как заставить скрипты летать, а не ползать, и покажем конкретные приёмы с примерами кода.

Digital-студия WNDER

Почему DOM — это медленно?

DOM (Document Object Model) — это представление HTML-документа в виде дерева объектов. Когда вы изменяете DOM, браузеру нужно пересчитать стили, перерисовать элементы и выполнить ещё кучу работы. Каждое обращение к DOM — как поход в магазин: можно сходить один раз и купить всё, а можно бегать за каждой мелочью. Чем больше таких «походов», тем медленнее.

Например, такой код:

for (let i = 0; i < 1000; i++) {
  document.getElementById('list').innerHTML += `
  • Item ${i}
  • `; }

    Он будет работать очень медленно, потому что на каждой итерации происходит перерисовка всего списка. Вместо этого лучше собрать всё в строку и вставить один раз.

    Правило первое: кэшируйте элементы

    Если вы обращаетесь к одному и тому же элементу несколько раз, сохраните его в переменную. Поиск по DOM — дорогая операция.

    // Плохо
    for (let i = 0; i < 100; i++) {
      document.querySelector('.my-element').style.color = 'red';
    }
    
    // Хорошо
    const element = document.querySelector('.my-element');
    for (let i = 0; i < 100; i++) {
      element.style.color = 'red';
    }
    
    Лайфхак: Если элемент используется в нескольких функциях, создайте глобальную переменную или используйте замыкание, чтобы не искать его каждый раз.

    Правило второе: минимизируйте перерисовки

    Браузер перерисовывает страницу после каждого изменения DOM. Если вы меняете стили или добавляете элементы в цикле, это вызывает множество перерисовок. Лучше сгруппировать изменения.

    Самый простой способ — использовать DocumentFragment для добавления нескольких элементов:

    const fragment = document.createDocumentFragment();
    for (let i = 0; i < 1000; i++) {
      const li = document.createElement('li');
      li.textContent = `Item ${i}`;
      fragment.appendChild(li);
    }
    document.getElementById('list').appendChild(fragment);
    

    Или изменить стили через cssText:

    // Плохо
    element.style.color = 'red';
    element.style.fontSize = '16px';
    element.style.margin = '10px';
    
    // Хорошо
    element.style.cssText = 'color: red; font-size: 16px; margin: 10px;';
    

    Правило третье: используйте делегирование событий

    Если у вас много элементов, на которые нужно повесить обработчик, не вешайте его на каждый. Вместо этого повесьте один обработчик на родителя и используйте всплытие событий.

    // Плохо: 100 обработчиков
    const buttons = document.querySelectorAll('.btn');
    buttons.forEach(btn => btn.addEventListener('click', handleClick));
    
    // Хорошо: 1 обработчик
    document.getElementById('container').addEventListener('click', (event) => {
      if (event.target.classList.contains('btn')) {
        handleClick(event);
      }
    });
    

    Это не только ускоряет работу, но и экономит память.

    Правило четвёртое: избегайте layout thrashing

    Layout thrashing (или «дёргание макета») происходит, когда вы чередуете чтение и запись в DOM. Браузер вынужден пересчитывать макет после каждой записи, чтобы вернуть актуальные данные при чтении.

    Пример проблемного кода:

    for (let i = 0; i < 10; i++) {
      const width = element.offsetWidth; // чтение
      element.style.width = width + 10 + 'px'; // запись
    }
    

    Лучше сначала выполнить все чтения, затем все записи:

    const width = element.offsetWidth;
    for (let i = 0; i < 10; i++) {
      element.style.width = width + i * 10 + 'px';
    }
    

    Сравнение методов работы с DOM

    В таблице ниже — основные методы и их производительность при массовых операциях.

    Метод Когда использовать Производительность
    innerHTML Массовая вставка HTML-строки Высокая (но осторожно с XSS)
    createElement + appendChild Динамическое создание элементов Средняя (много вызовов)
    DocumentFragment Добавление группы элементов Высокая (одна перерисовка)
    insertAdjacentHTML Вставка HTML в определённое место Высокая
    Правило: Всегда группируйте изменения DOM. Чем меньше операций записи, тем быстрее работает скрипт.

    Что в итоге?

    Оптимизация работы с DOM — это не магия, а набор простых правил. Кэшируйте элементы, группируйте изменения, используйте делегирование событий и избегайте layout thrashing. Эти приёмы помогут вашему коду работать быстрее и плавнее. И помните: лучше один раз вставить 1000 элементов через фрагмент, чем 1000 раз дёргать DOM по мелочи.

    Попробуйте применить хотя бы одно правило в своём проекте — и вы заметите разницу.

    Студия WNDER