
Serinin son makalesine hoş geldiniz! Bu makalede, event bubbling'in pratik ve güçlü bir uygulaması olan Event Delegation (Olay Delegasyonu) tekniğini öğreneceğiz. Bu teknik, özellikle dinamik olarak eklenen elementler ve performans optimizasyonu için vazgeçilmezdir.
Problem: Dinamik Elementlere Event Ekleme
Şu senaryoyu düşünün: Bir yapılacaklar listesi uygulaması yapıyorsunuz.
<ul id="gorev-listesi">
<li>Alışveriş yap <button class="sil">❌</button></li>
<li>Spor yap <button class="sil">❌</button></li>
<li>Kitap oku <button class="sil">❌</button></li>
</ul>
<button id="ekle">Yeni Görev Ekle</button>// Sayfa yüklendiğinde var olan butonlara event ekle
const silButonlari = document.querySelectorAll('.sil');
silButonlari.forEach(function(btn) {
btn.addEventListener('click', function() {
this.parentElement.remove();
});
});
// Yeni görev ekleme
const ekleBtn = document.querySelector('#ekle');
const liste = document.querySelector('#gorev-listesi');
ekleBtn.addEventListener('click', function() {
const yeniGorev = document.createElement('li');
yeniGorev.innerHTML = 'Yeni görev <button class="sil">❌</button>';
liste.appendChild(yeniGorev);
});Problem: Yeni eklenen görevlerin sil butonları çalışmıyor! 🐛
Neden? Çünkü querySelectorAll('.sil') sadece sayfa yüklendiğinde var olan butonları seçer. Sonradan eklenen butonlara event listener eklenmedi.
Çözüm 1: Her Seferinde Event Eklemek (Kötü Yöntem)
ekleBtn.addEventListener('click', function() {
const yeniGorev = document.createElement('li');
yeniGorev.innerHTML = 'Yeni görev <button class="sil">❌</button>';
liste.appendChild(yeniGorev);
// Yeni butona da event ekle
const yeniSilBtn = yeniGorev.querySelector('.sil');
yeniSilBtn.addEventListener('click', function() {
this.parentElement.remove();
});
});Bu çalışır ama kötü bir pratiktir çünkü:
- Kod tekrarı var
- Her yeni element için ayrı event listener oluşturuluyor
- Hafıza (memory) kullanımı artar
- Bakımı zor
Çözüm 2: Event Delegation (En İyi Yöntem) ⭐
Event delegation, bubbling özelliğinden faydalanır. Event listener'ı child elementlere değil, parent elemente ekleriz.
const liste = document.querySelector('#gorev-listesi');
// Tek bir event listener - parent'a eklendi
liste.addEventListener('click', function(event) {
// Tıklanan element sil butonu mu?
if (event.target.classList.contains('sil')) {
event.target.parentElement.remove();
}
});
// Yeni görev ekleme - artık ayrıca event eklemeye gerek yok!
ekleBtn.addEventListener('click', function() {
const yeniGorev = document.createElement('li');
yeniGorev.innerHTML = 'Yeni görev <button class="sil">❌</button>';
liste.appendChild(yeniGorev);
});Nasıl çalışır?
- Kullanıcı sil butonuna tıklar
- Event bubbling ile
<ul>elementine ulaşır event.targetile tıklanan elementi kontrol ederiz- Eğer sil butonuysa, işlemi yaparız
Event Delegation'ın Avantajları
| Avantaj | Açıklama |
|---|---|
| 🚀 Performans | Onlarca yerine tek event listener |
| 🔄 Dinamik elementler | Sonradan eklenenler de çalışır |
| 🧹 Temiz kod | Daha az tekrar, daha kolay bakım |
| 💾 Hafıza | Daha az memory kullanımı |
Gerçek Hayat Örneği: Yorum Sistemi
<div id="yorumlar">
<div class="yorum" data-id="1">
<p>Harika bir yazı!</p>
<button class="begen">👍 Beğen</button>
<button class="yanıtla">💬 Yanıtla</button>
<button class="sil">🗑️ Sil</button>
</div>
<div class="yorum" data-id="2">
<p>Çok faydalı oldu, teşekkürler.</p>
<button class="begen">👍 Beğen</button>
<button class="yanıtla">💬 Yanıtla</button>
<button class="sil">🗑️ Sil</button>
</div>
</div>const yorumlarContainer = document.querySelector('#yorumlar');
yorumlarContainer.addEventListener('click', function(event) {
const tiklanilan = event.target;
const yorum = tiklanilan.closest('.yorum');
if (!yorum) return; // Yorum dışına tıklandıysa çık
const yorumId = yorum.dataset.id;
// Beğen butonuna tıklandı
if (tiklanilan.classList.contains('begen')) {
console.log(`Yorum ${yorumId} beğenildi`);
tiklanilan.textContent = '👍 Beğenildi';
tiklanilan.disabled = true;
}
// Yanıtla butonuna tıklandı
if (tiklanilan.classList.contains('yanıtla')) {
console.log(`Yorum ${yorumId} yanıtlanıyor`);
yanitFormuGoster(yorumId);
}
// Sil butonuna tıklandı
if (tiklanilan.classList.contains('sil')) {
if (confirm('Yorumu silmek istediğinizden emin misiniz?')) {
yorum.remove();
console.log(`Yorum ${yorumId} silindi`);
}
}
});closest() Metodu
event.target.closest(selector) metodu, tıklanan elementin kendisi veya en yakın parent'ı arasında belirtilen selector'a uyan elementi bulur.
// Örnek HTML yapısı:
// <div class="kart">
// <div class="kart-icerik">
// <button class="sil">Sil</button>
// </div>
// </div>
container.addEventListener('click', function(event) {
// Tıklanan butona en yakın .kart elementini bul
const kart = event.target.closest('.kart');
if (kart) {
console.log('Kart bulundu:', kart);
}
});Gerçek Hayat Örneği: Ürün Galerisi
<div id="urun-galeri">
<!-- Ürünler dinamik olarak yüklenecek -->
</div>const galeri = document.querySelector('#urun-galeri');
// Ürünleri API'den al ve göster
async function urunleriYukle() {
const urunler = await fetch('/api/urunler').then(r => r.json());
urunler.forEach(function(urun) {
const kartHTML = `
<div class="urun-kart" data-id="${urun.id}">
<img src="${urun.resim}" alt="${urun.ad}">
<h3>${urun.ad}</h3>
<p>${urun.fiyat} TL</p>
<button class="sepete-ekle">Sepete Ekle</button>
<button class="favorile">❤️ Favorile</button>
</div>
`;
galeri.innerHTML += kartHTML;
});
}
// Tek bir event listener ile tüm ürün kartlarını yönet
galeri.addEventListener('click', function(event) {
const btn = event.target;
const kart = btn.closest('.urun-kart');
if (!kart) return;
const urunId = kart.dataset.id;
if (btn.classList.contains('sepete-ekle')) {
sepeteEkle(urunId);
btn.textContent = '✓ Sepete Eklendi';
btn.disabled = true;
}
if (btn.classList.contains('favorile')) {
favorile(urunId);
btn.textContent = '❤️ Favorilerde';
}
});
urunleriYukle();Gerçek Hayat Örneği: Tablo İşlemleri
const tablo = document.querySelector('#veri-tablosu');
tablo.addEventListener('click', function(event) {
const btn = event.target;
const satir = btn.closest('tr');
if (!satir) return;
const id = satir.dataset.id;
// Düzenle
if (btn.classList.contains('duzenle')) {
duzenlemeModunaGec(satir);
}
// Sil
if (btn.classList.contains('sil')) {
satirSil(id, satir);
}
// Kopyala
if (btn.classList.contains('kopyala')) {
satirKopyala(id);
}
// Satır seçimi (buton dışında bir yere tıklanınca)
if (!btn.matches('button')) {
satirSec(satir);
}
});matches() Metodu
element.matches(selector) metodu, elementin belirtilen selector'a uyup uymadığını kontrol eder.
container.addEventListener('click', function(event) {
// Sadece butonlara tepki ver
if (event.target.matches('button')) {
console.log('Bir butona tıklandı');
}
// Sadece .ozel-link class'ına sahip linklere tepki ver
if (event.target.matches('a.ozel-link')) {
event.preventDefault();
console.log('Özel link tıklandı');
}
});Performans Karşılaştırması
1000 elemanlı bir liste için:
| Yöntem | Event Listener Sayısı | Hafıza Kullanımı |
|---|---|---|
| Her elemente ayrı | 1000 | Yüksek |
| Event Delegation | 1 | Düşük |
// ❌ Kötü: 1000 event listener
items.forEach(item => {
item.addEventListener('click', handler);
});
// ✅ İyi: 1 event listener
container.addEventListener('click', function(event) {
if (event.target.matches('.item')) {
handler(event);
}
});Ne Zaman Event Delegation Kullanmalı?
✅ Kullanın:
- Dinamik olarak eklenen elementler
- Çok sayıda benzer element (liste, tablo, galeri)
- Performans kritik durumlarda
⚠️ Dikkatli olun:
- Çok karmaşık iç içe yapılarda okunabilirlik azalabilir
- Bazı event'ler bubble etmez (focus, blur gibi)
Özet ve Seri Kapanışı
Bu makalede öğrendiklerimiz:
✅ Event Delegation, parent elementi dinleyerek child'ları yönetir
✅ Dinamik elementler için mükemmel çözüm
✅ Performans açısından çok daha verimli
✅ closest() ve matches() metodları delegation için kullanışlı
🎉 Seri Özeti
Bu 8 makalelık seride JavaScript DOM Events konusunu kapsamlı şekilde inceledik:
- DOM Events'e Giriş - Olayların temelleri
- Event Dinleme Yöntemleri - Inline, onclick, addEventListener
- Events ve this - Callback'lerde this kullanımı
- Klavye Olayları ve Event Object - keydown, keyup, event bilgileri
- Form Events ve preventDefault - Form gönderimi kontrolü
- Input ve Change Events - Gerçek zamanlı form takibi
- Event Bubbling - Olayların yayılması
- Event Delegation - Verimli event yönetimi
Bu bilgilerle artık interaktif, dinamik ve performanslı web uygulamaları geliştirebilirsiniz!


