---
title: Gözlemciler
source: 'https://academia.sh/tr/kurslar/tarayici-platformu/gozlemciler'
course: 'Tarayıcı ve Web Platformu'
language: tr
updated: '2026-08-17T18:09:11+00:00'
license: 'CC BY-SA 4.0'
---

# Gözlemciler

Görünürlük, boyut ve ağaç değişimini toplu ve eşzamansız bildiren gözlemci ailesi; kesişim oranının hesabı, kök payı ve eşik, içerik ile kenarlık kutusu ayrımı, kayıtların toplu teslimi ve gözlemcinin yerleşim maliyetine katkısı.

Adres ve geçmiş, kullanıcının istediği görünümü tanımlar. Ancak görünümün ekranda ne
kadarının göründüğü, bir kabın ne kadar genişlediği ve ağacın ne zaman değiştiği bu
bilgiden okunamaz. Uzun ölçüm listesinde görsellerin yalnızca yaklaştıklarında yüklenmesi,
bir grafiğin kabı daraldığında yeniden çizilmesi, sonradan eklenen satırların işlenmesi —
üçü de sürekli ölçüm ister.

Bunu her karede elle yapmak iki nedenle yanlıştır. Ölçüm pahalıdır ve ölçümün kendisi
tarayıcıyı yerleşim hesabı yapmaya zorlar. Tarayıcı bu üç soruyu, kendi hesabını zaten
yaptığı anda yanıtlayan bir arayüz ailesiyle karşılar.

## Ortak Biçim

Üç gözlemci de aynı kalıbı izler ve kalıp bir kez öğrenildiğinde üçü de aynı biçimde
kullanılır.

Bir **gözlemci (observer)** nesnesi, bir geri çağırım işleviyle kurulur. Kurulduğunda hiçbir
şey gözlemez; hangi hedeflerin izleneceği ayrıca bildirilir. Aynı gözlemci birden çok
hedefi izleyebilir ve her hedef için ayrı seçenek verilebilir.

Geri çağırım, tek tek olaylarla değil **kayıt dizisiyle** çağrılır. Aralarında geçen sürede
biriken bütün değişiklikler tek çağrıda teslim edilir.

İzleme üç işlemle sonlandırılır: tek bir hedefin izlenmesi bırakılır, bütün hedeflerin
izlenmesi kesilir ya da henüz teslim edilmemiş kayıtlar elle alınır. Bir bileşen ağaçtan
çıkarıldığında gözlemcisini kesmek zorundadır; kesilmeyen gözlemci, hedefine erişim
tuttuğu için Eşzamansız JavaScript ve Çalışma Zamanı kursunda tanımlanan sızıntının
kaynağı olur.

Bu kalıbın olay yerine seçilmesinin nedeni, olay modelinin sıklık denetimi
sunmamasıdır. Kaydırma olayı her kaydırma adımında üretilir ve dinleyicisi her seferinde
ölçüm yapmak zorunda kalır. Gözlemci ise ölçümü tarayıcının kendi hesabından alır ve
yalnızca sonuç değiştiğinde bildirir.

## Kesişim Gözlemcisi

**Kesişim gözlemcisi (intersection observer)** bir hedefin, bir kök dikdörtgeniyle ne
kadar kesiştiğini bildirir. Kök varsayılan olarak görünüm alanıdır; kaydırılabilir bir kap
da kök olarak verilebilir.

İki ayar davranışı belirler. **Kök payı (root margin)** kök dikdörtgenini büyütür ya da
küçültür; büyütmek, hedef henüz görünmeden bildirim almayı sağlar. **Eşik (threshold)**
bildirimin hangi kesişim oranlarında üretileceğini söyler.

