Kısa cevap: element.addEventListener("click", dinleyici, secenekler) çağrısı, belirtilen türde bir olay o hedefe ulaştığında çalışacak bir fonksiyon kaydeder. Üçüncü parametre bir nesne olabilir ve dört seçenek alır: dinleyiciyi bir kez çalıştırıp otomatik silen once, tarayıcıya preventDefault() çağrılmayacağını söyleyen passive, dinleyiciyi yakalama evresine taşıyan capture ve bir AbortController ile dinleyiciyi topluca kaldırmaya yarayan signal. Kaydı silmek için removeEventListener() aynı tür, aynı fonksiyon referansı ve aynı capture değeriyle çağrılır.

Bu yazıda MDN referansına dayanarak addEventListener() imzasını, her seçeneğin varsayılan değerini, dinleyici içinde this değerinin neye bağlandığını, olay nesnesinin en sık kullanılan özelliklerini ve bellek ile kaldırma konusundaki tipik hataları anlatıyoruz. Olayın DOM ağacında nasıl yayıldığı, stopPropagation() ve olay delegasyonu ayrı bir konudur; onlar için event bubbling, capturing ve delegation yazısına bakın. Bu sayfa serinin bir parçasıdır, tüm konuların listesi Web API öğrenme rehberi sayfasında.

addEventListener ne yapar?

addEventListener(), EventTarget arayüzünün bir yöntemidir. MDN bu arayüzü olay alabilen ve dinleyici barındırabilen her nesnenin ortak tabanı olarak tanımlar. En bilinen hedefler Element, Document ve Window olsa da IDBRequest ya da AudioContext gibi DOM dışı nesneler de olay hedefidir. Bu yüzden aynı yöntemi bir düğmede, belgenin kendisinde, pencerede veya kendi yazdığınız bir sınıfta aynı şekilde kullanırsınız.

Yöntem, verdiğiniz fonksiyonu ya da handleEvent() yöntemi olan bir nesneyi, o hedefin o olay türüne ait dinleyici listesine ekler. Önemli bir ayrıntı: Aynı fonksiyon aynı hedefe, aynı tür ve aynı capture değeriyle zaten eklenmişse ikinci kez eklenmez. Buna karşılık her çağrıda yeniden yazılan anonim fonksiyonlar, kaynak kodu birebir aynı olsa bile, her seferinde yeni bir fonksiyon nesnesi olduğu için ayrı ayrı eklenir. MDN bunun döngü içinde bile geçerli olduğunu özellikle vurgular.

javascript
const hedef = new EventTarget();
let sayac = 0;
function say() {
  sayac++;
}
hedef.addEventListener("ping", say);
hedef.addEventListener("ping", say); // aynı referans, ikinci kez eklenmez
hedef.dispatchEvent(new Event("ping"));
console.log(sayac); // 1

let anonim = 0;
for (let i = 0; i < 2; i++) {
  hedef.addEventListener("pong", () => {
    anonim++;
  });
}
hedef.dispatchEvent(new Event("pong"));
console.log(anonim); // 2

Yukarıdaki örnek yalnızca standart EventTarget ve Event sınıflarını kullandığı için tarayıcıda da aynı şekilde çalışır; biz onu Node.js 24.4.0 ile çalıştırıp çıktıları doğruladık. Kendi olaylarınızı üretmek ve göndermek konusu CustomEvent ve EventTarget yazısında ayrıntılı işleniyor.

Sözdizimi ve parametreler

Sözdizimi ve parametreler: type, listener, options, useCapture

MDN üç çağrı biçimi listeler: yalnızca tür ve dinleyici, tür, dinleyici ve seçenek nesnesi, ya da eski stilde tür, dinleyici ve tek bir boolean değer olan useCapture. Yöntem bir şey döndürmez, dönüş değeri undefined olur.

