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

# Özel Elemanlar

Tarayıcının öge kaydına yeni bir etiket adı tanıtmak; ad kuralı, yaşam döngüsü geri çağrılarının sırası, tanımdan önce ağaca girmiş ögenin yükseltilmesi ve öznitelik–özellik yansıması.

Önceki ders ana iş parçacığındaki yükü çalışan iş parçacıklarına dağıttı: hesap bir
yerde, arayüz başka bir yerde. Yük dağıtıldıktan sonra geriye arayüzün kendi sorunu
kalır. İstasyon sayfasındaki ölçüm rozeti — değeri, birimi ve eşik rengiyle bir sayıyı
gösteren küçük parça — sayfada beş kez, gösterge panosunda on beş kez tekrarlanır. Her
tekrarda aynı işaretleme elle yazılır, aynı sınıflar elle verilir, aynı dinleyiciler elle
bağlanır.

Bu tekrarı bir yere toplamanın yolu, tarayıcıya yeni bir etiket adı tanıtmaktır: yapısı,
davranışı ve yaşam döngüsü kendi içinde tanımlı, belgede tek satırla yazılan bir öge. Bu
derste kurulan mekanizmanın adı **özel öge (custom element)** dir.

## Ad Kuralı ve Öge Kaydı

Tarayıcı, etiket adlarını bir tabloda tutar: `p`, `section`, `input` ve geri kalanı. Bu
tabloya program içinden yeni bir ad eklenir; ada karşılık gelen bir sınıf verilir ve
tarayıcı o addaki her ögeyi bu sınıfın bir örneğiyle ilişkilendirir. Bu tabloya **öge
kaydı (custom element registry)** denir.

Eklenen adın tek bir biçim kuralı vardır: **en az bir tire içermeli** ve küçük harfle
başlamalıdır. `olcum-rozeti` geçerlidir, `rozet` değildir. Kuralın gerekçesi kısıt değil
korumadır. HTML'e sonradan eklenen bir etiket adı hiçbir zaman tire içermez; tire içeren
ad uzayı böylece belge yazarına ayrılmış olur. Bu ayrım olmasaydı, bugün tanımlanan bir
`rozet` ögesi ileride standarda giren bir `rozet` etiketiyle çakışır ve sayfa sessizce
başka bir şey yapmaya başlardı.

Bir ad yalnızca bir kez tanımlanabilir. Aynı adı ikinci kez tanımlamaya çalışmak hata
üretir; bu, aynı bileşenin iki ayrı sürümünün aynı sayfaya girmesini erken yakalayan bir
denetimdir.

İki tür özel öge vardır. Birincisi, temel öge sınıfını genişleten ve kendi etiket adıyla
yazılan bağımsız ögedir. İkincisi, var olan bir etiketi genişletir ve o etiketin
davranışını devralır; belgede `is` özniteliğiyle yazılır. İkinci türün desteği koşulludur
ve özellik saptamayla sınanır; bu ders birinci türü esas alır.

## Yaşam Döngüsü Geri Çağrıları

Özel ögenin sınıfı dört noktada tarayıcı tarafından çağrılır. Bunlara **yaşam döngüsü
geri çağrısı (lifecycle callback)** denir.

**Kurucu**, öge nesnesi ilk kez var edildiğinde çalışır. **Bağlanma geri çağrısı**, öge
belgeye bağlı bir ağaca girdiğinde çalışır. **Bağlantı kesilme geri çağrısı**, öge o
ağaçtan çıkarıldığında. **Öznitelik değişimi geri çağrısı**, önceden bildirilmiş bir
özniteliğin değeri değiştiğinde.

Son geri çağrının bir koşulu vardır: sınıf, hangi özniteliklerin izleneceğini durağan bir
listeyle bildirmelidir. Listede olmayan bir özniteliğin değişmesi hiçbir çağrı üretmez.
Bu, her öznitelik yazımının bir geri çağrı tetiklemesini önleyen bilinçli bir
sınırlamadır.

