İçeriğe geç
ceaksan

MutationObserver Ne Zaman Tracking'i Kurtarır, Ne Zaman Kırar

Lazy-load JS, deferred GTM, Partytown ve consent gating tracking'i kırıyor. MutationObserver hangi durumda bridge, hangi durumda yetersiz, ne zaman server-side gerekir.

12 May 2026 7 dk okuma
TL;DR

PageSpeed için yapılan dört yaygın hamle (lazy-load app, deferred GTM, Partytown, consent gating) tracking veri akışını ya kazara ya bilerek kırar. MutationObserver sadece lazy-inject widget yakalama (Pattern 1) için güvenilir köprü; thread izolasyonu ya da zamanlama sorunlarında işe yaramaz. Conversion attribution, cross-domain ve post-purchase event'ler için server-side pipeline tek tutarlı çözüm.

PageSpeed için yapılan dört yaygın hamle, lazy-load app script’leri, deferred GTM, Partytown web worker offload ve consent gating, tracking veri akışını ya kazara ya bilerek kırar. MutationObserver bu denklemde tek başına çözüm değil; bir senaryoda güvenilir köprü, başka bir senaryoda ise tam tersine bloklama silahı. Hangi tarafta hangi işi yaptığını bilmek, “PageSpeed kazandım, satışın yarısını kaybettim” yanılgısından çıkışın anahtarı.

Tracking’i Kazara Kıran Üç Performans Pattern’i

Önce sorunun çıktısı: e-ticaret panelinde sipariş normal görünüyor, Meta Ads Manager yarısını gösteriyor.1 GA4 user analytics çalışıyor ama purchase event’i hiç gelmiyor.2 Performans eklentisi devre dışı bırakıldığında ya da Partytown migration’ı geri alındığında otuz dakika içinde event’ler geri dönüyor.3 Üç farklı kaynaktan gelen aynı şikâyet, üç farklı pattern’in sonucu.

Pattern 1 — App ve widget lazy-load

Ortalama bir Shopify veya WooCommerce mağazasında 15-20 app yüklü, bunların 5-10 tanesi frontend’e script enjekte ediyor. Her app 100-500KB ekliyor. Üçüncü taraf JS, toplam script execution süresinin %60-80’ini oluşturuyor.4 Performans ekibi bu yükü düşürmek için widget’ların lazy-load olmasını sağlıyor: form, popup ya da chat DOM’a ancak kullanıcı etkileşimiyle düşüyor.

Sorun: tracking event’i form submit anında yakalanmak istendiğinde, form henüz DOM’da yok. Klaviyo, Hotjar, Privy gibi vendor’ların success state’i sonradan render ettiği durumlarda dataLayer’a haber gitmiyor. Bu pattern MutationObserver’in gerçekten iş gördüğü tek yer.

Pattern 2 — Deferred GTM ve Delay JavaScript