javascript
addEventListener(type, listener)
addEventListener(type, listener, options)
addEventListener(type, listener, useCapture)
  • type: Dinlenecek olay türünün adı. Büyük ve küçük harfe duyarlıdır; "click" ile "Click" farklı türlerdir.
  • listener: Olay geldiğinde çağrılacak fonksiyon, handleEvent() yöntemi olan bir nesne ya da null.
  • options: capture, once, passive ve signal alanlarını içerebilen nesne.
  • useCapture: Seçenek nesnesi yerine verilen boolean. Belirtilmezse false kabul edilir.
SeçenekVarsayılanNe yapar
capturefalseDinleyiciyi yakalama evresinde, hedefin altındaki öğelerden önce çalıştırır
oncefalseDinleyici en fazla bir kez çağrılır, çağrıldığında otomatik olarak kaldırılır
passivefalse (bazı istisnalarla true)Dinleyicinin preventDefault çağırmayacağını bildirir, çağrılırsa etkisiz kalır
signalyokBağlı AbortController üzerinde abort çağrıldığında dinleyici kaldırılır

once: tek seferlik dinleyici

once: true verildiğinde dinleyici ilk çağrısından hemen sonra listeden çıkarılır. Bir karşılama mesajını yalnızca ilk tıklamada göstermek, bir kurulum adımını bir kez tetiklemek gibi durumlar için uygundur. Elle removeEventListener() çağırmanıza gerek kalmaz. MDN örneklerinden biri once ile passive seçeneklerini aynı nesnede birlikte kullanır; seçenekler birbirini dışlamaz, istediğiniz kadarını birleştirebilirsiniz.

signal: AbortController ile toplu kaldırma

signal seçeneğine bir AbortSignal verirseniz, o sinyalin sahibi olan AbortController üzerinde abort() çağrıldığı anda dinleyici kaldırılır. Bu yöntemin en büyük avantajı, aynı sinyali birden fazla addEventListener() çağrısına verip hepsini tek satırla temizleyebilmenizdir. Böylece her dinleyici için fonksiyon referansı saklamak zorunda kalmazsınız ve anonim fonksiyon kullansanız bile kaldırma mümkün olur.

javascript
const hedef = new EventTarget();
let tekSefer = 0;
hedef.addEventListener("kaydet", () => tekSefer++, { once: true });
hedef.dispatchEvent(new Event("kaydet"));
hedef.dispatchEvent(new Event("kaydet"));
console.log(tekSefer); // 1

const controller = new AbortController();
let a = 0;
let b = 0;
hedef.addEventListener("tik", () => a++, { signal: controller.signal });
hedef.addEventListener("tak", () => b++, { signal: controller.signal });
hedef.dispatchEvent(new Event("tik"));
hedef.dispatchEvent(new Event("tak"));
controller.abort(); // bu sinyalle eklenen tüm dinleyiciler kaldırılır
hedef.dispatchEvent(new Event("tik"));
hedef.dispatchEvent(new Event("tak"));
console.log(a, b); // 1 1

Sayfada bu desen genellikle bir bileşenin ya da bir modal pencerenin ömrüyle birlikte kullanılır: Bileşen açılırken bir AbortController oluşturursunuz, belgeye ve pencereye eklediğiniz bütün dinleyicilere aynı sinyali verirsiniz, bileşen kapanırken tek bir abort() çağrısıyla hepsini sökersiniz. Aynı sinyal fetch() isteklerini iptal etmek için de kullanılabilir; ağ tarafı için Fetch API yazısına bakın.

passive: kaydırma performansı

passive: kaydırma performansı

