JavaScript’te olay (event) mantığı: Neyi, nerede dinliyorsunuz?

Web arayüzlerinde “tıklama”, “üzerine gelme” ya da “form gönderme” gibi etkileşimler tarayıcı tarafından olay (event) olarak üretilir. JavaScript tarafında amacınız şudur: doğru hedefte (elementte) doğru olayı dinlemek ve kullanıcı deneyimini bozmadan gerekli işlemi yapmak.

Bu yazı, üç çok yaygın senaryoya odaklanır:

  • Click: buton, kart, link vb. tıklanınca
  • Hover: fare bir elementin üstüne gelince (mouseenter)
  • Form submit: kullanıcı formu gönderince (butonla veya Enter ile)

Tüm örneklerde temel araç addEventListener olacak. Ayrıntılı sözdizimi ve davranış için: EventTarget.addEventListener().


addEventListener hızlı başlangıç: sözdizimi ve iki kritik detay

1) En temel kullanım

Genel kalıp şöyle:

hedef.addEventListener('eventTuru', callback)

hedef genellikle bir DOM elementidir (ör. bir buton). callback ise olay gerçekleşince çalışacak fonksiyondur.

2) Aynı dinleyici birden fazla kez eklenebilir mi?

MDN’de anlatıldığı gibi, aynı event türü için aynı dinleyici fonksiyon referansını (ve aynı capture değerini) tekrar eklerseniz, tarayıcı bunu ikinci kez listeye eklemez. Yani “aynı dinleyici” çoğalmaz.

Ancak pratikte şu tuzak sık görülür: inline anonim fonksiyon her yazıldığında yeni bir fonksiyon ürettiği için, “aynı işi yapıyor gibi görünen” dinleyiciler aslında farklı referanslar olur ve birikerek çoğalabilir.

Kısa örnek: Neden inline fonksiyonlar çoğalır?

// 1) Aynı referans: ikinci ekleme çoğalmaz
const handler = () => console.log('tık');
btn.addEventListener('click', handler);
btn.addEventListener('click', handler);

// 2) Farklı referans: her seferinde yeni listener eklenir
btn.addEventListener('click', () => console.log('tık'));
btn.addEventListener('click', () => console.log('tık'));

3) Dinleyiciyi kaldırma (removeEventListener) neden önemli?

Tek sayfa uygulamalarında (SPA) veya dinamik içerik üreten sayfalarda, artık görünmeyen bileşenlerin dinleyicilerini temizlemeyi unutmak performans ve beklenmeyen davranışlara yol açabilir. Bu yüzden dinleyiciyi eklediğiniz gibi kaldırmayı da tasarlayın:

const handler = (e) => { /* ... */ }
hedef.addEventListener('click', handler)
hedef.removeEventListener('click', handler)


1) Click olayı: buton ve link etkileşimlerini doğru yönetme

click, kullanıcı bir işaretleme aygıtı düğmesiyle (çoğunlukla fare) bir öğe üzerinde basıp bıraktığında tetiklenen temel olaylardan biridir. Tanım ve detaylar için: Element: click event (MDN).

Örnek: Buton tıklanınca metin değiştirme

Aşağıdaki örnek, bir butona tıklanınca sayfadaki bir metni güncelleme fikrini gösterir:

// HTML: <button id="buyBtn">Satın al</button>
// HTML: <p id="status">Hazır</p>
const btn = document.querySelector('#buyBtn');
const status = document.querySelector('#status');
btn.addEventListener('click', () => {
  status.textContent = 'Tıklandı';
});

İpucu: Link (a) tıklamalarında “varsayılan davranış”

Linke tıklayınca tarayıcı genellikle başka bir sayfaya gider. Eğer tıklamayı sayfa içinde ele almak istiyorsanız (ör. modal açmak, filtre uygulamak), olay içinde event.preventDefault() kullanılır.

Not: Bazı olaylarda veya bazı koşullarda “varsayılan davranış”ı engelleme etkisi beklediğiniz gibi olmayabilir. Kullandığınız event türü ve element için hedef tarayıcılarda test etmek iyi bir pratiktir.

