Kısa cevap: Kendi olayınızı üretmek için new CustomEvent("olayadi", { detail: veri }) ile bir olay nesnesi oluşturur, bunu hedef.dispatchEvent(olay) ile gönderirsiniz; dinleyiciler veriyi event.detail üzerinden okur. Hedef bir DOM öğesi olabileceği gibi EventTarget sınıfını genişleten kendi sınıfınız da olabilir. dispatchEvent() dinleyicileri eşzamanlı çalıştırır ve olay iptal edilebilir oluşturulup bir dinleyici preventDefault() çağırdıysa false döndürür. Özel olaylar varsayılan olarak kabarcıklanmaz ve iptal edilemez; bunları bubbles ve cancelable seçenekleriyle açarsınız.
Bu yazıda Event() ve CustomEvent() yapıcılarının seçeneklerini, dispatchEvent() davranışını, Event sınıfını genişleterek kendi olay türünüzü yazmayı ve EventTarget tabanlı küçük bir olay yayıcı sınıf kurmayı anlatıyoruz. CustomEvent, Event ve EventTarget tarayıcı ile Node.js'te aynı standarda göre bulunduğu için DOM gerektirmeyen örnekleri Node.js 24.4.0 ile çalıştırıp çıktılarını doğruladık; DOM öğesi kullanan örnekler MDN'ye göre yazıldı. Dinleyici ekleme seçenekleri için addEventListener yazısına, tüm seri için Web API öğrenme rehberi sayfasına bakın.
Sentetik olay nedir?
MDN, tarayıcının kendi arayüzlerinin ürettiği olayların yanında sizin de DOM olayları oluşturup gönderebileceğinizi söyler ve bunlara tarayıcının ürettiği olaylardan ayırmak için sentetik olay adını verir. Sentetik bir olay üç adımda doğar: Olay nesnesini bir yapıcıyla oluşturursunuz, hedefte o türü dinleyen bir dinleyici kaydedersiniz ve olayı dispatchEvent() ile hedefe gönderirsiniz. Olay normal kurallara tabidir: Yakalama ve isteğe bağlı kabarcıklanma evreleri aynen uygulanır.
Bu mekanizmanın en yaygın kullanımı, birbirinden habersiz kod parçalarını gevşek bağlı hâle getirmektir. Bir sepet bileşeni "ürün eklendi" olayını yayınlar; başlıktaki sayaç, analiz kodu ve bildirim bileşeni bu olayı dinler. Sepet onları tanımak zorunda kalmaz, yeni bir dinleyici eklemek sepet kodunu değiştirmeyi gerektirmez.
Event() yapıcısı ve seçenekleri