Bazı olayların bir varsayılan eylemi vardır; örneğin wheel olayı varsayılan olarak kapsayıcıyı kaydırır. Tarayıcı, dinleyicinin preventDefault() çağırıp çağırmayacağını önceden bilemediği için kaydırmayı başlatmadan önce dinleyicinin bitmesini beklemek zorunda kalır. Dinleyici uzun sürerse kullanıcı kaydırmada belirgin bir gecikme hisseder. passive: true ile dinleyici, varsayılan eylemi iptal etmeyeceğini baştan bildirir ve tarayıcı beklemeden kaydırmaya başlar. Pasif bir dinleyici yine de preventDefault() çağırırsa hiçbir şey olmaz, tarayıcı konsola bir uyarı yazabilir.

Burada dikkat edilmesi gereken bir istisna var. Spesifikasyon, eski kodlarda da kaydırma kazancı sağlamak için wheel, mousewheel, touchstart ve touchmove olaylarında, hedef Window, Document, document.documentElement ya da document.body olduğunda passive varsayılanını true kabul eder. Bu hedeflerde kaydırmayı gerçekten engellemek istiyorsanız passive: false değerini açıkça yazmanız gerekir; varsayılana güvenmek bu durumda işe yaramaz. Sıradan scroll olayı ise iptal edilemediği için bu ayar onu etkilemez.

javascript
const container = document.querySelector("#container");

// Kaydırmayı engellemeyecek, tarayıcı beklemeden kaydırır
container.addEventListener("wheel", kaydirmaIzle, { passive: true });

// Belge düzeyinde kaydırmayı gerçekten engellemek için açıkça false yazılır
document.addEventListener(
  "touchmove",
  (event) => {
    event.preventDefault();
  },
  { passive: false },
);

function kaydirmaIzle(event) {
  // yalnızca okuma yapar, preventDefault çağırmaz
}

capture: yakalama evresinde dinlemek

capture: true verilen dinleyici, olay hedefe inerken yakalama evresinde çalışır; yani iç öğedeki dinleyicilerden önce tetiklenir. Varsayılan durumda dinleyiciler kabarcıklanma evresinde çağrılır. Dinleyiciyi doğrudan olayın hedefine eklediyseniz, olay o noktada hedef evresindedir. Bu evrelerin sırası, hangi olayların kabarcıklandığı ve yayılmanın nasıl durdurulduğu olay yayılımı yazısının konusudur; burada yalnızca şunu bilmeniz yeterli: capture değeri, aynı fonksiyonun aynı türe iki ayrı kayıt olarak eklenmesine izin veren tek seçenektir ve kaldırma sırasında da bu değerin eşleşmesi gerekir.

removeEventListener ile kaldırma kuralları

removeEventListener() bir kaydı tür, dinleyici fonksiyonu ve capture bayrağının birleşimine bakarak bulur. Hiçbir kayıtla eşleşmeyen bir çağrı hata vermez, sessizce hiçbir şey yapmaz. Bu sessizlik en sık yapılan hatanın da kaynağıdır: Kaldırma çalışmadığında tarayıcı sizi uyarmaz, dinleyici çalışmaya devam eder. MDN eşleşme kuralını açıkça yazar: once, passive ve signal eşleşmeyi etkilemez, yalnızca capture değeri aynı olmalıdır.

javascript
const hedef = new EventTarget();
let n = 0;
function dinle() {
  n++;
}
hedef.addEventListener("x", dinle, true);
hedef.removeEventListener("x", dinle, false); // eşleşmez
hedef.dispatchEvent(new Event("x"));
console.log(n); // 1
hedef.removeEventListener("x", dinle, { capture: true }); // eşleşir
hedef.dispatchEvent(new Event("x"));
console.log(n); // 1

hedef.addEventListener("y", dinle, { passive: true });
hedef.removeEventListener("y", dinle, { passive: false }); // yine eşleşir
hedef.dispatchEvent(new Event("y"));
console.log(n); // 1

MDN ayrıca bazı tarayıcı sürümlerinin bu konuda geçmişte tutarsız davrandığını not eder ve özel bir nedeniniz yoksa kaldırırken eklerken kullandığınız değerlerin aynısını vermenizi önerir. Bir dinleyici hem capture açık hem kapalı olarak iki kez eklendiyse, ikisini ayrı ayrı kaldırmanız gerekir. Bir olay işlenirken aynı hedefteki başka bir dinleyiciyi kaldırırsanız, kaldırılan dinleyici o olay için artık çağrılmaz.

