Manipularea Modernă a DOM-ului: Event Delegation și DocumentFragment în JavaScript

Pagina web (DOM) DOM Living Standard

Optimizarea performanței DOM la 60/120 FPS: delegarea evenimentelor, inserarea în bloc prin DocumentFragment, reducerea operațiunilor de Layout Thrashing și utilizarea requestAnimationFrame.

Sintaxă
parent.addEventListener('click', e => e.target.closest('.item')) | DocumentFragment

Manipularea arborelui DOM este una dintre cele mai costisitoare operațiuni din browser. Fiecare modificare adusă nodurilor din pagină poate forța motorul de randare să recalculeze stilurile geometrice (Reflow / Layout) și să re-deseneze pixelii pe ecran (Repaint). Pentru a obține animații fluide la 60 sau 120 FPS, dezvoltatorii folosesc două tehnici esențiale: Event Delegation și DocumentFragment.

1. Event Delegation: Un singur listener pentru mii de elemente
// SCENARIU GREȘIT: Adăugarea unui listener individual pe fiecare rând de tabel (consum masiv de RAM):
// document.querySelectorAll('.rand-tabel').forEach(el => el.addEventListener('click', ...));

// SCENARIU CORECT (Event Delegation):
// Atașăm un singur listener pe containerul părinte și folosim e.target.closest():
const containerTabel = document.querySelector('#tabel-utilizatori');

containerTabel.addEventListener('click', (event) => {
    // Găsim cel mai apropiat element buton de acțiune:
    const butonStergere = event.target.closest('.btn-sterge');
    if (!butonStergere) return; // Click-ul a fost în altă parte a tabelului

    const rand = butonStergere.closest('tr');
    const id = rand.dataset.id;
    console.log(`Ștergere utilizator ID: ${id}`);
    rand.remove();
});
2. Inserare în masă fără Reflow folosind DocumentFragment
// Generarea a 10.000 de elemente fără a bloca interfața utilizator
const listaElemente = ['Produs 1', 'Produs 2', 'Produs 3', /* ... 10.000 elemente */];
const containerLista = document.querySelector('#lista-produse');

// Creăm un container virtual în memorie (nu face parte din DOM-ul vizibil):
const fragment = document.createDocumentFragment();

for (const text of listaElemente) {
    const li = document.createElement('li');
    li.textContent = text;
    li.className = 'produs-item';
    fragment.appendChild(li); // Zero cost de Reflow/Repaint!
}

// O SINGURĂ inserție în DOM-ul real pentru toate cele 10.000 de elemente:
containerLista.appendChild(fragment);

Ce este Layout Thrashing și cum îl eviți

Layout Thrashing apare când alternezi citirea și scrierea din DOM într-o buclă (de exemplu: citești offsetWidth, apoi modifici style.width, apoi citești din nou). Browserul este forțat să recalculeze geometria la fiecare iterație. Regula este: citește toate datele mai întâi (Batch Reads), apoi aplică toate modificările (Batch Writes), preferabil în interiorul unui requestAnimationFrame().