---
title: 'Olay Devri'
source: 'https://academia.sh/tr/kurslar/tarayici-platformu/olay-devri'
course: 'Tarayıcı ve Web Platformu'
language: tr
updated: '2026-08-17T18:09:12+00:00'
license: 'CC BY-SA 4.0'
---

# Olay Devri

Ata düğüme bağlanan tek dinleyiciyle çok sayıda ögenin olaylarını karşılamak; hedefi geçerli hedeften ayırt etme, en yakın eşleşen atayı bulma, kimliği işaretlemede taşıma ve devrin sınırları.

Önceki ders olayın hedefe ulaşmadan önce ve ulaştıktan sonra atalarından geçtiğini
gösterdi. Bu yolculuk bir yük değil bir olanaktır: bir atanın dinleyicisi, o atanın
altındaki bütün ögelerin olaylarını görür. Ölçüm tablosunun her satırına ayrı dinleyici
bağlamak yerine tabloya tek dinleyici bağlanabilir.

Bu tekniğin adı **olay devri (event delegation)** dir. Devrin işe yaraması tek bir
ayrıma dayanır: dinleyici ata düğüme bağlıdır, ama olayı üreten öge hedeftir. Dinleyici,
kendisine gelen olayın hangi alt ögeden geldiğini çözümlemek zorundadır.

## İki Sorun

Doğrudan bağlama iki ayrı sorun üretir ve ikincisi daha sinsidir.

Birincisi **kayıt sayısıdır**. Yüz satırlık bir tabloda satır başına bir dinleyici, yüz
kayıt demektir. Her kayıt bellekte bir giriş tutar ve her kayıt, ögenin ağaçtan
çıkarılmasıyla temizlenmesi gereken bir bağ kurar.

İkincisi **yaşam süresidir**. Dinleyici bir ögeye kaydedildiği anda var olan ögeye
bağlanır. Sayfa yüklendikten sonra eklenen bir satır, kayıt sırasında ortada olmadığı
için dinleyicisiz kalır. Bu, DOM API'si dersindeki canlı koleksiyon ile durağan liste
ayrımının davranış tarafındaki karşılığıdır: durağan bir anda kurulan bağ, sonradan gelen
düğümleri kapsamaz.

Devir iki sorunu birden çözer. Dinleyici, satırlardan daha uzun yaşayan bir ataya —
tabloya — bağlanır. Satırlar gelip gitse de tablo yerinde durur.

## Hedefi Çözümlemek

Devredilen dinleyicinin ilk işi, gelen olayın ilgilendiği bir ögeden gelip gelmediğine
karar vermektir. Burada iki işlem birbirine karıştırılır.

**Eşleşme sınaması** bir ögenin verilen seçiciye uyup uymadığını söyler; yalnız o ögeye
bakar. **En yakın ata araması** hedeften başlar, ata zincirini yukarı yürür ve seçiciye
uyan ilk düğümü döndürür.

Fark, tıklamanın nereye düştüğünde ortaya çıkar. Silme düğmesinin içinde bir simge ögesi
varsa, kullanıcı düğmeye bastığında hedef düğme değil simgedir. Yalnız hedefi sınayan kod
bu tıklamayı kaçırır.