Veri taşımayan basit bir olay için new Event(type, options) yeterlidir. type olayın adıdır. Seçenek nesnesi üç boolean alır ve üçünün de varsayılanı false olur: bubbles olayın kabarcıklanıp kabarcıklanmayacağını, cancelable iptal edilip edilemeyeceğini, composed ise olayın shadow kökünün dışındaki dinleyicileri tetikleyip tetiklemeyeceğini belirler. MDN, olayın yalnızca belgeye değil herhangi bir öğeye gönderilebileceğini de hatırlatır.
| Seçenek | Varsayılan | true olduğunda |
|---|---|---|
| bubbles | false | Olay hedefin atalarına doğru kabarcıklanır |
| cancelable | false | preventDefault çağrısı olayı iptal eder, dispatchEvent false döner |
| composed | false | Olay shadow DOM sınırını geçerek normal DOM dinleyicilerine ulaşır |
| detail (yalnızca CustomEvent) | null | Dinleyiciye event.detail ile istenen değer taşınır |
CustomEvent ile veri taşımak
CustomEvent, Event arayüzünden türer ve tek ek özelliği detail alanıdır. new CustomEvent(type, options) çağrısında seçenek nesnesi Event() seçeneklerinin hepsini ve ek olarak detail alanını kabul eder. detail herhangi bir değer olabilir: sayı, dize, nesne, hatta fonksiyon. Verilmezse değeri null olur. MDN, olay adlarının büyük ve küçük harfe duyarlı olduğunu da özellikle belirtir.
const hedef = new EventTarget();
hedef.addEventListener("urunbulundu", (e) => {
console.log(e.detail.ad, e.detail.fiyat); // kalem 25
});
hedef.dispatchEvent(
new CustomEvent("urunbulundu", { detail: { ad: "kalem", fiyat: 25 } }),
);
const bos = new CustomEvent("bos");
console.log(bos.detail); // null
console.log(bos instanceof Event); // true
console.log(bos.bubbles, bos.cancelable); // false false
// Olay adı büyük ve küçük harfe duyarlıdır
let duydu = false;
hedef.addEventListener("Kaydedildi", () => (duydu = true));
hedef.dispatchEvent(new CustomEvent("kaydedildi"));
console.log(duydu); // falsedetail salt okunur bir özelliktir; olay oluşturulduktan sonra detail alanına başka bir değer atayamazsınız. Ancak değer bir nesneyse nesnenin kendisi kopyalanmaz, dinleyiciye aynı referans gider. Aşağıdaki denemede dinleyici nesnenin bir alanını değiştirir ve değişiklik olayı gönderen tarafta da görünür. Bu bazen bilinçli olarak dinleyicilerden geri bildirim almak için kullanılır, ama çoğu zaman beklenmedik yan etkilerin kaynağıdır; dinleyicilerin veriyi değiştirmesini istemiyorsanız bir kopya ya da dondurulmuş bir nesne gönderin.
const hedef = new EventTarget();
const veri = { adet: 1 };
hedef.addEventListener("guncelle", (e) => {
e.detail.adet = 99;
});
hedef.dispatchEvent(new CustomEvent("guncelle", { detail: veri }));
console.log(veri.adet); // 99dispatchEvent nasıl çalışır?
MDN'ye göre dispatchEvent() olayı hedefe gönderir ve etkilenen dinleyicileri uygun sırayla, eşzamanlı olarak çağırır. Tarayıcının kendi olayları olay döngüsüne bir görev olarak sıralanır; dispatchEvent() ise tüm dinleyiciler çalışıp bitmeden geri dönmez. Bu yüzden dispatchEvent() satırından sonraki kod, dinleyicilerin yaptığı değişiklikleri hemen görür. Olay döngüsünün ayrıntıları için JavaScript event loop yazısına bakabilirsiniz.
const sepet = new EventTarget();
console.log("1: gönderilmeden önce"); // 1: gönderilmeden önce
sepet.addEventListener("siparisvermeden", (e) => {
console.log("2: dinleyici çalıştı"); // 2: dinleyici çalıştı
if (e.detail.tutar > 1000) {
e.preventDefault();
}
});
const olay = new CustomEvent("siparisvermeden", {
detail: { tutar: 1500 },
cancelable: true,
});
const devam = sepet.dispatchEvent(olay);
console.log("3: gönderimden sonra", devam); // 3: gönderimden sonra falseDönüş değeri kuralı şöyledir: Olay iptal edilebilir ise ve olayı alan dinleyicilerden en az biri preventDefault() çağırdıysa false, diğer tüm durumlarda true. Bu sayede olayı gönderen kod "bir dinleyici bu işlemi veto etti mi" sorusunu sorabilir; örnekteki sepet, tutar sınırını aşan siparişi bir dinleyicinin durdurmasına izin verir. MDN'nin yerleşik olayları tetikleme örneği de aynı deseni kullanır: Bir onay kutusuna MouseEvent("click", { bubbles: true, cancelable: true }) gönderir ve dönüş değerinden bir işleyicinin olayı iptal edip etmediğini anlar.
İki ayrıntı daha bilinmeli. Birincisi, dispatchEvent() çağrıldığında olayın target özelliği o hedefe ayarlanır ve gönderilen olayın isTrusted değeri false olur. İkincisi, dinleyicilerden biri hata fırlatırsa bu hata dispatchEvent() çağıran koda iletilmez; MDN'nin uyarısına göre dinleyiciler iç içe bir çağrı yığınında çalışır, bitene kadar çağıranı bekletir ama istisnalar yakalanmamış istisna olarak raporlanır. Yani dispatchEvent() çağrısını try bloğuna almak dinleyici hatalarını yakalamaz. Olay türü başlatma sırasında belirtilmemişse dispatchEvent() InvalidStateError fırlatır.
EventTarget sınıfını genişletmek
EventTarget() yapıcısı yeni bir olay hedefi oluşturur. MDN bu yapıcının doğrudan çağrılmasının nadir olduğunu, çoğunlukla EventTarget sınıfını genişleten bir sınıfın yapıcısında super() ile kullanıldığını söyler. Böylece sınıfınız addEventListener(), removeEventListener() ve dispatchEvent() yöntemlerini hazır olarak kazanır ve ayrı bir olay kütüphanesine gerek kalmaz. Aşağıdaki örnek MDN'nin sayaç örneğinin DOM'suz bir uyarlamasıdır.
class Sayac extends EventTarget {
constructor(baslangic = 0) {
super();
this.deger = baslangic;
}
#degisti() {
this.dispatchEvent(new CustomEvent("degerdegisti", { detail: this.deger }));
}
artir() {
this.deger++;
this.#degisti();
}
azalt() {
this.deger--;
this.#degisti();
}
}
const sayac = new Sayac(10);
sayac.addEventListener("degerdegisti", (e) => {
console.log(`Yeni değer: ${e.detail}`);
});
sayac.artir(); // Yeni değer: 11
sayac.artir(); // Yeni değer: 12
sayac.azalt(); // Yeni değer: 11Sayfada bu sınıfı bir arayüze bağlamak için MDN örneğindeki gibi düğmelerin click olaylarında artir() ve azalt() çağrılır, degerdegisti dinleyicisi de ekrandaki sayıyı günceller. Mantık katmanı DOM'dan habersiz kalır; aynı sınıf testlerde ya da başka bir arayüzde değiştirilmeden kullanılabilir. Sınıf sözdizimi, # ile başlayan özel yöntemler ve super() dil konularıdır, JavaScript öğrenme rehberinde anlatılır.
Event sınıfını genişleterek olay türü yazmak