Click için pratik kontrol listesi

  • Butonsa mümkünse gerçek <button> kullanın (klavye erişilebilirliği daha tutarlıdır).
  • Linkse gerçekten navigasyon mu yapıyorsunuz, yoksa sadece “buton gibi” mi davranacak? Buna göre <a> veya <button> seçin.
  • Tek bir kapsayıcı içinde çok sayıda tıklanabilir öğe varsa, event delegation (aşağıda) düşünün.

2) Hover davranışı: mouseenter ne zaman tercih edilir?

Hover senaryoları (tooltip, alt menü, kart vurgusu) genellikle fare olaylarıyla yapılır. Burada en çok karıştırılan ikili: mouseenter ve mouseover.

MDN’e göre mouseenter olayı kabarcıklanmaz (bubbling yok). Bu, iç içe elementlerde fare hareket ederken gereksiz tekrar tetiklenmeleri azaltabildiği için bazı hover tasarımlarında avantaj sağlar.

Örnek: Bir kartın üstüne gelince açıklama gösterme

// HTML: <div class="card">...<div class="tip" hidden>...</div></div>
const card = document.querySelector('.card');
const tip = card.querySelector('.tip');
card.addEventListener('mouseenter', () => {
  tip.hidden = false;
});
card.addEventListener('mouseleave', () => {
  tip.hidden = true;
});

mouseenter vs mouseover: hızlı karşılaştırma

Özellik mouseenter mouseover
Kabarcıklanma (bubbling) Yok Var
İç içe öğelerde tetiklenme Daha “sakin” (üst öğede ekstra tetiklenme az) Alt öğelere girip çıkarken sık tetiklenebilir
Delegation ile kullanım Zor (bubbling olmadığı için) Uygun

Hover için önemli not: mobil/touch cihazlar

Touch cihazlarda “hover” kavramı her zaman beklediğiniz gibi çalışmaz. Bu yüzden kritik bir işlevi yalnızca hover’a bağlamamak (ör. menüyü sadece hover ile açmak) daha dayanıklı bir tasarım sağlar.


3) Form submit olayı: Enter tuşunu da kapsayan doğru yaklaşım

Formlarda en güvenilir dinleme noktası çoğu zaman butonun click’i değil, formun submit olayıdır. Çünkü kullanıcı sadece “Gönder” butonuna tıklamaz; input alanındayken Enter tuşu da submit tetikleyebilir. Referans: HTMLFormElement: submit event (MDN).

Örnek: Formu yakala, sayfa yenilenmesini engelle, veriyi oku

// HTML: <form id="signup"> ... </form>
const form = document.querySelector('#signup');
form.addEventListener('submit', (event) => {
  event.preventDefault();
  const data = new FormData(form);
  const email = data.get('email');
  // burada fetch ile gönderme, ekrana mesaj basma vb. yapılabilir
});

Bu örnekte preventDefault() formun varsayılan gönderimini (tipik olarak sayfa navigasyonu/yenilenmesi) durdurmak için kullanılır. Böylece veriyi JavaScript ile işleyebilirsiniz.

Neden buton click yerine submit dinlemek daha tutarlı olur?

  • Enter tuşu gibi klavye davranışlarını otomatik kapsar.
  • Formun içinde farklı “submitter” öğeler olabilir; form seviyesinde dinlemek daha merkezidir.
  • Doğrulama/validasyon akışlarıyla entegrasyonu yönetmek genellikle daha kolaydır (yine de proje gereksiniminize göre test etmeniz gerekir).

Programatik submit: submit() ile requestSubmit() farkı

Formu JavaScript ile göndermek istediğinizde iki yöntem sık geçer:

  • form.submit(): MDN’in submit event sayfasında da belirtildiği gibi, bu çağrı genellikle submit event’i tetiklemeden formu gönderebilir ve tarayıcının normal doğrulama akışını (constraint validation) atlayabilir.
  • form.requestSubmit(): Normal submit akışını (submit event ve ilgili davranışlar) istiyorsanız daha uygun bir seçenek olabilir.

