Tüm blog yazıları

JavaScript DOM Events: Event Delegation

JavaScript DOM Events serisinin bu son makalesinde, dinamik elementlere kolayca event eklemenin ve performans sorunlarını çözmenin güçlü yolu olan Event Delegation tekniğini keşfedin. To-do listesi örneğiyle, neden vazgeçilmez olduğunu görün.

eminbasbayan6 dk okuma
JavaScript Event Delegation tekniği ile dinamik listelerde olay yönetimi ve performans optimizasyonu.

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.

html
<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>
javascript
// 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)

javascript
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.

javascript
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?

  1. Kullanıcı sil butonuna tıklar
  2. Event bubbling ile <ul> elementine ulaşır
  3. event.target ile tıklanan elementi kontrol ederiz
  4. Eğer sil butonuysa, işlemi yaparız

Event Delegation'ın Avantajları

AvantajAçıklama
🚀 PerformansOnlarca yerine tek event listener
🔄 Dinamik elementlerSonradan eklenenler de çalışır
🧹 Temiz kodDaha az tekrar, daha kolay bakım
💾 HafızaDaha az memory kullanımı

Gerçek Hayat Örneği: Yorum Sistemi

html
<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>
javascript
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.

javascript
// Ö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

html
<div id="urun-galeri">
    <!-- Ürünler dinamik olarak yüklenecek -->
</div>
javascript
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

javascript
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.

javascript
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öntemEvent Listener SayısıHafıza Kullanımı
Her elemente ayrı1000Yüksek
Event Delegation1Düşük
javascript
// ❌ 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:

  1. DOM Events'e Giriş - Olayların temelleri
  2. Event Dinleme Yöntemleri - Inline, onclick, addEventListener
  3. Events ve this - Callback'lerde this kullanımı
  4. Klavye Olayları ve Event Object - keydown, keyup, event bilgileri
  5. Form Events ve preventDefault - Form gönderimi kontrolü
  6. Input ve Change Events - Gerçek zamanlı form takibi
  7. Event Bubbling - Olayların yayılması
  8. Event Delegation - Verimli event yönetimi

Bu bilgilerle artık interaktif, dinamik ve performanslı web uygulamaları geliştirebilirsiniz!

JavaScript Classes ile pratik uygulamalar, metotlar arası iletişim ve gerçek dünya kod örnekleri
JavaScript Dersleri9 dk okuma

JavaScript Classes - Pratik Uygulamalar

JavaScript class'ları ile pratik uygulamalara geçin! Metotlar arası iletişimden constructor'da metot çağırmaya, kod tekrarını azaltmaktan gerçek dünya senaryolarına kadar class kullanımının inceliklerini keşfedin. Kendi alışveriş sepeti örneğinizle bilginizi pekiştirin.

Yazıyı oku
JavaScript class yapısını gösteren bir kod örneği, ES6 ile modern nesne oluşturma
JavaScript Dersleri9 dk okuma

JavaScript Classes

ES6 ile JavaScript'e gelen class yapısı sayesinde nesne oluşturmak artık çok daha kolay ve düzenli! Constructor function karmaşasından kurtulup, okunabilir ve yönetilebilir kodlar yazmak isteyenler için modern JavaScript'in temel taşı olan class'ları tüm detaylarıyla keşfedin. Bu rehberle nesne şabl

Yazıyı oku
JavaScript kodunda Constructor Function ve new keyword'ü ile nesne oluşturma örnekleri
JavaScript Dersleri8 dk okuma

Constructor Functions ve new Keyword

Factory Function'ların hafıza israfı sorununa Constructor Function'lar ve new keyword'ü ile son verin! Metotları paylaştırarak JavaScript objelerini daha verimli nasıl oluşturacağınızı, new'in perde arkasında neler yaptığını ve prototype sisteminin gücünü bu rehberde keşfedin.

Yazıyı oku

Çerez tercihlerin

Platformun çalışması için zorunlu çerezler kullanıyoruz. İzin verirsen anonim kullanım verilerini ölçüyoruz. Ayrıntıları incele