ResizeObserver bir elemanın boyutu değiştiğinde, MutationObserver ise DOM ağacında bir değişiklik olduğunda (çocuk eklenip çıkarılması, öznitelik veya metin değişimi) sizi haberdar eder. İkisi de bir geri çağırmayla oluşturulur, observe() ile hedef alır, disconnect() ile durdurulur. Pencere resize olayı yalnızca pencere boyutunu bildirir; ResizeObserver ise tek bir kutunun, pencereyle hiç ilgisi olmayan sebeplerle değişen boyutunu da yakalar.
Bu yazıda önce ResizeObserver'ın hangi kutuyu ölçtüğünü, contentBoxSize ve borderBoxSize dizilerini ve meşhur "ResizeObserver loop completed with undelivered notifications" hatasının nedenini, ardından MutationObserver seçeneklerini, MutationRecord alanlarını ve takeRecords() kullanımını göreceksiniz. Görünürlük takibi bu yazının konusu değil; o iş için Intersection Observer yazısına bakın.
ResizeObserver ile eleman boyutunu izlemek

new ResizeObserver(callback) tek parametre alır. Geri çağırma bir ResizeObserverEntry dizisi ve gözlemcinin kendisiyle çağrılır. MDN, ikinci parametrenin örneğin belirli bir koşul sağlandığında gözlemi bırakmak için kullanılabileceğini, ihtiyacınız yoksa yazmayabileceğinizi belirtir. Arayüzün kendine ait bir özelliği yoktur; üç yöntemi vardır: observe(), unobserve() ve disconnect().
const kart = document.querySelector(".kart");
const ro = new ResizeObserver((entries) => {
for (const entry of entries) {
const boyut = entry.contentBoxSize[0];
// genişliğe göre düzen sınıfı değiştir
entry.target.classList.toggle("dar", boyut.inlineSize < 400);
}
});
ro.observe(kart);Bu örnek kart 400 pikselden darsa dar sınıfını ekler. MDN'nin kendi örneği de benzer şekilde, bir kutunun genişliğine göre başlık ve paragrafın yazı boyutunu değiştirir ve bunun görüş alanıyla ilgisi olmayan boyut değişimlerinde de çalıştığını vurgular. classList.toggle() ayrıntısı classList yazısında anlatılıyor.
Hangi kutu ölçülüyor: box seçeneği
observe(hedef, { box }) ile hangi kutu modelinin izleneceğini seçersiniz. MDN üç değer sayar:
| box değeri | Ölçülen |
|---|---|
| content-box (varsayılan) | CSS içerik alanı: kenarlık kutusundan dolgu ve kenarlık çıkarılmış hali |
| border-box | İçerik, dolgu ve kenarlığı kapsayan kenarlık kutusu |
| device-pixel-content-box | İçerik alanı, cihaz pikseli cinsinden ve CSS dönüşümleri uygulanmadan önce |
device-pixel-content-box özellikle canvas için kullanışlıdır. MDN'nin örneğinde canvas'ın width ve height değerleri devicePixelContentBoxSize[0] içindeki boyutlarla ayarlanır; böylece canvas pikselleri fiziksel piksellerle bire bir eşleşir. MDN, geri çağırmanın yerleşimden sonra ve boyamadan önce çalıştığını, bunun da fiziksel piksel boyutunu boyamadan önce öğrenme fırsatı verdiğini belirtir. Canvas boyutlandırmanın neden önemli olduğu Canvas çizim rehberinde anlatılıyor.
contentBoxSize neden dizi?
contentBoxSize, borderBoxSize ve devicePixelContentBoxSize tek bir nesne değil, nesne dizisidir. MDN bunun parçalara bölünen elemanları desteklemek için olduğunu söyler; çok sütunlu düzende bir eleman birden fazla parçaya bölünebilir. Her dizi öğesi bir ResizeObserverSize nesnesidir ve iki alan taşır: inlineSize ve blockSize. Yatay yazım yönünde inlineSize genişliğe, blockSize yüksekliğe karşılık gelir; dikey yazım yönünde tersidir. Çok sütunlu düzende dönen boyut ilk sütunun boyutudur.
Eski bir alan olan contentRect da hâlâ vardır ve bir DOMRectReadOnly döndürür. MDN bunu yalnızca geriye dönük uyumluluk için tutulan eski (legacy) bir özellik olarak tanımlar ve ileride kullanımdan kaldırılabileceğini belirtir. MDN örnekleri bu yüzden önce contentBoxSize var mı diye bakar, yoksa contentRect kullanır. Yeni kodda boyut dizilerini tercih edin.
Gözlemi açıp kapatmak
MDN örneğinde bir onay kutusu gözlemi açıp kapatır: kutu işaretliyken observe(), değilken unobserve() çağrılır. Gözlem kapalıyken kap genişlese de daralsa da yazı boyutu değişmez. unobserve() tek hedefi, disconnect() ise gözlemcinin izlediği bütün hedefleri bırakır; ikisi de değer döndürmez ve MDN'ye göre istisna fırlatmaz. Bir bileşen sayfadan kaldırılırken ona bağlı gözlemciyi de durdurmak iyi bir alışkanlıktır; böylece artık kullanılmayan bir elemana ait geri çağırma çalışmaya devam etmez.
checkbox.addEventListener("change", () => {
if (checkbox.checked) {
resizeObserver.observe(divElem);
} else {
resizeObserver.unobserve(divElem);
}
});Mini örnek: canvas çözünürlüğünü kaba uydurmak
Duyarlı bir sayfada canvas'ın CSS boyutu kabıyla birlikte değişir, ama çizim yüzeyinin piksel sayısı width ve height öznitelikleriyle belirlenir. İkisi uyuşmazsa çizim ya bulanık ya da çarpık görünür. MDN'nin devicePixelContentBoxSize örneği bu sorunu ResizeObserver ile çözer: canvas'ın içerik kutusu cihaz pikseli cinsinden ölçülür ve çizim yüzeyi bu boyuta ayarlanır.
const canvas = document.querySelector("canvas");
const observer = new ResizeObserver((entries) => {
const entry = entries.find((e) => e.target === canvas);
canvas.width = entry.devicePixelContentBoxSize[0].inlineSize;
canvas.height = entry.devicePixelContentBoxSize[0].blockSize;
ciz(); // boyut atandığı için bağlam sıfırlandı, yeniden çiz
});
observer.observe(canvas, { box: "device-pixel-content-box" });Burada iki ayrıntıya dikkat edin. Birincisi, canvas.width ataması bağlamı sıfırlar ve içeriği siler; bu yüzden her boyut değişiminden sonra çizimi yeniden yapmanız gerekir. İkincisi, canvas'ın ekrandaki CSS boyutu genellikle kabından gelir; çizim yüzeyinin piksel sayısını değiştirmek CSS boyutunu değiştirmez. Geri çağırma içinde canvas'ın CSS genişliğini siz değiştirirseniz ise aşağıda anlatılan döngü sorunu ortaya çıkabilir. Canvas boyutlandırma kuralları Canvas çizim rehberinde ayrıntılı anlatılıyor.
ResizeObserver döngü hatası
Tarayıcı boyut bildirimlerini boyamadan önce gönderir. Geri çağırmanız elemanın boyutunu yine değiştirirse stil ve yerleşim yeniden hesaplanır ve bu yeni boyut bildirimleri doğurabilir. Sonsuz döngüyü önlemek için her turda yalnızca DOM'da daha derinde olan elemanlar işlenir; koşulu sağlamayan bildirimler bir sonraki boyamaya ertelenir ve Window nesnesinde şu iletiyle bir error olayı tetiklenir: ResizeObserver loop completed with undelivered notifications.
MDN önemli bir noktanın altını çizer: bu mekanizma yalnızca tarayıcının kilitlenmesini önler, döngünün kendisini engellemez. Aşağıdaki kod her bildirimde genişliği 10 piksel artırdığı için eleman sonsuza kadar büyür ve konsolda aynı ileti her karede tekrar eder:
const divElem = document.querySelector("body > div");
const resizeObserver = new ResizeObserver((entries) => {
for (const entry of entries) {
entry.target.style.width = `${entry.contentBoxSize[0].inlineSize + 10}px`;
}
});
resizeObserver.observe(divElem);Çözüm amacınıza bağlıdır. Döngüyü gerçekten istiyorsanız (örneğin sürekli büyüyen bir efekt), boyut değiştiren kodu requestAnimationFrame geri çağırmasına taşıyarak boyamadan sonraya erteleyin. Döngü istemiyorsanız geri çağırmanızın kendi kendini tetiklemesini engelleyin; MDN bunun bir yolunu "beklenen boyutu" bir WeakMap içinde saklamak olarak gösterir:
const expectedSizes = new WeakMap();
const resizeObserver = new ResizeObserver((entries) => {
requestAnimationFrame(() => {
for (const entry of entries) {
const expectedSize = expectedSizes.get(entry.target);
if (entry.contentBoxSize[0].inlineSize === expectedSize) {
continue; // bu değişikliği zaten biz yaptık
}
const newSize = entry.contentBoxSize[0].inlineSize + 10;
entry.target.style.width = `${newSize}px`;
expectedSizes.set(entry.target, newSize);
}
});
});Hata olayı sonsuza kadar tetiklenmediği sürece gözlemci sonunda dengeye oturur ve büyük olasılıkla doğru bir yerleşim üretir. Ancak MDN'nin uyardığı gibi kullanıcı, tek karede olması gereken değişiklikler birkaç kareye yayıldığı için kısa bir bozuk yerleşim görebilir. requestAnimationFrame ayrıntıları animasyon döngüsü yazısında.
MutationObserver ile DOM değişikliklerini izlemek