```js
// enyakin.mjs — hedeften sinira dogru yuruyen es arama
const oge = (ad, ozn = {}, ...cocuklar) => {
  const d = { ad, ozn, cocuklar, ebeveyn: null };
  for (const c of cocuklar) c.ebeveyn = d;
  return d;
};

const adlandir = (d) =>
  d.ad + (d.ozn.id ? "#" + d.ozn.id : "") + (d.ozn.class ? "." + d.ozn.class : "");

const esler = (d, secici) => {
  if (secici.startsWith(".")) return (d.ozn.class ?? "").split(/\s+/).includes(secici.slice(1));
  if (secici.startsWith("#")) return d.ozn.id === secici.slice(1);
  return d.ad === secici;
};

// Hedeften baslar, sinira kadar yukari yurur; sinir da denenir, otesi denenmez.
const enYakin = (hedef, secici, sinir) => {
  for (let d = hedef; d; d = d.ebeveyn) {
    if (esler(d, secici)) return d;
    if (d === sinir) break;
  }
  return null;
};

const simge = oge("span", { class: "simge" });
const dugme = oge("button", { class: "sil" }, simge);
const satir = oge("tr", { class: "olcum", "data-kod": "T-04" }, oge("td", {}), oge("td", {}, dugme));
const tablo = oge("table", { id: "gunluk" }, satir);

console.log("hedef         :", adlandir(simge));
console.log("gecerli hedef :", adlandir(tablo));
console.log("esler(hedef, '.sil')   :", esler(simge, ".sil"));
console.log("enYakin('.sil')        :", adlandir(enYakin(simge, ".sil", tablo)));
console.log("enYakin('tr')          :", adlandir(enYakin(simge, "tr", tablo)), enYakin(simge, "tr", tablo).ozn["data-kod"]);
console.log("enYakin('body')        :", enYakin(simge, "body", tablo));
```

```
hedef         : span.simge
gecerli hedef : table#gunluk
esler(hedef, '.sil')   : false
enYakin('.sil')        : button.sil
enYakin('tr')          : tr.olcum T-04
enYakin('body')        : null
```

Üçüncü satır sorunu gösterir: hedefin kendisi silme düğmesi değildir. Dördüncü satır
çözümü gösterir: yukarı yürüyen arama düğmeyi bulur. Beşinci satır bir adım daha gider ve
düğmenin ait olduğu satırı bulur; ölçümün kodu oradan okunur.

Son satır **sınır** kavramını gösterir. Arama geçerli hedefte durur. Bu sınır olmadan
arama belgenin köküne kadar çıkar ve dinleyicinin kapsamı dışındaki bir düğümü döndürerek
başka bir bileşenin ögesi üzerinde işlem yapılmasına yol açabilir. Tarayıcı API'sinde bu
işlemlerin adı `matches` ve `closest` tir; `closest` sınır almadığı için sonucun geçerli
hedefin altında kaldığı ayrıca sınanır.

## Devri Kurmak

İki yaklaşımın davranış farkı, sonradan eklenen bir satırda görünür.