Detaylar ve tarayıcı davranışları için: MDN submit event.


Event delegation: Çok sayıda öğe için tek dinleyici

Liste, tablo veya kart grid’i gibi yapılarda her öğeye ayrı click dinleyicisi eklemek yerine, kapsayıcıya tek dinleyici ekleyip tıklanan öğeyi event.target üzerinden ayırt etmek sıklıkla daha pratik olur. Bu yaklaşıma event delegation denir.

Örnek: Liste içindeki buton tıklamalarını tek yerden yönetme

// HTML: <ul id="todo"><li><button data-action="done">Bitti</button> ...
const list = document.querySelector('#todo');
list.addEventListener('click', (event) => {
  const btn = event.target.closest('button');
  if (!btn) return;
  if (btn.dataset.action === 'done') {
    // ilgili satırı bul ve güncelle
  }
});

Not: Delegation çoğunlukla bubbling yapan olaylarda işe yarar. Bu nedenle hover tarafında delegation istiyorsanız mouseenter yerine bubbling yapan alternatifleri düşünmeniz gerekebilir. mouseenter’ın bubbling yapmadığı bilgisi için: MDN mouseenter.


addEventListener için seçenekler: once, capture, passive ne zaman düşünülür?

MDN addEventListener sayfasında, üçüncü parametre olarak options (veya boolean capture) kullanabileceğiniz belirtilir. Başlangıç seviyesinde her zaman şart değildir; ama bazı senaryolarda faydalıdır:

  • once: true — dinleyici ilk çalışmadan sonra otomatik kaldırılsın istiyorsanız.
  • capture: true — olay yakalama (capturing) fazında dinlemek istiyorsanız. (Not: Aynı dinleyicinin “aynı” sayılması için capture değerinin de aynı olması beklenir.)
  • passive: true — özellikle scroll/touch performansında anlam kazanır; ancak her olay türü için uygun değildir.

Yaygın hatalar ve hızlı çözümler

1) DOM yüklenmeden element seçmek

Script’iniz DOM oluşmadan çalışırsa querySelector null dönebilir. Çözüm: script’i sayfanın en altına almak veya DOMContentLoaded ile beklemek.

2) Anonim fonksiyonla ekleyip kaldıramamak

removeEventListener aynı fonksiyon referansını ister. Bu yüzden kaldırmanız gereken durumlarda handler’ı değişkende tutun.

3) Hover ile kritik işlemleri bağlamak

Hover, touch cihazlarda farklı çalışabilir. Kritik bir aksiyonu yalnızca hover’a bağlamayın; en azından klavye ile erişimi de test edin.

4) Submit yerine sadece button click dinlemek

Enter tuşuyla gönderimi kaçırabilirsiniz. Formu form üzerinde submit ile yakalamak daha kapsayıcı olur. Kaynak: MDN submit event.


Mini uygulama fikri: Üç olayı tek sayfada birleştirin

Öğrenmeyi pekiştirmek için küçük bir demo yapın:

  • Bir “Sepete ekle” butonu: click ile sayaç artırın.
  • Ürün kartında tooltip: mouseenter/mouseleave ile göster/gizle.
  • E-posta kayıt formu: submit ile FormData okuyun, preventDefault ile sayfa yenilenmesini engelleyin.

Sonuç

Click, hover ve submit; web arayüzlerinin omurgası sayılabilecek üç etkileşim türüdür. addEventListener ile doğru elementte doğru olayı dinlemek, kullanıcı davranışlarını daha öngörülebilir şekilde yönetmenize yardımcı olur. click için basit etkileşimleri, mouseenter ile daha kontrollü hover senaryolarını ve submit ile Enter tuşunu da kapsayan form akışlarını kurarak sağlam bir temel oluşturabilirsiniz.

Resmi davranış tanımları ve ek örnekler için: addEventListener, click, mouseenter, submit.