25.12.2025

JavaScript’te Event Delegation: Daha Az Listener ile Daha Temiz ve Hızlı Arayüzler

Tek tek event listener bağlamak yerine tek noktadan yönet: dinamik listelerde sade, hızlı ve bakımı kolay çözüm.

Neden Event Delegation?

Tek tek butonlara/öğelere addEventListener eklemek kısa vadede kolay görünür; ama liste büyüdükçe ve DOM dinamikleştikçe iki sorun ortaya çıkar:

  • Çok sayıda listener: Bellek ve yönetim maliyeti artar.
  • Sonradan eklenen öğeler: Yeni gelen elemanlara listener bağlamayı unutmak sık olur.

Event delegation, olayların (event) DOM ağacında yukarı doğru “kabarcıklanması” (bubbling) özelliğinden yararlanır: Listener’ı üst kapsayıcıya koyar, tıklanan öğeyi içeride ayıklarsın.


Temel Örnek: Dinamik Todo Listesi

Aşağıdaki senaryoda listeye yeni li eklendikçe ayrı listener eklemiyoruz.

<ul id="todos">
  <li data-id="1"><button data-action="toggle">Tamamla</button> <button data-action="delete">Sil</button></li>
</ul>

<form id="addForm">
  <input id="todoText" placeholder="Yeni todo" />
  <button>Ekle</button>
</form>

const todos = document.querySelector('#todos');
const addForm = document.querySelector('#addForm');
const todoText = document.querySelector('#todoText');

// Tek listener: tüm buton tıklamalarını yakalar
todos.addEventListener('click', (e) => {
  const btn = e.target.closest('button');
  if (!btn) return; // tıklanan yer button değilse

  const li = btn.closest('li');
  const action = btn.dataset.action;

  if (action === 'toggle') {
    li.classList.toggle('done');
  }

  if (action === 'delete') {
    li.remove();
  }
});

// Listeye eleman eklerken ekstra listener gerekmez
addForm.addEventListener('submit', (e) => {
  e.preventDefault();
  const text = todoText.value.trim();
  if (!text) return;

  const id = crypto.randomUUID();
  const li = document.createElement('li');
  li.dataset.id = id;
  li.innerHTML = `
    <button data-action="toggle">Tamamla</button>
    <button data-action="delete">Sil</button>
    <span>${text}</span>
  `;

  todos.appendChild(li);
  todoText.value = '';
});

Buradaki küçük ama kritik detaylar

  • e.target.closest('button'): İkon/SVG gibi iç elemanlara tıklansa bile butonu bulur.
  • data-action: “Hangi butona basıldı?” bilgisini okunur hale getirir.
  • closest('li'): İşlem yapılacak satırı kolayca seçer.

Delegation Ne Zaman Uygun Değil?

Event delegation her yerde sihirli değildir:

  • Bubbling olmayan eventler: focus, blur gibi bazı eventler kabarcıklanmaz (çözüm: focusin/focusout veya capture).
  • Çok özel etkileşimler: Her öğe için farklı state/closure gerekiyorsa tek listener karmaşıklaşabilir.
  • Yüksek frekanslı eventler: mousemove gibi eventlerde kapsayıcıya bağlamak gereksiz yoğunluk yaratabilir.

Capture ile İnce Ayar

Bazen event’i bubbling aşamasında değil, capture aşamasında yakalamak istersin:

document.addEventListener('click', (e) => {
  // üstten aşağı yakalama
}, { capture: true });

Bu, özellikle “önce ben müdahale edeyim” dediğin global senaryolarda (ör. modal dışına tıklayınca kapat) iş görebilir.


Pratik İpucu: Tek Bir Router Gibi Düşün

Delegation’ı küçük bir “UI router” gibi kurgulayabilirsin:

todos.addEventListener('click', (e) => {
  const actionEl = e.target.closest('[data-action]');
  if (!actionEl) return;

  const actions = {
    toggle: () => actionEl.closest('li').classList.toggle('done'),
    delete: () => actionEl.closest('li').remove(),
  };

  actions[actionEl.dataset.action]?.();
});

Bu yaklaşım büyüyen arayüzlerde if-else zincirlerini azaltır.


Sonuç

Event delegation, özellikle liste/tablolar, dinamik içerikler ve çok sayıda tıklanabilir öğe içeren UI’larda kodu sadeleştirir. Daha az listener ile daha az hata noktası, daha iyi bakım ve genellikle daha iyi performans elde edersin.