JavaScript’te MutationObserver ile DOM Değişikliklerini İzleme: Widget ve Eklentiler İçin Pratik Rehber
Dinamik sayfalarda DOM değişikliklerini MutationObserver ile yakalayıp güvenli ve performanslı şekilde tepki verin.
Modern web sayfaları (SPA’ler, CMS’ler, A/B test araçları) DOM’u sürekli değiştirir. Bir widget geliştiriyorsanız “buton sonradan eklendi, event bağlanmadı” ya da “render tekrarlandı, iki kez çalıştı” gibi sorunlar kaçınılmazdır. Bu noktada MutationObserver, DOM’daki değişimleri izleyip doğru anda aksiyon almanızı sağlar.
MutationObserver ne işe yarar?
- Yeni eklenen/çıkarılan elementleri yakalar
- Attribute değişikliklerini izler (ör.
data-*,class) - Metin değişikliklerini (characterData) takip edebilir
Kullanım alanları:
- Üçüncü parti script/widget entegrasyonları
- Sonsuz scroll ile gelen içerikte “yeni kartlara etiket basma”
- CMS üzerinde sonradan yüklenen formları otomatik zenginleştirme
Temel kullanım
Aşağıdaki örnek, #feed içine sonradan eklenen .product-card öğelerini bulup bir kez “işaretler”:
const feed = document.querySelector('#feed');
function enhanceCard(card) {
if (card.dataset.enhanced === '1') return; // idempotent
card.dataset.enhanced = '1';
// Örnek: karta rozet ekle
const badge = document.createElement('span');
badge.className = 'badge';
badge.textContent = 'Yeni';
card.prepend(badge);
}
const observer = new MutationObserver((mutations) => {
for (const m of mutations) {
// childList: eklenen/çıkarılan düğümler
for (const node of m.addedNodes) {
if (!(node instanceof Element)) continue;
// Node’un kendisi kart olabilir
if (node.matches('.product-card')) enhanceCard(node);
// Ya da içinde kartlar olabilir
node.querySelectorAll?.('.product-card').forEach(enhanceCard);
}
}
});
observer.observe(feed, {
childList: true,
subtree: true
});
Neden dataset.enhanced?
MutationObserver aynı element için birden fazla tetiklenebilir (yeniden render, taşıma, vs.). Bu küçük “idempotent” kontrol, iki kez rozet basma gibi hataları engeller.
Attribute izleme: class/data değişince tepki ver
Örneğin bir modal açıldığında body’ye .modal-open ekleniyor olabilir. Bu durumu dinleyip scroll kilidi ya da ölçüm alabilirsiniz:
const obs = new MutationObserver((mutations) => {
for (const m of mutations) {
if (m.type === 'attributes' && m.attributeName === 'class') {
const isOpen = document.body.classList.contains('modal-open');
console.log('Modal durumu:', isOpen);
}
}
});
obs.observe(document.body, {
attributes: true,
attributeFilter: ['class']
});
Performans ipuçları (kritik)
MutationObserver güçlüdür; yanlış kullanılırsa gereksiz iş üretir.
- İzleme kapsamını daraltın:
documentyerine mümkünse hedef container. attributeFilterkullanın: Her attribute değişimini dinlemeyin.- Batch işlem yapın: Çok sayıda mutation gelirse tek seferde işleyin.
Basit bir “batch” örneği:
let pending = new Set();
let scheduled = false;
function scheduleFlush() {
if (scheduled) return;
scheduled = true;
queueMicrotask(() => {
scheduled = false;
for (const el of pending) enhanceCard(el);
pending.clear();
});
}
const observer2 = new MutationObserver((mutations) => {
for (const m of mutations) {
for (const node of m.addedNodes) {
if (!(node instanceof Element)) continue;
node.querySelectorAll?.('.product-card').forEach((el) => pending.add(el));
}
}
scheduleFlush();
});
Ne zaman kullanmamalı?
- Kendi kodunuz elementleri ekliyorsa, doğrudan o noktada fonksiyon çağırmak daha net olabilir.
- Sürekli değişen devasa DOM alanlarında (ör. çok yoğun animasyon/virtual list) maliyet artabilir.
Sonuç
MutationObserver, özellikle kontrol etmediğiniz DOM değişimlerine uyum sağlamak için ideal: eklenti, widget ve dinamik içerik senaryolarında “sonradan gelen element” problemini temizce çözer. Doğru scope ve idempotent yaklaşım ile hem sağlam hem de performanslı bir çözüm elde edersiniz.