```js
// devir.mjs — her ogeye baglama ile tek dinleyiciyle devir arasindaki fark
const oge = (ad, ozn = {}, ...cocuklar) => {
  const d = { ad, ozn, cocuklar, ebeveyn: null, dinleyiciler: [] };
  for (const c of cocuklar) c.ebeveyn = d;
  return d;
};
const esler = (d, s) =>
  s.startsWith(".") ? (d.ozn.class ?? "").split(/\s+/).includes(s.slice(1)) : d.ad === s;
const enYakin = (hedef, s, sinir) => {
  for (let d = hedef; d; d = d.ebeveyn) { if (esler(d, s)) return d; if (d === sinir) break; }
  return null;
};
// Kabarcik evresi: hedeften koke dogru her dugumun dinleyicileri cagrilir.
const tikla = (hedef) => {
  for (let d = hedef; d; d = d.ebeveyn)
    for (const dinleyici of d.dinleyiciler) dinleyici({ hedef, gecerliHedef: d });
};
const dinleyiciSayisi = (kok) =>
  kok.dinleyiciler.length + kok.cocuklar.reduce((t, c) => t + dinleyiciSayisi(c), 0);

const satirKur = (kod) =>
  oge("tr", { class: "olcum", "data-kod": kod }, oge("td", {}), oge("td", {}, oge("button", { class: "sil" })));
const tabloKur = () =>
  oge("table", { id: "gunluk" },
    oge("tr", { class: "baslik" }, oge("th", {})),
    satirKur("T-01"), satirKur("T-02"), satirKur("T-03"));

const dugmeler = (kok) =>
  [kok, ...kok.cocuklar.flatMap((c) => dugmeler(c))].filter((d) => esler(d, ".sil"));

let silinen = [];
const sil = (satir) => silinen.push(satir.ozn["data-kod"]);

// Birinci yol: her silme dugmesine ayri dinleyici.
const a = tabloKur();
for (const b of dugmeler(a)) b.dinleyiciler.push(({ gecerliHedef }) => sil(gecerliHedef.ebeveyn.ebeveyn));
console.log("dogrudan baglama, dinleyici:", dinleyiciSayisi(a));
a.cocuklar.push(Object.assign(satirKur("T-04"), { ebeveyn: a })); // sonradan gelen satir
silinen = []; tikla(dugmeler(a)[0]); console.log("  T-01 dugmesi:", silinen.length ? silinen : "islem yok");
silinen = []; tikla(dugmeler(a)[3]); console.log("  T-04 dugmesi:", silinen.length ? silinen : "islem yok");

// Ikinci yol: tabloya tek dinleyici; hedef her cagrida yeniden cozumlenir.
const b = tabloKur();
b.dinleyiciler.push(({ hedef, gecerliHedef }) => {
  const dugme = enYakin(hedef, ".sil", gecerliHedef);
  if (dugme === null) return;
  sil(enYakin(dugme, "tr", gecerliHedef));
});
console.log("devir, dinleyici          :", dinleyiciSayisi(b));
b.cocuklar.push(Object.assign(satirKur("T-04"), { ebeveyn: b }));
silinen = []; tikla(dugmeler(b)[0]); console.log("  T-01 dugmesi:", silinen.length ? silinen : "islem yok");
silinen = []; tikla(dugmeler(b)[3]); console.log("  T-04 dugmesi:", silinen.length ? silinen : "islem yok");
silinen = []; tikla(b.cocuklar[0].cocuklar[0]); console.log("  baslik hucresi:", silinen.length ? silinen : "islem yok");
```

```
dogrudan baglama, dinleyici: 3
  T-01 dugmesi: [ 'T-01' ]
  T-04 dugmesi: islem yok
devir, dinleyici          : 1
  T-01 dugmesi: [ 'T-01' ]
  T-04 dugmesi: [ 'T-04' ]
  baslik hucresi: islem yok
```

Doğrudan bağlamada dinleyici sayısı satır sayısıyla birlikte artar ve sonradan eklenen
satır sessiz kalır. Devirde tek kayıt vardır ve sonradan eklenen satır ilk satırdan farksız
çalışır. Farkın kaynağı zamanlamadır: doğrudan bağlamada eşleşme **kayıt anında**, devirde
**olay anında** kurulur.

Son satır devrin ikinci gereğini gösterir. Tabloya bağlı dinleyici, tablonun altındaki her
tıklamayı görür — başlık hücresini, boş bir hücreyi, satır arasındaki boşluğu. Eşleşme
bulunamadığında dinleyici hiçbir şey yapmadan döner. Bu erken çıkış, devredilen her
dinleyicinin ilk satırıdır.

## Kimliği İşaretlemede Taşımak

Devredilen dinleyici, olayın hangi kayda ait olduğunu bilmek zorundadır. Bu bilgiyi bir
dizide sıra numarasıyla tutmak kırılgandır: satırlar sıralandığında ya da süzüldüğünde
sıra numarası kayar.

Sağlam yol, kimliği ögenin kendisinde taşımaktır. Web'in Temelleri ve HTML kursundaki
Veri Öznitelikleri dersinde tanımlanan `data-` öznitelikleri bu iş için vardır: değer
işaretlemede durur, öge ağaçta hareket etse de değeriyle birlikte hareket eder. Yukarıdaki
örnekte satırın ölçüm kodu bu yolla okunmuştur.