Sıra, elle yazılmış bir kurulum kodundan farklıdır ve tahmin edilerek öğrenilemez.
Aşağıdaki model çağrı sırasını görünür kılar.

```js
// yasam-dongusu.mjs — ozel oge yasam dongusunun ve yukseltmenin kucuk bir modeli
const cagrilar = [];

const belge = { ad: "#belge", ozn: {}, cocuklar: [], ebeveyn: null, ornek: null };
const bagliMi = (d) => { let a = d; while (a.ebeveyn) a = a.ebeveyn; return a === belge; };
function* hepsi(kok) { yield kok; for (const c of kok.cocuklar) yield* hepsi(c); }

const tanimlar = new Map();

// Yukseltme sirasi sabittir: kurucu → izlenen oznitelikler → bagliysa baglanma.
function yukselt(dugum) {
  const sinif = tanimlar.get(dugum.ad);
  if (!sinif || dugum.ornek) return;
  dugum.ornek = new sinif(dugum);
  for (const ad of sinif.izlenenOznitelikler ?? [])
    if (ad in dugum.ozn) dugum.ornek.oznitelikDegisti(ad, null, dugum.ozn[ad]);
  if (bagliMi(dugum)) dugum.ornek.baglandi();
}

// Oge olusturulurken tanim varsa yukseltme aninda olur, yoksa oge tanimsiz kalir.
function oge(ad, ozn = {}) {
  const dugum = { ad, ozn, cocuklar: [], ebeveyn: null, ornek: null };
  yukselt(dugum);
  return dugum;
}

function tanimla(ad, sinif) {
  if (!ad.includes("-")) throw new Error(`gecersiz ad: ${ad}`);
  if (tanimlar.has(ad)) throw new Error(`zaten tanimli: ${ad}`);
  tanimlar.set(ad, sinif);
  for (const d of hepsi(belge)) if (d.ad === ad) yukselt(d); // belge sirasina gore
}

function cikar(dugum) {
  const kardesler = dugum.ebeveyn.cocuklar;
  const bagliydi = bagliMi(dugum);
  kardesler.splice(kardesler.indexOf(dugum), 1);
  dugum.ebeveyn = null;
  if (bagliydi) dugum.ornek?.baglantiKesildi();
}

function ekle(ebeveyn, cocuk) {
  if (cocuk.ebeveyn) cikar(cocuk);
  cocuk.ebeveyn = ebeveyn;
  ebeveyn.cocuklar.push(cocuk);
  if (!cocuk.ornek) yukselt(cocuk);
  else if (bagliMi(cocuk)) cocuk.ornek.baglandi();
}

function oznitelikYaz(dugum, ad, deger) {
  const eski = dugum.ozn[ad] ?? null;
  dugum.ozn[ad] = deger;
  const izlenen = tanimlar.get(dugum.ad)?.izlenenOznitelikler ?? [];
  if (dugum.ornek && izlenen.includes(ad)) dugum.ornek.oznitelikDegisti(ad, eski, deger);
}

let sayac = 0;
class OlcumRozeti {
  static izlenenOznitelikler = ["deger", "birim"];
  constructor(dugum) {
    this.etiket = `rozet#${++sayac}`;
    cagrilar.push(`${this.etiket} kurucu`);
  }
  oznitelikDegisti(ad, eski, yeni) {
    cagrilar.push(`${this.etiket} oznitelikDegisti(${ad}, ${eski}, ${yeni})`);
  }
  baglandi() { cagrilar.push(`${this.etiket} baglandi`); }
  baglantiKesildi() { cagrilar.push(`${this.etiket} baglantiKesildi`); }
}