```js
// kesisim.mjs — kesisim oraninin ve esik gecisinin hesabi
const dikdortgen = (ust, alt, sol = 0, sag = 360) => ({ ust, alt, sol, sag });
const alan = (r) => Math.max(0, r.alt - r.ust) * Math.max(0, r.sag - r.sol);

const kesisim = (a, b) => dikdortgen(
  Math.max(a.ust, b.ust), Math.min(a.alt, b.alt),
  Math.max(a.sol, b.sol), Math.min(a.sag, b.sag),
);

// Pay, kok dikdortgenini her yonde genisletir ya da daraltir.
const paylaKok = (kok, pay) => dikdortgen(
  kok.ust - pay.ust, kok.alt + pay.alt, kok.sol - pay.sol, kok.sag + pay.sag,
);

const oran = (hedef, kok) => alan(kesisim(hedef, kok)) / alan(hedef);

const gorunumAlani = dikdortgen(0, 800);
const onYukleme = { ust: 0, alt: 300, sol: 0, sag: 0 };

const satirlar = [
  ["T-01", dikdortgen(-40, 60)],    // ust kenardan disari tasmis
  ["T-02", dikdortgen(300, 400)],   // tumuyle icerde
  ["T-03", dikdortgen(760, 860)],   // alt kenarda
  ["T-04", dikdortgen(900, 1000)],  // gorunum disinda, pay icinde
  ["T-05", dikdortgen(1200, 1300)], // pay disinda
];

const ESIK = 0.25;
console.log("satir  oran(paysiz)  oran(pay:alt 300)  esik 0.25");
for (const [ad, hedef] of satirlar) {
  const a = oran(hedef, gorunumAlani);
  const b = oran(hedef, paylaKok(gorunumAlani, onYukleme));
  console.log(
    ad,
    a.toFixed(2).padStart(12),
    b.toFixed(2).padStart(18),
    (a >= ESIK ? "  gecti" : "  gecmedi").padEnd(9),
    b >= ESIK ? "| payla gecti" : "| payla da gecmedi",
  );
}
console.log("genisletilmis kok:", JSON.stringify(paylaKok(gorunumAlani, onYukleme)));
```

```
satir  oran(paysiz)  oran(pay:alt 300)  esik 0.25
T-01         0.60               0.60   gecti   | payla gecti
T-02         1.00               1.00   gecti   | payla gecti
T-03         0.40               1.00   gecti   | payla gecti
T-04         0.00               1.00   gecmedi | payla gecti
T-05         0.00               0.00   gecmedi | payla da gecmedi
genisletilmis kok: {"ust":0,"alt":1100,"sol":0,"sag":360}
```

Oran, hedefin **kendi alanına** göre hesaplanır; kökün alanına göre değil. Bu nedenle
görünüm alanından büyük bir öge tamamen ekranı kaplasa da oranı bire ulaşmaz.

Payın etkisi dördüncü satırda görünür. Ekranın 100 piksel altında duran satır paysız
hesapta hiç kesişmez; alt paya 300 piksel eklendiğinde tümüyle kesişir sayılır. Görselin
yüklenmesi bu bildirimle başlatıldığında, kullanıcı satıra ulaştığında görsel hazırdır.
Payın büyüklüğü bir ödünleşimdir: küçük pay boş kutular, büyük pay gereksiz indirme
üretir.

Eşik değerleri liste olarak verilebilir; her eşik geçişinde bildirim üretilir. Yalnız
"göründü ya da kayboldu" sorusu için tek bir sıfır eşiği yeter. Bir reklamın ya da içeriğin
ne kadar süre ne oranda görüldüğünü ölçmek için birden çok eşik gerekir.

İki davranış sık şaşırtır. Gözlemci, hedef izlenmeye başlandığında **ilk durumu da**
bildirir; ekranda zaten görünen ögeler için bir çağrı üretilir. Ayrıca gözlemci "görünür
mü" değil "kesişiyor mu" sorusunu yanıtlar: saydamlığı sıfır olan ya da başka bir ögenin
altında kalan hedef, kesişiyorsa görünür sayılır.

## Boyut Gözlemcisi

**Boyut gözlemcisi (resize observer)** bir ögenin ölçüsü değiştiğinde bildirir. Pencere
boyutu olayından ayrıldığı nokta budur: pencere değişmeden de bir kabın ölçüsü değişebilir —
yan panel açıldığında, yazı tipi yüklendiğinde, kardeş bir öge büyüdüğünde.