Dinleyici içinde this ve currentTarget

addEventListener() ile normal bir function eklediğinizde, fonksiyonun içindeki this değeri dinleyicinin bağlı olduğu öğeyi gösterir ve olay nesnesinin currentTarget özelliğiyle aynıdır. Ok fonksiyonlarının kendi this bağlamı yoktur, dışarıdaki bağlamı kullanırlar; bu yüzden ok fonksiyonu içinde this öğeyi göstermez. Hangisini kullanırsanız kullanın, güvenli yol event.currentTarget okumaktır. Dilin this kuralları için JavaScript öğrenme rehberine bakabilirsiniz.

Bir sınıf içinde dinleyici yazarken sınıf örneğine erişmeniz gerekir. MDN bunun için üç yol gösterir: Yöntemi yapıcıda bind(this) ile bağlamak, yöntemi bir ok fonksiyonu içinden çağırmak ya da nesnenin kendisini dinleyici olarak verip handleEvent() yöntemini tanımlamak. Son yöntemde this doğrudan sınıf örneğidir ve tek bir yöntem birden çok olay türünü event.type ile ayırt edebilir.

javascript
class Sepet {
  adet = 0;
  constructor(hedef) {
    // dinleyici olarak nesnenin kendisi veriliyor
    hedef.addEventListener("ekle", this);
    hedef.addEventListener("cikar", this);
  }
  handleEvent(event) {
    switch (event.type) {
      case "ekle":
        this.adet++;
        break;
      case "cikar":
        this.adet--;
        break;
    }
  }
}

const hedef = new EventTarget();
const sepet = new Sepet(hedef);
hedef.dispatchEvent(new Event("ekle"));
hedef.dispatchEvent(new Event("ekle"));
hedef.dispatchEvent(new Event("cikar"));
console.log(sepet.adet); // 1

hedef.addEventListener("kontrol", function (e) {
  console.log(this === e.currentTarget); // true
});
hedef.dispatchEvent(new Event("kontrol"));

bind() kullandığınızda dikkat etmeniz gereken nokta, bind() her çağrıldığında yeni bir fonksiyon döndürmesidir. Kaldırma yapacaksanız bağlanmış fonksiyonu bir kez oluşturup saklamalı, hem ekleme hem kaldırmada o referansı kullanmalısınız. MDN örneğinde bu yüzden yapıcıda this.onclick2 = this.onclick2.bind(this) satırı bulunur.

Olay nesnesinde en çok kullanılan alanlar

Olay nesnesinde en çok kullanılan alanlar: type, target, currentTarget, isTrusted

Dinleyici tek bir argüman alır: Event arayüzünden ya da ondan türeyen bir arayüzden (örneğin MouseEvent, KeyboardEvent, PointerEvent, SubmitEvent) bir nesne. Dönüş değeri yok sayılır. Dinleyiciye veri taşımak ya da ondan veri almak için parametre ve dönüş değeri yerine kapanışları (closure) kullanırsınız; dış kapsamdaki değişkenler dinleyiciden okunabilir ve değiştirilebilir.

Özellik veya yöntemAnlamı
typeOlayın türü, örneğin click veya load
targetOlayın ilk gönderildiği nesne
currentTargetŞu anda çalışan dinleyicinin bağlı olduğu nesne, dinleyici dışında null
isTrustedOlay tarayıcı tarafından üretildiyse true, dispatchEvent ile gönderildiyse false
timeStampOlayın oluşturulduğu an, zaman kökeninden itibaren milisaniye
defaultPreventedpreventDefault çağrılıp olay iptal edildiyse true
preventDefault()İptal edilebilir olayın varsayılan eylemini engeller
stopPropagation()Olayın diğer öğelere yayılmasını durdurur