Aynı öznitelikler eylem türünü de taşıyabilir. Bir satırda silme, düzenleme ve
işaretleme düğmeleri varsa, her biri için ayrı bir seçici sınamak yerine düğmelerin
ortak bir özniteliğinde eylem adı tutulur; dinleyici tek arama yapar ve eylemi bir
eşlemeden çağırır. Bu, dinleyicinin uzunluğunu satır sayısından da eylem sayısından da
bağımsız kılar.

## Devrin Sınırları

Devir her olay türünde işlemez ve iki sınırı vardır.

**Kabarmayan olaylar devredilemez.** Önceki derste tanımlandığı gibi odak alma ve odak
kaybetme olayları kabarmaz; ataya bağlı kabarcık kipindeki bir dinleyici onları görmez.
Bu olayların kabaran karşılıkları ayrı türler olarak tanımlıdır ve devir onlarla kurulur.
İkinci yol, dinleyiciyi ataya **yakalama kipinde** kaydetmektir: yakalama evresi kabarmayan
olaylarda da işlediği için ata düğüm olayı görür. İki yolun seçimi, olayın hedefe
ulaşmadan mı ulaştıktan sonra mı işlenmesi gerektiğine bağlıdır.

**Yayılımı durduran ara dinleyiciler devri sessizce bozar.** Tabloya devredilmiş bir
dinleyici, satırın içindeki bir dinleyici `stopPropagation` çağırdığında hiç
çağrılmaz. Önceki derste yayılımı durdurmanın son çare olduğu söylenmişti; devir
kurulmuş bir ağaçta bu, uzaktan etki eden bir kesintiye dönüşür.

Bir üçüncü nokta sınır değil ama tasarım kararıdır: dinleyicinin bağlandığı ata ne kadar
yukarıdaysa kapsamı o kadar geniştir. Belgenin tamamına bağlanan bir dinleyici sayfadaki
her tıklamada çalışır. Devir, ilgilenilen ögelerin **ortak en yakın atasına** kurulur;
tablonun olayları tabloya, formun olayları forma devredilir.

## Özet

- Olay devri, ilgilenilen ögelerin bir atasına tek dinleyici bağlayıp hedefi olay anında
  çözümlemektir; dinleyici sayısını öge sayısından bağımsız kılar.
- Devir, kayıt anında var olmayan ögeleri de kapsar; eşleşme kayıt anında değil olay
  anında kurulduğu için sonradan eklenen düğümler dinleyicisiz kalmaz.
- Hedef, seçiciye uyan en yakın ataya çözümlenir; yalnız hedefi sınayan kod, iç içe
  ögelerden gelen tıklamaları kaçırır.
- Arama geçerli hedefte durdurulur; sınırsız arama dinleyicinin kapsamı dışındaki bir
  düğümü döndürebilir.
- Kayıt kimliği `data-` öznitelikleriyle ögenin kendisinde taşınır; sıra numarasına
  dayanan eşleme sıralama ve süzme sonrasında bozulur.
- Kabarmayan olaylar kabarcık kipiyle devredilemez; yakalama kipi ya da kabaran karşılık
  türü kullanılır. Ara bir dinleyicinin yayılımı durdurması devri sessizce iptal eder.

## Sonraki Adım

Bu derse kadar dinleyiciler yalnızca **eklediler**: olay geldi, kod çalıştı, sayfaya bir
davranış katıldı. Oysa tarayıcının kendi davranışları da vardır ve bunlar dinleyici
olmadan da işler. Bağlantıya tıklamak gezinme başlatır, gönderim düğmesi formu gönderir,
onay kutusu işaretini değiştirir, boşluk tuşu sayfayı kaydırır. Bir dinleyici bu
davranışların yerine kendi davranışını koymak istediğinde, tarayıcının yapacağı işi iptal
etmesi gerekir. Sonraki ders bu iptalin nasıl istendiğini, hangi olaylarda mümkün
olduğunu ve yayılımı durdurmakla neden karıştırılmaması gerektiğini inceler.