Bildirim, CSS ile Görsel Sunum kursundaki Kutu Modeli dersinde tanımlanan kutuları ayrı
ayrı verir: içerik kutusu ile kenarlık kutusu farklı sayılardır ve hangisinin okunacağı
hesaba bağlıdır. Grafiğin çizim alanı içerik kutusudur; kabın kapladığı yer kenarlık
kutusudur.

Bu gözlemcinin kendine özgü bir tuzağı vardır. Geri çağırım içinde gözlenen ögenin
boyutunu değiştiren kod, yeni bir bildirim üretir; bildirim yeniden boyut değiştirir ve
döngü kurulur. Tarayıcı bu döngüyü saptayıp bir hata bildirir ve o kare için çizimi
sürdürür. Çözüm, geri çağırımda ölçüye bağlı olmayan bir değişiklik yapmak ya da yeni
ölçüyü bir koşulla sınırlamaktır.

## Değişim Gözlemcisi

**Değişim gözlemcisi (mutation observer)** belge ağacındaki değişiklikleri bildirir: çocuk
eklenmesi ve çıkarılması, öznitelik değişimi, metin düğümünün içeriğinin değişmesi. Hangi
türlerin izleneceği seçeneklerle bildirilir ve alt ağacın tamamı kapsama alınabilir.

```js
// kayit.mjs — degisim kayitlarinin biriktirilip toplu teslim edilmesi
class DegisimGozlemcisi {
  constructor(geriCagirim) { this.geriCagirim = geriCagirim; this.kuyruk = []; this.hedefler = []; }
  gozle(hedef, secenek) { this.hedefler.push({ hedef, secenek }); }
  kaydet(kayit) {
    const eslesen = this.hedefler.find(
      (h) => h.hedef === kayit.hedef && h.secenek[kayit.tur] === true,
    );
    if (eslesen === undefined) return;                 // gozlenmeyen tur kaydedilmez
    if (kayit.tur === "oznitelik" && eslesen.secenek.eskiDeger !== true) kayit = { ...kayit, eski: undefined };
    this.kuyruk.push(kayit);
  }
  bosalt() {                                            // mikro gorev sinirinda calisir
    if (this.kuyruk.length === 0) return;
    const kayitlar = this.kuyruk;
    this.kuyruk = [];
    this.geriCagirim(kayitlar);
  }
}

let cagriSayisi = 0;
const gozlemci = new DegisimGozlemcisi((kayitlar) => {
  cagriSayisi += 1;
  console.log(`geri cagirim ${cagriSayisi}: ${kayitlar.length} kayit`);
  for (const k of kayitlar) console.log("   ", JSON.stringify(k));
});

const liste = { ad: "ul#olcumler" };
const baslik = { ad: "h2#baslik" };
gozlemci.gozle(liste, { cocukListesi: true, oznitelik: true, eskiDeger: true });
gozlemci.gozle(baslik, { cocukListesi: true });        // oznitelik gozlenmiyor

// Tek bir gorev icinde uc satir eklenip bir oznitelik degistiriliyor.
for (const kod of ["T-05", "T-06", "T-07"])
  gozlemci.kaydet({ tur: "cocukListesi", hedef: liste, eklenen: kod });
gozlemci.kaydet({ tur: "oznitelik", hedef: liste, ad: "data-suzgec", eski: "hepsi" });
gozlemci.kaydet({ tur: "oznitelik", hedef: baslik, ad: "class", eski: "acik" });
console.log("teslim oncesi kuyruk:", gozlemci.kuyruk.length);
gozlemci.bosalt();

// Sonraki gorevde tek degisiklik: ayri bir geri cagirim.
gozlemci.kaydet({ tur: "cocukListesi", hedef: liste, eklenen: "T-08" });
gozlemci.bosalt();
gozlemci.bosalt();                                      // bos kuyruk cagri uretmez
console.log("toplam geri cagirim:", cagriSayisi);
```

```
teslim oncesi kuyruk: 4
geri cagirim 1: 4 kayit
    {"tur":"cocukListesi","hedef":{"ad":"ul#olcumler"},"eklenen":"T-05"}
    {"tur":"cocukListesi","hedef":{"ad":"ul#olcumler"},"eklenen":"T-06"}
    {"tur":"cocukListesi","hedef":{"ad":"ul#olcumler"},"eklenen":"T-07"}
    {"tur":"oznitelik","hedef":{"ad":"ul#olcumler"},"ad":"data-suzgec","eski":"hepsi"}
geri cagirim 2: 1 kayit
    {"tur":"cocukListesi","hedef":{"ad":"ul#olcumler"},"eklenen":"T-08"}
toplam geri cagirim: 2
```