WP Rocket’ın “Delay JavaScript Execution” özelliği, Perfmatters’ın “Delay JavaScript” ayarı, SpeedyCache’in “Delay JS” seçeneği, üçüncü taraf script’lerini ilk kullanıcı etkileşimine kadar erteler. Üç plugin de aynı pattern’i kullanır, üçü de tracking üzerindeki etkisini kendi dokümantasyonunda kabul eder. WP Rocket: “Google Analytics ve Google Ads tracking verinizde sorun yaşıyorsanız, Delay JavaScript Execution seçeneği etkilemiş olabilir.5 SpeedyCache aynı şeyi açıkça söyler: “Delay JS, Analytics script’ini geciktirmeyi seçtiğin durumda analytics’i de etkiler; sitenle etkileşmeyen kullanıcıları ya da bot’ları saymaz.6 Perfmatters dokümanı google-analytics.com/analytics.js, gtag(, /gtm.js, /busting/facebook-tracking/ gibi tag string’lerini delay listesine eklemek için adım adım talimat verir, ardından “tracking script’lerinde veri doğruluğunu artırmak için” exclude listesini önerir.7 Vendor kendi öneri zincirini “delay et, sonra tracking için exclude et” şeklinde kuruyor.

Bu pattern plugin’lere de bağımlı değil. Aynı mantık birkaç satırlık vanilla JS ile elle de kurulabilir: <script type="text/plain" data-src="..."> etiketleri ilk scroll, mousemove, touchstart ya da keydown event’inde aktif edilir. Plugin’ler arka planda tam olarak bu mekanizmayı paketliyor; el yapımı versiyonun aynı kırılma riskleri taşıdığını söylemek için ek kanıt gerekmiyor.

Pratik etkisi GitHub issue’larında belgelenmiş. WP Rocket #7296’da kullanıcının ilk click’i geciktirilen JS’in arkasında kuyruğa giriyor, handler tetiklenmiyor.8 WP Rocket #3088’de Pixel Caffeine’in init ve track event’leri kırılıyor.9 Pageview event’i tetiklenmeden önce sayfa terk edilirse o etkileşim hiç kaydedilmiyor; conversion attribution sessizce kayboluyor. Bu sorun MutationObserver ile çözülmüyor çünkü problem DOM mutasyonu değil, timing.

Pattern 3 — Partytown ve Web Worker offload

Partytown, Qwik tarafından geliştirilen ve Shopify Hydrogen cookbook’unda resmi yer bulan bir third-party script offload aracı. Tracking script’lerini main thread’den alıp bir Web Worker sandbox’ına taşıyor. Tasarım kararı net: ana thread, worker’a doğrudan erişemiyor.

Sonuç olarak gtag() veya dataLayer.push() çağrıları varsayılan kurulumda Worker’a iletilmiyor. fatbobman’ın 2025-12 tarihli snippet’inde belirtildiği gibi: “Ana thread, Partytown içinde çalışan gtag veya dataLayer.push fonksiyonlarına doğrudan ulaşma yeteneğini kaybeder.”10 Forwarding’i config içinde elle açmadıysan, purchase gibi server için kritik event’ler yere düşer. Partytown #519’da user analytics çalışıyor ama purchase event’i kayboluyor.11 #210’da iOS GA4 sadece initial load’u logluyor; geri kalan etkileşimler boşa düşüyor.12

Resmi Partytown dokümantasyonunun da kabul ettiği bir mimari sınır var: “Partytown, Shopify Web Pixels içinde çalışamaz çünkü Web Pixels, üst frame’e erişimi olmayan sandboxed bir iframe ortamıdır.”13 Yani Shopify’da klasik GTM kurulumu Partytown ile birlikte yaşıyor olsa da, Web Pixels API üzerinden gelen event’ler bu yola hiç giremiyor.

KVKK ve GDPR uyumluluğu için kullanılan consent management platform’lar (Cookiebot, Secure Privacy, consentmanager.net, Axeptio, OneTrust) tracking’i bilerek bloklar. Mekanizma: tracking script’i <head>’e enjekte edildiği anda bir gözlemci tarafından yakalanıp, onay yoksa silinir.

Secure Privacy’nin kendi açıklaması: “Çoğu site, _consent banner yüklenmesini bitirmeden önce analitik ve reklam cookie’lerini düşürür, bu doğrudan bir GDPR ihlalidir.14 Cookiebot belgelerinde aynı şey _vendor tarafından da kabul ediliyor: “Consent Management Platform, tag’leri bloklayabilir.”15 consentmanager.net “automatic blocking” özelliğini bu mantık üzerine kurmuş.16

Pattern 2 tracking’i kazara durdurur, Pattern 4 bilinçli durdurur. Sonuç aynı: tracking script’i ana akışa giremez. Aradaki tek fark, niyet.

MutationObserver: Aynı API, İki Kamp

// Camp 1 — lazy-load widget üzerinde tracking'i kurtar
new MutationObserver((muts) =>
  muts.forEach((m) =>
    m.addedNodes.forEach((n) => {
      if (n.matches?.(".newsletter-form-success"))
        dataLayer.push({ event: "newsletter_signup" });
    }),
  ),
).observe(document.body, { childList: true, subtree: true });

// Camp 2 — onay öncesi tracking'i bloklayan
new MutationObserver((muts) =>
  muts.forEach((m) =>
    m.addedNodes.forEach((n) => {
      if (n.tagName === "SCRIPT" && n.src?.includes("googletagmanager.com")) {
        n.dataset.blockedSrc = n.src;
        n.type = "text/plain";
      }
    }),
  ),
).observe(document, { childList: true, subtree: true });

Birinde “veriyi kurtar”, diğerinde “veriyi engelle”. jsdev.space’in 2025 tarihli guide’ı Camp 1 (DOM tracking, lazy widget yakalama) için referans kalıbı.17 Camp 2 (consent öncesi script bloklama) Cookient, ConsentStack ve Signatu vendor docs’larında explicit kod örneğiyle anlatılıyor; üçü de aynı endüstri konvansiyonunu önerir: script tag’i node.remove() ile silmek yerine type attribute’ünü text/plain yapmak. Browser unrecognized MIME type’ı çalıştırmaz, ek olarak src prefetch’i de tetiklenmez; eski remove() yaklaşımının tarayıcı race condition sorunundan kaçınır.181920 CMP teknik dokümantasyonları (Secure Privacy, Cookiebot, consentmanager.net) Camp 2 versiyonunu “automatic blocking” olarak isimlendiriyor.

Tezim açık: client-side bridge ve client-side blocker’ı bir arada uzun vadeli sürdürmek mümkün değil. Kazandığın şey her iki tarafta da kırılgan; consent yönetimi bir page reload’da geri geliyor, bridge her vendor güncellemesinde yeniden test gerektiriyor. Tek tutarlı çözüm server-side.

MutationObserver Bridge Ne Zaman Yeter

Sadece Pattern 1’de güvenilir. Lazy-inject olan bir widget DOM’a düştüğünde, MutationObserver bunu yakalar ve dataLayer’a push eder. Pattern bilinen, kod kısa, sürdürülmesi makul. Simo Ahava’nın 2014’te yayınladığı GTM DOM Listener kalıbı hâlâ geçerli.21

Ama Simo’nun aynı yazısındaki disclaimer’a kulak vermek gerekir: “Bu tarz hack’lerle hatalı markup’i veya derme çatma bir tag implementation’ı düzeltmeye karşı oldukça güçlü hislerim var. Geliştiricilerinizle konuşup GTM’in standart özellikleri ile çalışan bir çözüm üretmenizi şiddetle öneririm.”21 2014’te söylenmiş bu cümle, on iki yıl sonra hâlâ aynı işaret fişeği.

Pattern 2 ve 3’te MutationObserver yardımcı olmuyor:

  • Deferred JS sorunu DOM mutasyonu değil, timing. Script kuyrukta beklerken DOM’a hiçbir değişiklik gelmiyor, gözlemcinin tetikleneceği bir olay yok.
  • Partytown sorunu thread izolasyonu. Worker içindeki event ana thread’e dönmüyor; ana thread’deki MutationObserver, Worker içindeki olayları göremiyor.

Yanlış araçla doğru sorun çözülmez.

GTM Element Visibility Trigger: “Daha Hafif Çözüm” Tuzağı

Bazı kaynaklar SPA tracking için “GTM Element Visibility + MutationObserver birlikte kullan” öneriyor. Bu önerinin iki sorunu var.

Birincisi redundant. GTM Element Visibility trigger’i IntersectionObserver tabanlı, scroll listener’a göre tarayıcıda daha hafif çalışıyor. “Observe DOM changes” kutucuğunu işaretlersen GTM zaten arka planda kendi MutationObserver’ını kuruyor. Üstüne kendi MutationObserver’ını eklemek, aynı işi iki defa yapmak.

İkincisi gizli performans cezası. Simo Ahava’nın belirttiği gibi: “GTM, izlemek istediğin her element için bir timer yönetmek zorunda; tek elementi Element ID yöntemiyle takip etmek, CSS selector ile tanımlanmış bir grup elementi takip etmekten daha iyi performans verir.22 CSS selector geniş yazılırsa veya çok element izlenirse, kurtarmak istediğin tracking için yeni bir script time faturası geliyor. Tradeoff’tan kaçış yok, sadece tradeoff’u bilinçli yönetmek var.

Server-Side Pipeline Ne Zaman Şart

Üç senaryoda client-side MutationObserver bridge bile yetmez:

  1. Conversion attribution, pixel client-side fire olamadığında. Cookieless tarayıcı, ad blocker, ITP gibi durumlarda server kayıt geçmesi gerekiyor.
  2. Cross-domain session, user_id’nin client’tan client’a güvenilir taşınmadığı senaryolar. Checkout subdomain’i, üçüncü parti ödeme sayfaları.
  3. Post-purchase ve async event’ler, Shopify webhook’u ya da Stripe customer.subscription.updated gibi backend event’lerin client-side karşılığı yok. Kullanıcı sayfada değil, olay yine olmuş.

Bu noktada Scout gibi bir server-side event pipeline gerekiyor. Scout, ekibimle geliştirdiğim event router; Shopify webhook’larından Stripe lifecycle’ına kadar backend event’leri client-side’a düşürmeden tek noktadan toplar ve hedef destinasyonlara iletir. Stape, server-side GTM (kendi-host veya yönetilen) ya da Segment gibi alternatifler de aynı sınıfta. Hangi araç olursa olsun mantık aynı: kritik event’i tarayıcıdan çıkar.

Karşılaştırma Tablosu

PatternSorun neredeMutationObserver yardım eder miKalıcı çözüm
1. Widget lazy-loadDOM zamanlamasıEvet, bridge işe yararVendor callback veya server-side webhook
2. Deferred GTM / Delay JSTiming, kuyrukHayırCritical event’leri server-side taşı veya nowprocket exclude
3. Partytown Web WorkerThread izolasyonuHayırExplicit forward config + server-side yedek
4. Consent gatingBilinçli bloklamaAPI’nin ters kullanımı (blocker)Server-side + consent mode uyumlu pipeline

Üç sütundan bakınca tablonun ortak cevabı belli: MutationObserver bir Pattern’in tek senaryosunda işe yarıyor, üç Pattern’de yetmiyor, dört Pattern’in ortak kalıcı çözümü server-side.

Kaynakça

Footnotes

  1. Shopify Community, “Anyone else seeing missing conversions in Ads Manager?”, thread #579010.
  2. Partytown GitHub Issue #519, “GA Events not fired”, QwikDev/partytown.
  3. Partytown GitHub Issue #210, “Pageview tracking broken on iOS”, QwikDev/partytown.
  4. Thunder Page Speed, “Shopify Third-Party Scripts: The Hidden Speed Killer”.
  5. WP Rocket Docs, “Google Analytics / Google Ads tracking issues”, article 1492.
  6. SpeedyCache Docs, “How to Delay JS Until User Interaction”, speedycache.com/docs/file-optimization/how-to-delay-js-until-user-interaction.
  7. Perfmatters Docs, “Delay JavaScript”, perfmatters.io/docs/delay-javascript.
  8. WP Rocket GitHub Issue #7296, “Delay JS interrupts user click”.
  9. WP Rocket GitHub Issue #3088, “Pixel Caffeine breaking with Delay JS”.
  10. fatbobman, “How to Forward Custom Tag Events to GTM Running Inside Partytown”, 2025-12.
  11. Partytown GitHub Issue #519, QwikDev/partytown.
  12. Partytown GitHub Issue #210, QwikDev/partytown.
  13. Partytown Docs, “Shopify OS2”, partytown.qwik.dev/shopify-os2.
  14. Secure Privacy KB, “Complete Guide to Blocking Cookies for GDPR Compliance Prior Consent Script Load”.
  15. Cookiebot Support, “A Consent Management Platform (CMP) may be blocking tags”, article 23551330842268.
  16. consentmanager.net, “Automatic blocking of codes and cookies”.
  17. jsdev.space, “MutationObserver DOM Tracking Guide”, 2025-04.
  18. Cookient, “Why 90% of Cookie Banners Don’t Actually Block Tracking”, 2026-01, cookient.app/blog/why-90-percent-cookie-banners-dont-block-tracking.
  19. ConsentStack, “How Cookie Consent Script Blocking Actually Works”, 2025-11, consentstack.io/blog/how-script-blocking-works.
  20. Signatu Docs, “Blocking Strategies”, signatu.com/docs/guides/blocking.
  21. Simo Ahava, “Google Tag Manager DOM Listener”, 2014. 2
  22. Simo Ahava, “Element Visibility Trigger in Google Tag Manager”.
Önemli Noktalar
  • 01 Lazy-load app script, deferred GTM, Partytown, consent gating, dördü de tracking'i farklı şekilde durdurur
  • 02 Vendor docs (WP Rocket, Partytown, Secure Privacy) bu davranışları kendileri belgelemiş
  • 03 MutationObserver iki kampta da silah: tracking kurtaran bridge, tracking engelleyen blocker
  • 04 GTM Element Visibility yalnız başına çözüm değil, sadece daha hafif tradeoff
  • 05 Conversion attribution, cross-domain, post-purchase için server-side pipeline şart
Sık Sorulan Sorular (FAQ)
+ GTM'i defer edersem GA4 verim azalır mı?

Evet ölçülebilir oranda. Sayfayı 3 saniyeden önce terk eden ziyaretçilerin pageview'ı kaydolmaz. WP Rocket ve Perfmatters resmi docs'unda da yazılı.

+ Partytown ile GA4 kullanmak güvenli mi?

Sadece event forwarding'i config'de explicit yaptıysan. Default kurulumda purchase gibi server-relevant event'ler ana thread'den Worker'a geçmiyor. Issue #519 dahil çoklu rapor var.

+ MutationObserver hangi durumda işe yarar?

Sadece lazy-inject olan widget DOM'a düştüğünde event yakalamak için. Deferred script veya Web Worker izolasyonu sorununu çözmez. Aynı API consent yönetiminde tersine, tracking script'lerini engellemek için kullanılır.

+ GTM Element Visibility Trigger çözüm değil mi?

IntersectionObserver bazlı, scroll listener'a göre daha hafif. Ama CSS selector geniş yazılırsa veya çok element izlenirse her birine timer açılıyor. Tradeoff'tan kaçış yok.

+ Tüm bu sorunların kalıcı çözümü ne?

Kritik conversion event'lerini server-side pipeline'a taşımak. Scout, Stape, sGTM kendi-host gibi seçenekler var. Client-side tek başına 2026'da yeterli değil.