MutationObserver, eski DOM3 Mutation Events özelliğinin yerine tasarlanmıştır. new MutationObserver(callback) gözlemciyi oluşturur ama izleme hemen başlamaz; MDN'ye göre önce observe() çağrılarak hangi DOM bölümünün ve hangi tür değişikliklerin izleneceği belirtilmelidir. Geri çağırma bir MutationRecord dizisi ve gözlemciyi alır; ikinci parametre çoğu zaman gözlemciyi disconnect() ile durdurmak için kullanılır.
const hedef = document.getElementById("some-id");
const config = { attributes: true, childList: true, subtree: true };
const observer = new MutationObserver((mutationList, observer) => {
for (const mutation of mutationList) {
if (mutation.type === "childList") {
console.log("Bir çocuk düğüm eklendi veya çıkarıldı.");
} else if (mutation.type === "attributes") {
console.log(`${mutation.attributeName} özniteliği değişti.`);
}
}
});
observer.observe(hedef, config);
// daha sonra: observer.disconnect();observe() seçenekleri
| Seçenek | Varsayılan | Açıklama |
|---|---|---|
| childList | false | Hedefe çocuk düğüm eklenmesini veya çıkarılmasını izler |
| attributes | false (attributeFilter veya attributeOldValue varsa true) | Öznitelik değerlerindeki değişimi izler |
| characterData | false (characterDataOldValue varsa true) | Metin düğümlerinin içeriğindeki değişimi izler |
| subtree | false | Diğer seçenekleri hedefin bütün alt ağacına genişletir |
| attributeFilter | yok | Yalnızca bu dizideki öznitelik adlarını izler |
| attributeOldValue | false | Değişen özniteliğin eski değerini kaydeder |
| characterDataOldValue | false | Değişen metnin eski değerini kaydeder |
childList, attributes ve characterData seçeneklerinden en az biri true olmalıdır. MDN üç durumda TypeError fırlatıldığını söyler: hiçbir şey izlenmeyecek şekilde ayar verilirse; attributes açıkça false iken attributeOldValue true veya attributeFilter verilmişse; characterDataOldValue true iken characterData false ise. Aynı gözlemci üzerinde observe() birden çok kez çağrılarak farklı DOM bölümleri izlenebilir, aynı düğüm de birden fazla gözlemci tarafından izlenebilir.
MutationRecord alanları
| Alan | İçerik |
|---|---|
| type | attributes, characterData veya childList |
| target | attributes için özniteliği değişen eleman, characterData için metin düğümü, childList için çocukları değişen düğüm |
| addedNodes / removedNodes | Eklenen veya çıkarılan düğümler; yoksa boş NodeList |
| previousSibling / nextSibling | Eklenen veya çıkarılan düğümlerin önceki ve sonraki kardeşi ya da null |
| attributeName | Değişen özniteliğin adı ya da null |
| oldValue | attributes ve characterData için önceki değer (ilgili OldValue seçeneği açıksa), childList için null |
addedNodes ve removedNodes birer NodeList olduğu için doğrudan for...of ile gezilebilir. NodeList ile HTMLCollection farkı DOM ağacında gezinme yazısında anlatılıyor.
Kaydın türünü görmek için MDN'nin type sayfasındaki örnek iyi bir başlangıçtır: aynı gözlemci childList, attributes ve subtree seçenekleriyle bir kabı izler. Kaba yeni bir paragraf eklendiğinde kaydın türü childList, kabın class özniteliği red ile blue arasında değiştirildiğinde ise attributes olur. Tek geri çağırma içinde switch (mutation.type) ile türlere göre ayrı işlem yapmak bu yüzden yaygın bir kalıptır; MDN'nin sohbet odası örneği de önce türe, sonra attributeName değerine göre dallanır.
oldValue ne zaman dolu gelir?
oldValue alanı her kayıtta dolu gelmez. MDN'ye göre bir öznitelik değişiminde eski değeri görmek için observe() çağrısında attributeOldValue true olmalı, attributes true olmalı ya da hiç yazılmamalı ve kaydın türü attributes olmalıdır. Metin düğümlerinde aynı kural characterDataOldValue ve characterData için geçerlidir. Bu koşullar sağlanmazsa ya da kayıt childList türündeyse oldValue değeri null olur. Yeni değeri ise kayıtta aramazsınız; mutation.target üzerinden o anki özniteliği okursunuz. MDN'nin renk örneği bir başlığın style özniteliğini izler ve her değişimde eski stil metnini ekrana yazar.
Belirli öznitelikleri ve eski değeri izlemek
MDN'nin sohbet odası örneği attributeFilter ile yalnızca status ve username özniteliklerini izler, attributeOldValue ile de eski kullanıcı adını yakalar. attributeFilter verildiği için attributes seçeneği yazılmasa da varsayılan olarak true kabul edilir:
const observer = new MutationObserver((mutationList) => {
for (const mutation of mutationList) {
if (mutation.type !== "attributes") continue;
if (mutation.attributeName === "username") {
console.log(`Eski kullanıcı adı: ${mutation.oldValue}`);
}
}
});
observer.observe(document.querySelector("#user-list"), {
attributeFilter: ["status", "username"],
attributeOldValue: true,
subtree: true,
});subtree ile çıkarılan alt ağaçlar
subtree: true ile izlenen bir alt ağaçtan bir parça çıkarılırsa, çıkarma bildirimi teslim edilene kadar o parçadaki değişiklikler de raporlanmaya devam eder; bildirim teslim edildikten sonra ayrılmış parçadaki değişiklikler artık gözlemciyi tetiklemez. MDN bu davranışın bağlantının koptuğu an ile sizin yeni izleme başlatabileceğiniz an arasındaki değişiklikleri kaçırmamak için olduğunu açıklar ve şu çıktıyı gösterir:
const target = document.getElementById("target");
const child = document.getElementById("child");
const observer = new MutationObserver((mutations) => {
mutations.forEach((mutation) => {
console.log(mutation.type, mutation.target.id, mutation.attributeName);
if (mutation.type === "childList" && mutation.target.id === "target") {
child.setAttribute("data-bar", "");
}
});
});
observer.observe(target, { attributes: true, childList: true, subtree: true });
target.removeChild(child);
child.setAttribute("data-foo", "");
// Çıktı (MDN):
// childList target null
// attributes child data-foo
// "attributes child data-bar" bildirimi gelmez.<div id="target">
<div id="child"></div>
</div>disconnect ve takeRecords
disconnect() gözlemciyi durdurur; aynı gözlemci daha sonra observe() ile yeniden kullanılabilir. Önemli ayrıntı: MDN'ye göre algılanmış ama henüz geri çağırmaya teslim edilmemiş bütün bildirimler disconnect() ile atılır. Bunları kaybetmek istemiyorsanız önce takeRecords() çağırın; bu yöntem bekleyen kayıtları dizi olarak döndürür ve kuyruğu boşaltır:
const bekleyenler = observer.takeRecords();
observer.disconnect();
if (bekleyenler.length > 0) {
callback(bekleyenler); // aynı işleyiciyle son kayıtları da işle
}İzlenen eleman DOM'dan çıkarılıp çöp toplayıcı tarafından serbest bırakılırsa gözlemci o elemanı izlemeyi bırakır, ama gözlemcinin kendisi diğer elemanları izlemek için var olmaya devam edebilir. Yani tek bir elemanın kaldırılması, gözlemcinin başka yerlerde kurduğu izlemeleri etkilemez; hepsini bırakmak için yine disconnect() gerekir.
Mini örnek: liste boşalınca gözlemi bırakmak
MDN'nin yapıcı sayfasındaki örnek, iki gözlemci yönteminin birlikte nasıl kullanıldığını iyi gösterir. Bir listeye düğmeyle öğe eklenir ve çıkarılır; gözlemci her eklemeyi ve çıkarmayı bir günlüğe yazar, liste tamamen boşaldığında da kendini durdurur. Aşağıdaki sürüm aynı mantığı Türkçe iletilerle kurar:
<button id="ekle" type="button">Öğe ekle</button>
<button id="sil" type="button">Öğe sil</button>
<ul id="kap"></ul>
<pre id="gunluk"></pre>const kap = document.querySelector("#kap");
const gunluk = document.querySelector("#gunluk");
let sayac = 0;
document.querySelector("#ekle").addEventListener("click", () => {
const li = document.createElement("li");
li.textContent = `öğe ${sayac++}`;
kap.appendChild(li);
});
document.querySelector("#sil").addEventListener("click", () => {
kap.querySelector("li")?.remove();
});
const izleyici = new MutationObserver((kayitlar, observer) => {
for (const kayit of kayitlar) {
for (const dugum of kayit.addedNodes) {
gunluk.textContent = `Eklendi: ${dugum.textContent}\n${gunluk.textContent}`;
}
for (const dugum of kayit.removedNodes) {
gunluk.textContent = `Çıkarıldı: ${dugum.textContent}\n${gunluk.textContent}`;
}
if (kayit.target.childNodes.length === 0) {
gunluk.textContent = `Gözlem durduruldu\n${gunluk.textContent}`;
observer.disconnect();
}
}
});
izleyici.observe(kap, { childList: true, subtree: true });Dikkat edilecek iki nokta var. Birincisi, çıkarılan düğüm artık belgede olmasa bile removedNodes içinde gelir ve textContent değeri okunabilir. İkincisi, geri çağırmanın ikinci parametresi olan gözlemci, dış değişkene ihtiyaç duymadan disconnect() çağırmayı sağlar. Bir öğe ekleyip silmek tek bir kayıtta değil, iki ayrı childList kaydında raporlanır; hangisinin ne olduğunu addedNodes ve removedNodes uzunluklarından anlarsınız. Gözlem durduktan sonra yeni öğe eklemek günlüğe hiçbir şey yazmaz; yeniden izlemek için observe() çağrısını tekrarlamanız gerekir.
Üç gözlemcinin karşılaştırması