currentTarget ile ilgili sık atlanan bir kural var: Bu değer yalnızca dinleyici çalışırken doludur. Olay nesnesini bir değişkende saklayıp dinleyici bittikten sonra okursanız null görürsünüz. Asenkron bir işlemden sonra öğeye ihtiyaç duyacaksanız, öğeyi dinleyici içinde ayrı bir değişkene alın. isTrusted ise kullanıcı eylemlerinden ve tarayıcının kendi ürettiği olaylardan gelirse true, betikle gönderilen olaylarda false olur; MDN, HTMLElement.click() ile tetiklenen click olayında da false olduğunu belirtir.

javascript
const hedef = new EventTarget();
let sakla;
hedef.addEventListener("bak", (e) => {
  sakla = e;
  console.log(e.currentTarget === hedef); // true
});
hedef.dispatchEvent(new Event("bak"));
console.log(sakla.currentTarget); // null
console.log(sakla.type); // bak
console.log(sakla.isTrusted); // false
console.log(sakla.target === hedef); // true

onclick özelliği ile addEventListener farkı

MDN olay işleyici kaydetmek için iki önerilen yol tanımlar: onclick gibi bir onevent özelliğine fonksiyon atamak ya da addEventListener() kullanmak. Üçüncü yol olan HTML içine onclick="..." özniteliği yazmak ise önerilmez; işaretlemeyi şişirir ve hata ayıklamayı zorlaştırır. onevent özelliği ile HTML özniteliği aynı tek yuvayı paylaşır; bu yuvaya yeni bir fonksiyon atamak eskisinin yerine geçer. addEventListener() ile eklenenler ise bu yuvadan bağımsızdır, birbirini silmez.

Konuonclick özelliğiaddEventListener
Aynı olaya birden çok işleyiciHayır, son atanan geçerliEvet, eklendikleri sırayla çalışır
Yakalama evresiHayırEvet, capture ile
once, passive, signalYokVar
Kaldırmanull atanarakremoveEventListener veya abort
DOM dışı hedeflerYalnızca tanımlıysaHer EventTarget üzerinde

Sıralama konusunda da MDN bir kural verir: Aynı evre içinde dinleyiciler kaydedildikleri sırayla çalışır. onevent işleyicisi sıraya ilk kez null dışı bir değer aldığında girer; sonradan başka bir fonksiyon atamanız yalnızca çağrılacak fonksiyonu değiştirir, sıradaki yerini değiştirmez.

Uygulamalı mini örnek: tek seferlik bildirim ve temizlik

Aşağıdaki örnek bir bilgi kutusunu ele alır. Kapatma düğmesi yalnızca bir kez çalışmalıdır; kutu açıkken Escape tuşu ve sayfa genelindeki tıklamalar da dinlenir. Kutu kapandığında tüm dinleyiciler tek bir abort() ile kaldırılır. Bu kod tarayıcıya özgüdür, MDN sayfalarındaki API adlarına göre yazıldı ve çalıştırılmadı.

html
<div id="bilgi" role="status">
  Yeni sürüm yayında.
  <button type="button" id="kapat">Kapat</button>
</div>
javascript
const kutu = document.getElementById("bilgi");
const kapat = document.getElementById("kapat");
const controller = new AbortController();
const { signal } = controller;

function kutuyuKapat() {
  kutu.remove();
  controller.abort(); // aşağıdaki üç dinleyici birlikte kaldırılır
}

kapat.addEventListener("click", kutuyuKapat, { once: true, signal });

document.addEventListener(
  "keydown",
  (event) => {
    if (event.key === "Escape") {
      kutuyuKapat();
    }
  },
  { signal },
);

document.addEventListener(
  "click",
  (event) => {
    if (!kutu.contains(event.target)) {
      kutuyuKapat();
    }
  },
  { signal },
);