// A. Tanimdan ONCE agaca girmis iki oge (ayristiricinin urettigi durum).
const pano = oge("section", { id: "pano" });
ekle(belge, pano);
ekle(pano, oge("olcum-rozeti", { deger: "-4.2", birim: "C" }));
ekle(pano, oge("olcum-rozeti", { deger: "72" }));
cagrilar.push("--- tanimla('olcum-rozeti') ---");
tanimla("olcum-rozeti", OlcumRozeti);

// B. Tanimdan SONRA olusturulan bir oge.
cagrilar.push("--- tanimdan sonra olusturulan oge ---");
const yeni = oge("olcum-rozeti");
oznitelikYaz(yeni, "deger", "3.4");
ekle(pano, yeni);

// C. Bagli bir oge baska bir ebeveyne tasiniyor.
cagrilar.push("--- oge baska bir ebeveyne tasiniyor ---");
const yanPano = oge("aside", { id: "yan" });
ekle(belge, yanPano);
ekle(yanPano, yeni);

// D. Tire icermeyen ad.
cagrilar.push("--- tire icermeyen ad ---");
try { tanimla("rozet", OlcumRozeti); } catch (h) { cagrilar.push(h.message); }

console.log(cagrilar.join("\n"));
```

```
--- tanimla('olcum-rozeti') ---
rozet#1 kurucu
rozet#1 oznitelikDegisti(deger, null, -4.2)
rozet#1 oznitelikDegisti(birim, null, C)
rozet#1 baglandi
rozet#2 kurucu
rozet#2 oznitelikDegisti(deger, null, 72)
rozet#2 baglandi
--- tanimdan sonra olusturulan oge ---
rozet#3 kurucu
rozet#3 oznitelikDegisti(deger, null, 3.4)
rozet#3 baglandi
--- oge baska bir ebeveyne tasiniyor ---
rozet#3 baglantiKesildi
rozet#3 baglandi
--- tire icermeyen ad ---
gecersiz ad: rozet
```

Çıktının okunması gereken dört yeri var.

Birincisi, ilk yedi satır **tanım çağrılana kadar hiçbir geri çağrının çalışmadığını**
gösterir. Ögeler ağaçtaydı, öznitelikleri yerindeydi; bekleyen şey tanımdı.

İkincisi, her öge için sıra aynıdır: kurucu, sonra izlenen özniteliklerin her biri için
bir öznitelik değişimi çağrısı, sonra bağlanma. Bağlanma her zaman sonuncudur. Bir
bileşenin ilk çizimini bağlanma geri çağrısında yapması bu yüzden güvenlidir: o noktada
öznitelik değerleri okunmuştur.

Üçüncüsü, ikinci rozette `birim` özniteliği belgede yazılı olmadığı için çağrı üretmemiş,
eksik öznitelik için varsayılan bir çağrı yapılmamıştır. Varsayılan değeri kurmak sınıfın
işidir.

Dördüncüsü, taşınan öge için **bağlantı kesilme ve bağlanma art arda çalışmıştır**.
Ağaçta yer değiştirmek, ögeyi çıkarıp yeniden eklemektir. Buradan bağlayıcı bir kural
çıkar: bağlanma geri çağrısı ögenin ömrü boyunca birden çok kez çalışabilir, bu yüzden
yinelemeye dayanıklı yazılmalıdır. Dinleyici bağlayan bir bağlanma geri çağrısının
karşılığında bağlantı kesilme geri çağrısında dinleyiciyi kaldırması gerekir; yoksa her
taşımada bir dinleyici birikir.

## Kurucuda Yapılamayanlar

Kurucu, ögenin en erken uğrağıdır ve bu erkenlik bir kısıt getirir. Kurucu çalıştığı anda
öge henüz ağaçta olmayabilir, öznitelikleri okunmamış olabilir, çocukları ayrıştırılmamış
olabilir. Ayrıştırıcı bir etiketi gördüğü anda örneği kurar; kapanış etiketine daha
gelmemiştir.

Bundan üç kural çıkar. Kurucu **öznitelik okumaz ve yazmaz**. Kurucu **çocuk düğüm
eklemez ve okumaz**. Kurucu **belgeye erişmez**. Kurucunun işi yalnızca ögenin kendi
alanlarını ilk değerlerine kurmaktır; belgeyle ilgili her şey bağlanma geri çağrısına
kalır.

Ayrıştırma sırasının bir yan sonucu daha vardır: belgenin ortasındaki bir özel ögenin
bağlanma geri çağrısı çalıştığında, o ögenin çocukları henüz ayrıştırılmamış olabilir.
Çocuklarını okuması gereken bir bileşen bunu ya ayrıştırmanın bitmesini bekleyerek ya da
çocuk listesini gözlemleyerek yapar; bağlanma anında sayarak yapamaz.

## Yükseltme ve Tanımın Gecikmesi

Tanımdan önce ağaca girmiş bir ögenin tanım geldiğinde sınıfla ilişkilendirilmesine
**yükseltme (upgrade)** denir. Yükseltme yeni bir nesne üretmez: ağaçtaki nesne olduğu
gibi kalır, sınıfın davranışını kazanır.

Bu, sayfanın tanımdan önceki hâlinin bir sorun değil bir ara durum olduğu anlamına gelir.
Tanımı taşıyan betik ertelenmiş olarak yüklendiğinde, kullanıcı belgedeki ham ögeleri
kısa bir süre görür. Bu ara durum biçemle yönetilir: tanımlanmamış ögeleri seçen bir
sözde sınıf vardır ve bu ögeler tanım gelene kadar gizlenebilir ya da yer tutucu
biçimiyle gösterilebilir. Kurulacak denge, boş bir alan göstermekle biçimsiz içerik
göstermek arasındadır; ölçüm rozetinde doğru seçim, sayıyı ham hâliyle göstermek ve
yalnızca eşik rengini tanım gelene kadar uygulamamaktır — okunabilir içerik hiç
gizlenmez.

Program tarafında da bir bekleme noktası gerekir: bir adın tanımlanmasını bekleyen ve
tanım geldiğinde çözülen bir söz. Bir ögenin özelliklerine yükseltme öncesinde erişen kod
bu sözü bekler; beklemeyen kod bir sonraki bölümdeki tuzağa düşer.

## Öznitelik ile Özellik Arasında Yansıma

DOM API'si dersinde kurulan ayrım burada tasarım kararına dönüşür: öznitelik belgede
yazılı dizgidir, özellik program tarafındaki yaşayan değerdir. Bir özel öge ikisini de
sunar ve arada bir **yansıma (reflection)** kurar: özelliğe yazılan değer özniteliğe
yazılır, özniteliğe yazılan değer özelliği günceller.

İki yönlü bu bağ dikkatsiz kurulduğunda kendi kendini besler. Özellik ayarlayıcısı
özniteliği yazar, öznitelik değişimi geri çağrısı özelliği yazar, o da özniteliği yeniden
yazar. Döngüyü kesen kural sadedir: **her iki yönde de yazmadan önce değerin gerçekten
değiştiği sınanır.** Aynı değeri yeniden yazan bir adım zincirin devamını kesmelidir.

İkinci tuzak daha sinsidir ve yükseltmenin gecikmesinden doğar.

```js
// yukseltme-tuzagi.mjs — tanimdan once atanan ozellik erisimciyi golgeler
// Yukseltme, ogenin prototipini sinif prototipiyle degistirmektir; oge nesnesi aynidir.
class Rozet {
  get deger() { return this.degerAlani ?? null; }
  set deger(v) {
    this.degerAlani = Number(v);
    this.cizimSayisi = (this.cizimSayisi ?? 0) + 1; // gercek bir ogede bu, yeniden cizim olurdu
  }
}