| Gözlemci | Cevapladığı soru | Girdi türü | Ayar nerede |
|---|---|---|---|
| IntersectionObserver | Eleman kök alanında ne kadar görünüyor? | IntersectionObserverEntry | Yapıcıda (sonradan değişmez) |
| ResizeObserver | Elemanın kutusu hangi boyuta geldi? | ResizeObserverEntry | observe() içinde box |
| MutationObserver | DOM ağacında ne değişti? | MutationRecord | observe() içinde seçenekler |
Sık yapılan hatalar
contentBoxSizedeğerini nesne sanmak. Dizi olduğu içincontentBoxSize[0].inlineSizeşeklinde okunur.- Geri çağırmada izlenen elemanın boyutunu koşulsuz değiştirmek. Döngü hatasına yol açar; beklenen boyutu saklayın veya değişikliği
requestAnimationFrameile erteleyin. - MutationObserver'a boş ayar vermek. En az bir izleme türü açık olmalı, yoksa
TypeErroralırsınız. - Gözlemcinin kendi yaptığı değişikliği yeniden işlemek. MutationObserver geri çağırmasında izlenen ağacı değiştirirseniz bu da yeni kayıt üretir; bir bayrak veya kontrol ile kendi değişikliğinizi ayırın.
disconnect()öncesi bekleyen kayıtları unutmak. Gerekirse öncetakeRecords()çağırın.- Her şey için
subtree: trueve tüm öznitelikleri açmak. Gereksiz kayıt üretir;attributeFilterile daraltın.
Sık Sorulan Sorular
ResizeObserver ile window resize olayı arasındaki fark nedir?
resize olayı pencerenin boyutu değiştiğinde çalışır. ResizeObserver ise tek tek elemanların içerik veya kenarlık kutusunu izler; MDN'nin örneğinde bir kaydırıcıyla kutunun genişliği değiştirildiğinde pencere aynı kalsa bile geri çağırma çalışır.
MutationObserver geri çağırması ne zaman çalışır?
Kayıtlar bir kuyrukta toplanır ve geri çağırmaya dizi halinde teslim edilir; bu yüzden aynı anda yapılan birçok değişikliği tek çağrıda görürsünüz. Tarayıcıda işlerin hangi sırayla çalıştığına dair genel resim için olay döngüsü yazısına bakın.
SVG elemanları izlenebilir mi?
Evet. ResizeObserver bir Element'in içerik veya kenarlık kutusunu ya da bir SVGElement'in sınır kutusunu izler; SVG hedefinde contentRect SVG'nin sınır kutusunu verir.
Kendi bileşenimdeki öznitelik değişikliğini başka yolla izleyebilir miyim?
Özel elemanlarda observedAttributes ve attributeChangedCallback bu iş için tasarlanmıştır; konu Web Components yazısında anlatılıyor. Başkasının oluşturduğu DOM'u izlemek gerektiğinde MutationObserver uygundur.
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 · Intersection Observer · requestAnimationFrame · DOM ağacında gezinme · DOM eleman oluşturma · Web Components