Klavye olayında event.key değerinin nasıl hesaplandığını ve neden keyCode yerine tercih edildiğini klavye olayları yazısında, fare ve dokunmatik girdiyi tek modelde ele almayı pointer events yazısında bulabilirsiniz.

Sık yapılan hatalar

  • Fonksiyonu çağırarak vermek: addEventListener("click", kaydet()) yazmak kaydet fonksiyonunu hemen çalıştırır ve dönüş değerini dinleyici olarak verir. Doğrusu kaydet referansını parantezsiz vermektir.
  • Tür adını yanlış yazmak: Tür büyük ve küçük harfe duyarlıdır ve on öneki almaz; "onclick" değil "click" yazılır. DOMContentLoaded gibi karışık harfli adlar da birebir yazılmalıdır.
  • Döngüde anonim dinleyici üretmek: MDN bellek bölümünde, döngünün her turunda yeni bir anonim fonksiyon oluşturmanın hem daha fazla bellek kullandığını hem de kaldırmayı imkânsız kıldığını gösterir. Tek bir adlandırılmış fonksiyon kullanın ya da olay delegasyonu ile tek dinleyiciye indirin.
  • capture değerini kaldırırken unutmak: true ile eklenen dinleyici false ile kaldırılamaz.
  • Pasif dinleyicide preventDefault beklemek: Belge düzeyindeki touchmove ve wheel dinleyicileri varsayılan olarak pasif olabilir; engelleme gerekiyorsa passive: false yazın.
  • Dinleyici içinde ağır iş yapmak: Uzun süren dinleyiciler kullanıcı etkileşimini geciktirir. Bunun sayfa hızı ölçümlerine etkisi için Core Web Vitals yazısına bakın.

Sık Sorulan Sorular

addEventListener aynı fonksiyonu iki kez ekler mi?

Hayır. Aynı tür, aynı fonksiyon referansı ve aynı capture değeriyle ikinci çağrı yok sayılır. capture değeri farklıysa iki ayrı kayıt oluşur. Her seferinde yeni yazılan anonim fonksiyonlar ise farklı nesneler olduğu için hepsi eklenir.

Dinleyiciye nasıl parametre gönderirim?

Dinleyici yalnızca olay nesnesini alır. Ek veri için dinleyiciyi bir ok fonksiyonu ya da normal fonksiyonla sarıp içeriden asıl fonksiyonu istediğiniz argümanlarla çağırırsınız veya dış kapsamdaki bir değişkeni kullanırsınız. Kaldırma gerekecekse bu sarmalayıcıyı bir değişkende saklayın ya da signal kullanın.

Dinleyicinin içinden başka bir dinleyici eklersem hemen çalışır mı?

MDN'ye göre bir olay işlenirken aynı hedefe eklenen yeni dinleyici o olayı almaz; ancak olay akışının daha sonraki bir aşamasında, örneğin kabarcıklanma evresinde başka bir öğede tetiklenebilir.

EventTarget.when() nedir, addEventListener yerine kullanılmalı mı?

when(), olay akışını bir Observable olarak döndüren yeni bir yöntemdir ve subscribe() ile kullanılır. MDN basit bir sayaç örneğinde ikisini neredeyse eşdeğer gösterir; farkı, olay akışlarını birleştirip dönüştürebilmenizdir. Tarayıcı desteği için MDN uyumluluk tablosuna bakın; yaygın kullanım için addEventListener() temel yöntem olmaya devam eder.

Marka adları sahiplerine aittir. Bu yazı bağımsız bir teknik rehberdir; adı geçen kuruluşlarla bayilik veya ortaklık ilişkisi bulunduğu anlamına gelmez.

İlgili içerikler: Web API öğrenme rehberi · Event bubbling, capturing ve delegation · CustomEvent ve EventTarget · Klavye olayları · Pointer events · Form gönderme ve FormData · JavaScript event loop