Dört davranış çıktıda okunur. Kayıtlar **biriktirilir**; dört değişiklik tek çağrıda
teslim edilmiştir. Teslim, olaylar gibi anında değil, çalışan kod bittikten sonra
gerçekleşir — Eşzamansız JavaScript ve Çalışma Zamanı kursundaki mikro görev sınırı burada
işler. İzlenmeyen tür hiç kaydedilmez: başlık ögesinin öznitelik değişimi kuyruğa
girmemiştir. Boş kuyruk çağrı üretmez.

Eski değerin kayıtta bulunması ayrıca istenir. İstenmediğinde kayıt yalnızca "bu öznitelik
değişti" der; önceki değer kaybolmuştur. Bu ayrım bellek maliyeti içindir ve varsayılan
olarak kapalıdır.

Değişim gözlemcisi son çare olarak kullanılır. Kendi yazdığı ağacı değiştiren kod, neyi
değiştirdiğini zaten bilir; ağacı gözlemek yerine değişikliği yapan yerde bildirimde
bulunmak hem ucuz hem açıktır. Gözlemci, denetim dışındaki bir kodun ürettiği değişikliği
izlemek gerektiğinde anlamlıdır.

## Ölçümün Maliyeti

Gözlemcilerin ortak gerekçesi burada toplanır. Bir ögenin ölçüsünü ya da konumunu doğrudan
sorgulamak, tarayıcıyı bekleyen bütün biçem ve yerleşim hesaplarını **o anda** yapmaya
zorlar. Döngü içinde sırayla ölçüp değiştiren kod bu zorlamayı her turda tetikler.

Gözlemciler bu zorlamayı ortadan kaldırır: ölçüm, tarayıcının kendi hesabını yaptığı anda
alınır ve sonuç geri çağırıma hazır olarak gelir. Ölçüm ile değiştirmenin ayrı aşamalara
bölünmesi ise sonraki dersin konusudur.

## Özet

- Üç gözlemci ortak bir kalıp izler: geri çağırımla kurulur, hedefler ayrıca bildirilir,
  teslim tek tek değil kayıt dizisiyle yapılır ve izleme açıkça kesilir.
- Kesişim oranı hedefin kendi alanına göre hesaplanır; kök payı bildirimi erkene çeker,
  eşik listesi hangi oranlarda bildirim üretileceğini belirler.
- Kesişim gözlemcisi izleme başladığında ilk durumu da bildirir ve görünürlüğü değil
  kesişimi ölçer.
- Boyut gözlemcisi içerik ve kenarlık kutularını ayrı bildirir; geri çağırımda boyutu
  değiştiren kod döngü üretir.
- Değişim kayıtları biriktirilip mikro görev sınırında toplu teslim edilir; izlenmeyen tür
  kaydedilmez ve eski değer ayrıca istenmedikçe taşınmaz.
- Gözlemciler ölçümü tarayıcının kendi hesabından aldığı için doğrudan sorgulamanın
  yarattığı yerleşim zorlamasını ortadan kaldırır.

## Sonraki Adım

Gözlemciler ölçümün ne zaman alınacağını çözer, ancak değişikliğin ne zaman uygulanacağını
açık bırakır. Ölçüm listesi kaydırılırken başlığın konumunu güncelleyen, bir çubuğu
büyüten ya da sayacı ilerleten kod, bunu rastgele bir anda yaparsa tarayıcının çizim
düzenine ters düşer: kare atlanır, hareket kesikleşir. Tarayıcının her kareyi hangi
sırayla hazırladığı tanımlıdır ve bu sıraya yerleşmenin bir yolu vardır. Sonraki ders kare
kavramını, zamanlayıcıların neden kare hizasında olmadığını ve okuma ile yazmanın neden
ayrı aşamalara bölündüğünü ele alır.