CustomEvent yerine doğrudan Event sınıfını da genişletebilirsiniz. MDN bunun yeniden kullanım, daha karmaşık özel veriler ya da olaya yöntem eklemek istediğinizde özellikle yararlı olduğunu belirtir. Aşağıdaki sınıf olay türünü sabitler ve veriyi salt okunur bir alıcı ile sunar; dinleyici veriyi detail yerine anlamlı bir özellik adıyla okur.
class DerlemeOlayi extends Event {
#sure;
constructor(sure) {
super("derleme");
this.#sure = sure;
}
get sure() {
return this.#sure;
}
}
const hedef = new EventTarget();
hedef.addEventListener("derleme", (e) => {
console.log(`Derleme ${e.sure} ms sürdü`); // Derleme 420 ms sürdü
console.log(e instanceof DerlemeOlayi); // true
});
hedef.dispatchEvent(new DerlemeOlayi(420));| Yaklaşım | Güçlü yanı | Ne zaman seçilir |
|---|---|---|
| new Event(type) | En basit, veri yok | Sadece bir şeyin olduğunu haber vermek |
| new CustomEvent(type, { detail }) | Ek sınıf yazmadan veri taşır | Çoğu uygulama içi bildirim |
| class X extends Event | Sabit tür, adlı ve salt okunur alanlar, yöntemler | Kütüphane veya çok yerde kullanılan olay türleri |
| class Y extends EventTarget | Kendi nesnenize olay yayma yeteneği | DOM dışı model, durum yöneticisi, servis sınıfı |
DOM içinde kabarcıklanan özel olaylar
Bir alt öğeden gönderilen özel olayı bir atada yakalamak için olayın bubbles: true ile oluşturulması gerekir. MDN'nin örneğinde bir textarea her input olayında kendi üzerinden awesome adlı bir olay gönderir; form öğesi bu olayı dinler ve detail içindeki fonksiyonla metnin güncel değerini okur. Örnek, dinleyicinin olay henüz hiç oluşturulmamışken de kaydedilebildiğini gösterir.
<form>
<textarea></textarea>
</form>const form = document.querySelector("form");
const textarea = document.querySelector("textarea");
// Form, alt öğeden kabarcıklanarak gelen özel olayı dinler
form.addEventListener("awesome", (e) => console.log(e.detail.text()));
textarea.addEventListener("input", function () {
// Olay her girişte oluşturulup gönderilir, bubbles açık
this.dispatchEvent(
new CustomEvent("awesome", {
bubbles: true,
detail: { text: () => textarea.value },
}),
);
});Bir Web Component içinden dışarıya olay yayıyorsanız, shadow kökünü geçebilmesi için composed: true değerini de vermeniz gerekir; aksi halde shadow kökü olayın son durağı olur. Bu konu Web Components yazısında, evrelerin kendisi olay yayılımı yazısında ayrıntılı işleniyor.
Uygulamalı mini örnek: sepet olayları
Aşağıdaki örnek, bir alışveriş sepeti modelini EventTarget üzerine kurar. Model iki olay yayınlar: Ürün eklenmeden önce iptal edilebilir bir ekleniyor olayı ve ekleme tamamlanınca bilgi amaçlı bir eklendi olayı. Stok kontrolü yapan bir dinleyici ilk olayı veto edebilir. Kod yalnızca standart sınıfları kullanır ve Node.js 24.4.0 ile çalıştırıldı.
class Sepet extends EventTarget {
#urunler = [];
ekle(urun) {
const izin = this.dispatchEvent(
new CustomEvent("ekleniyor", { detail: urun, cancelable: true }),
);
if (!izin) {
return false;
}
this.#urunler.push(urun);
this.dispatchEvent(new CustomEvent("eklendi", { detail: this.#urunler.length }));
return true;
}
}
const sepet = new Sepet();
const stok = { kalem: 5, defter: 0 };
sepet.addEventListener("ekleniyor", (e) => {
if (stok[e.detail] === 0) {
e.preventDefault(); // stokta yoksa eklemeyi engelle
}
});
sepet.addEventListener("eklendi", (e) => {
console.log(`Sepette ${e.detail} ürün var`); // Sepette 1 ürün var
});
console.log(sepet.ekle("kalem")); // true
console.log(sepet.ekle("defter")); // falseÇalıştırıldığında ilk ekle() çağrısı önce "Sepette 1 ürün var" satırını yazar, ardından true döner; ikinci çağrı stok dinleyicisi tarafından veto edildiği için eklendi olayı hiç gönderilmez ve false döner. Olay adları için tutarlı bir kural seçmek, örneğin tümü küçük harf, büyük küçük harf hatalarını baştan önler.
Bir olayı Promise ile beklemek ve dinleyicileri temizlemek
Özel olaylar çoğu zaman "bir şey hazır olduğunda devam et" türü akışlarda kullanılır. once: true seçeneğiyle eklenen bir dinleyiciyi bir Promise içine sararsanız, olay ilk geldiğinde Promise çözülür ve dinleyici kendiliğinden kaldırılır. Aşağıdaki örnek, gönderimin eşzamanlı olmasıyla Promise geri çağrılarının daha sonra çalışması arasındaki farkı da gösterir: dispatchEvent() döndükten sonraki satır, then() içindeki satırdan önce yazılır. Örnek Node.js 24.4.0 ile çalıştırıldı.
function olayiBekle(hedef, tur) {
return new Promise((resolve) => {
hedef.addEventListener(tur, resolve, { once: true });
});
}
const baglanti = new EventTarget();
olayiBekle(baglanti, "hazir").then((e) => {
console.log(`Durum: ${e.detail}`); // Durum: bağlandı (ikinci satır)
});
baglanti.dispatchEvent(new CustomEvent("hazir", { detail: "bağlandı" }));
console.log("dispatchEvent döndü"); // dispatchEvent döndü (ilk satır)Dinleyici resolve fonksiyonunu eşzamanlı olarak çağırır, ama then() geri çağrısı bir mikro görev olarak sıraya girer ve o anki kod bitince çalışır; mikro görevlerin sırası için event loop yazısına bakın. Uzun ömürlü bir olay yayıcıya, örneğin uygulama genelinde paylaşılan bir EventTarget nesnesine, sayfanın bir bölümünden dinleyici ekliyorsanız o bölüm kaldırıldığında dinleyicileri de kaldırmanız gerekir; aksi halde dinleyiciler ve kapanışlarında tuttukları veriler bellekte kalır. Bunun en kolay yolu, aynı AbortSignal değerini bütün addEventListener() çağrılarına verip bölüm kapanırken tek bir abort() çağırmaktır; desen addEventListener yazısındaki signal bölümünde örnekleniyor.
Sık yapılan hatalar

- bubbles vermeyi unutmak: Alt öğeden gönderilen olay ebeveyne ulaşmaz.
- cancelable vermeden preventDefault beklemek: İptal etkisiz kalır,
dispatchEvent()yinetruedöner. - dispatchEvent çağrısını try ile sarmak: Dinleyici hataları çağırana iletilmez, yakalanmamış istisna olarak raporlanır.
- detail içinde paylaşılan nesne göndermek: Dinleyiciler aynı referansı değiştirebilir.
- initCustomEvent kullanmak: MDN bu yöntemi kullanımdan kalkmış olarak işaretler;
CustomEvent()yapıcısını kullanın. - Yerleşik olay adlarını yeniden kullanmak: Kendi olayınıza
clickya dasubmitgibi bir ad vermek, o türü dinleyen başka kodları da tetikler; özgün bir ad seçin. - Sekmeler ya da pencereler arası iletişim için CustomEvent kullanmak:
dispatchEvent()olayı yalnızca çağrıldığı hedefe ve onun yayılma yoluna gönderir. Farklı pencere ve iframe'ler için postMessage yazısına bakın.
Sık Sorulan Sorular
CustomEvent ile Event arasındaki tek fark detail mi?
Evet. CustomEvent, Event özelliklerinin ve yöntemlerinin hepsini devralır ve buna yalnızca detail özelliğini ekler. Eski initCustomEvent() yöntemi de vardır ama kullanımdan kalkmıştır.
dispatchEvent asenkron mu çalışır?
Hayır. Dinleyicilerin tamamı dispatchEvent() geri dönmeden önce eşzamanlı olarak çağrılır. Tarayıcının kendi ürettiği olaylar ise olay döngüsüne görev olarak eklenir.
Gönderdiğim olayı kullanıcı eyleminden ayırt edebilir miyim?
event.isTrusted özelliği tarayıcının ürettiği olaylarda true, dispatchEvent() ile gönderilen olaylarda false olur. Kullanıcı etkinliği gerektiren API'ler için ayrıntı güvenli bağlam ve kullanıcı etkinliği yazısında.
Firefox eklentisinden sayfaya CustomEvent göndermek neden hata veriyor?
MDN, bir web eklentisinin içerik betiği ile sayfa betiği arasında iletişim kurarken dize olmayan bir detail kullanıldığında Firefox'ta "Permission denied to access property" hatası alınabileceğini ve nesneyi kopyalayarak bunun aşılabileceğini belirtir.
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 · addEventListener kullanımı · Olay yayılımı ve delegasyon · Web Components · window.postMessage ve BroadcastChannel · JavaScript event loop