function yukselt(dugum, { kurtar }) {
  Object.setPrototypeOf(dugum, Rozet.prototype);
  if (kurtar && Object.hasOwn(dugum, "deger")) {
    const bekleyen = dugum.deger; // erisimciyi golgeleyen kendi ozelligi
    delete dugum.deger;           // golge kaldirilir
    dugum.deger = bekleyen;       // atama artik erisimciye gider
  }
}

console.log("cagiran kod, tanim yuklenmeden once ozellige deger atiyor:");
for (const kurtar of [false, true]) {
  const dugum = {};   // tanimi henuz yuklenmemis oge
  dugum.deger = "5";  // erisimci yok: ogenin kendi ozelligi olusur
  yukselt(dugum, { kurtar });
  console.log(
    `  kurtarma ${kurtar ? "var" : "yok"} → deger: ${JSON.stringify(dugum.deger)},`,
    `tip: ${typeof dugum.deger}, cizim: ${dugum.cizimSayisi ?? 0}`,
  );
}
```

```
cagiran kod, tanim yuklenmeden once ozellige deger atiyor:
  kurtarma yok → deger: "5", tip: string, cizim: 0
  kurtarma var → deger: 5, tip: number, cizim: 1
```

Tanım yüklenmeden önce özelliğe yapılan atama, ögenin **kendi özelliğini** oluşturur.
Yükseltme prototipi değiştirir, ama prototipteki erişimci ögenin kendi özelliğinin
altında kalır ve hiçbir zaman çağrılmaz. İlk satırda değer dizgi olarak durur, dönüşüm
yapılmamış, çizim tetiklenmemiştir. Sorun sessizdir: bir hata iletisi yoktur, yalnızca
bileşen çalışmaz.

Kurtarma tek kalıptır ve kurucuda uygulanır: sınıfın tanımladığı her özellik için, ögede
aynı adda bir kendi özelliği varsa değeri alınır, özellik silinir ve yeniden atanır.
Silme prototipteki erişimciyi görünür kılar, yeniden atama onu çalıştırır. İkinci satırda
değer sayıya dönüşmüş ve çizim bir kez tetiklenmiştir.

## Özet

- Özel öge, tarayıcının öge kaydına bir etiket adı ile bir sınıf çifti eklemektir; ad en
  az bir tire içermeli ve yalnızca bir kez tanımlanmalıdır.
- Tire koşulu, sonradan standarda girecek etiket adlarıyla çakışmayı önlemek için ad
  uzayını ayırır.
- Yaşam döngüsü çağrıları sabit sırayla işler: kurucu, izlenen özniteliklerin değişim
  çağrıları, sonra bağlanma; bağlantı kesilme ise ağaçtan çıkarılışta.
- Bağlanma geri çağrısı ögenin ömrü boyunca birden çok kez çalışabilir; kurduğu her şeyi
  bağlantı kesilme geri çağrısı sökmelidir.
- Kurucu öznitelik okumaz, çocuk eklemez, belgeye erişmez; belgeyle ilgili iş bağlanma
  geri çağrısına aittir.
- Tanımdan önce ağaçta bulunan ögeler yükseltilir; tanımdan önce özelliğe yazılan değer
  erişimciyi gölgeler ve kurucuda sil–yeniden ata kalıbıyla kurtarılır.

## Sonraki Adım

Bu ders ögeye bir ad ve bir yaşam döngüsü verdi, ama içini açık bıraktı. Rozetin işaretlemesi
hâlâ belgenin ortak ağacında durur: sayfanın herhangi bir yerinde yazılmış bir biçem kuralı
rozetin içine sızabilir, rozetin kendi kuralı da dışarı taşabilir; sayfadaki bir seçici
rozetin iç düğümlerini bulur ve onlara dokunur. Yeniden kullanılabilir bir parçanın
gereksinimi tam da bunun tersidir: içerisi dışarıdan görünmesin, dışarısı içeriden
etkilenmesin. Sonraki ders belge ağacının içine ayrı bir ağaç açan ve bu iki yönlü yalıtımı
kuran mekanizmayı inceler.
