Geschrieben von

Single Page Applications und Tracking

Webtracking

Was sind Single Page Applications?

Ein klassische Website besteht aus mehreren HTML-Dokumenten, die untereinander verlinkt sind. Ruft der Nutzer die erste Seite auf, dann sendet der Browser eine Anfrage an den Server. Der Server antwortet mit dem HTML-Code, die der Browser dem Nutzer dann darstellen kann. Besucht der selbe Nutzer die nächste Seite geht es wieder von vorne los. Der Browser sendet die Anfrage an den Server, der mit dem angefragten HTML-Dokument antwortet. Bei vielen Anfragen zur gleichen Zeit, muss der Server einiges an Arbeit leisten. Hier kommen Single Page Applications (SPAs) ins Spiel.

Bei einer Single Page Application wird nur der erste Aufruf vom Server beantwortet. Der Browser bekommt ein einziges HTML-Dokument und alle anderen Ressourcen schon bei der ersten Antwort geliefert. Werden neue Daten benötigt, dann werden diese einfach dynamisch nachgeladen. Inhalte, die zunächst ausgeblendet waren werden einfach eingeblendet oder im es werden im Hintergrund neue Daten geladen. Bei letzterem muss nicht das komplette HTML-Dokument mit allen CSS-Dateien nochmal geladen werden, sondern nur der kleine Teil der Daten, der aktuell benötigt wird. Man kennt das z.B. von einem Feed wie bei Facebook. Scrollt man runter, dann werden nur die weiteren Beiträge nachgeladen, alles andere bleibt bestehen.

Es wird also alles clientseitig verarbeitet. Die Client-Server-Kommunikation und der Server werden dabei entlastet. Einige SPAs können sogar offline weiterhin benutzt werden. Dabei werden die Daten im Browser – genauer im Web-Storage – zwischengespeichert. Bei einer Trennung der Internetverbindung kann der Browser auf den Zwischenspeicher zugreifen.

Herausforderung für das Tracking

Da SPAs nur auf der Einstiegsseite einen “echten” Pageload erzeugen, muss man das beim Tracking mit Google Analytics und Google Tag Manager berücksichtigen. Beim ersten Pageload werden die JavaScript-Bibliotheken von analytics.js, gtag.js und gtm.js geladen. Durch den Pageload werden die Seitenaufrufe erfasst. Navigiert der Nutzer auf der SPA weiter, wird in der Regel kein Pagereload erzeugt. Entsprechend können die Tracking-Skripte nicht neu angestoßen werden, damit der Seitenaufruf getrackt wird. Zumindest können dies die Skripte nicht standardmäßig.

Ausnahme mit Google Analytics 4?

Wie in meinem Beitrag “Google Analytics 4: Events” erklärt, können innerhalb der Google Analytics 4-Benutzeroberfläche unter “Optimierte Analysen” Events definiert werden, die automatisch mit dem GA4-Skript getrackt werden sollen.

Wenn du bei “Optimierte Analysen” den Bereich “Erweiterte Einstellungen einblenden” unter “Seitenaufrufe” anklickst, dann kannst du mit folgender Option einstellen, ob “Seitenänderungen basierend auf Ereignissen im Browserverlauf” mitgetrackt werden sollten. Dabei handelt es sich um Screenviews in SPA. Man aktiviert dadurch den History Change Trigger, den man aus dem Google Tag Manager kennt.

Diese Out-of-the-Box Lösung sollte dennoch gut getestet werden, ob sie auch wie erwartet bei allen SPAs funktioniert!

Wie erkenne ich, dass ich es mit einer SPA zu tun habe?

Mit folgendem Test kannst du das einfach herausfinden:

  1. Öffne die Seite im Browser
  2. Wechsle in die Entwicklertools (in der Regel mit F12)
  3. Tab “Network” aufrufen und nach “Doc” filtern (siehe Screenshot unten)
  4. Seite neu laden
  5. Jetzt musst du sehen, dass sich die untere Tabelle mit Daten füllt
  6. Nun navigiere zur nächsten Seite (bzw. zum nächsten View)

Wenn beim Seiten- bzw. Screen-Wechsel die untere Tabelle kurz gelöscht und dann wieder beladen wird, dann handelt es sich um eine “normale” Website. Wenn sich die untere Tabelle beim Seiten- bzw. Screen-Wechsel nicht kurz löscht, sondern einfach mit weiteren Zeilen aufgefüllt wird, dann handelt es sich um ein dynamisches Nachladen und entsprechend um eine SPA.

Hier noch der von oben genannte Screenshot:

Was man beim Tracking von SPAs beachten sollte

Folgende Tracking-Themen sind bei Single Page Applications relevant:

  • Routing
  • URL-Varianten zusammenfassen
  • URL-Tracking ohne Routing
  • Referrer-Problematik

Routing

Beim Routing geht es darum, dass man URLs je Screenview ausspielt. Das ist nicht nur für das Tracking relevant, sondern für weitere Aspekte. Neben SEO können somit auch URLs besser geteilt werden. Der Hintergrund ist folgender:

  • Der Nutzer lädt die Seite und landet auf der Startseite domain.com
  • Auf der Seite gibt es eine Navigation mit “Über uns” und “Blog”
  • Der Nutzer klickt auf den Blog und kommt auch drauf
  • Nun werden ihm alle Blog-Einträge angezeigt
  • Die URL bleibt jedoch die gleiche: domain.com

Wenn der Nutzer nun den Blog teilen möchte, dann kann er zwar die URL aus der Adressleiste kopieren und weiterleiten. Der Nutzer, der den geteilten Links aufruft, landet aber auf der Startseite und nicht auf den Blog. Wir müssen also sicherstellen, dass die URL mitaktualisiert wird. In der Vergangenheit konnte man URLs nur ändern, indem man window.location geändert hat. Dies führt jedoch dazu, dass ein Pagereload erzeugt wurde, den man ja bei SPAs eigentlich nicht haben möchte. Dadurch werden wieder alle Ressourcen von Server angefordert. Stattdessen hat man sich lange Zeit eines Tricks bedient. Man hat zwar window.location aktualisiert, jedoch mit einem Hash (#). Aus domain.com wurde dann domain.com#blog. Das hat die URL geändert und keinen Pagereload erzeugt, da der Hash (Fragment) immer dazu gedacht war innerhalb eines Dokuments zu navigieren (als Sprungmarke).

Das ist bei SPAs auch heutzutage ein gängiger Weg URLs darzustellen. Die aus SEO- und Tracking-Sicht bessere Option ist der Einsatz der History API. Die History API wurde mit HTML5 eingeführt und bietet 2 Methoden, um die URL zu aktualisieren: pushState() und replaceState(). Während pushState() einen neuen History-Eintrag erzeugt, verändert replaceState() einen bestehenden Eintrag. Navigiert der Nutzer z.B. von der Startseite zum Blog, dann sollte man mit der pushState-Methode die URL aktualisieren. Die Methode nimmt 3 Parameter entgegen, wobei der letzte Parameter insbesondere für die URL-Änderung relevant ist:

pushState('', '', '/blog/');

Wie die History API im Detail funktioniert und implementiert werden kann, erfährst du hier.

Warum ist das für Tracking relevant? Ist Frontend-seitig alles korrekt implementiert, kannst du z.B. im Google Tag Manager auf die Änderungen im History-Eintrag mit dem Trigger “Verlaufsänderung” reagieren und so virtuelle Seitenaufrufe nach Google Analytics schicken (Link zur Anleitung).

URL-Varianten zusammenfassen

Falls deine SPA kein Routing über die History API einsetzt, sondern den Weg über Hashes (z.B. domain.com/#/ueber/) geht, kann es passieren, dass die gleiche Ressource über mehrere URLs hinweg erreichbar ist:

  • domain.com/#/ueber
  • domain.com/ueber
  • domain.com/index.html#ueber

Werden alle URLs einzeln erfasst, hat man in den Reports je URL einen Eintrag, was die Auswertung erschwert. Deshalb sollte man sich hier nur für eine Variante entscheiden und diese als “page”- oder “page_path”-Dimension an Google Analytics schicken.

URL-Tracking ohne Routing

Gibt es auf der SPA kein Routing, dann muss man sich an SPA-Events dranhängen, die einen neuen Screenview herbeiführen. So kann man z.B. den Klick auf das Menü als Trigger nehmen, um einen virtuellen Seitenaufruf nach Google Analytics zu schicken. Besser ist es jedoch auf Events des jeweiligen SPA-Frameworks zu achten, um dann darauf basierend einen Seitenaufruf zu erzeugen.

Referrer-Problematik

Um die Referrer-Problematik zu beleuchten, ist es wichtig grundlegend zu verstehen, wie Google Analytics die Traffic-Quellen zuweist. Schaue dazu bei meinem Beitrag “Google Analytics: Traffic-Zuordnung” vorbei.

Grob zusammenfassend – und für den nachfolgenden Case vereinfachend – kann man sagen, dass Google Analytics zunächst prüft, ob Kampagnen-Daten vorhanden sind (über UTMs) und sich erst dann den document.referrer anschaut (in Wirklichkeit ist es aber etwas komplexer – siehe im verlinkten Beitrag).

Gehen wir von diesem einfach Modell aus, dann erfolgt die Traffic-Quellen-Zuordnung wie folgt:

  • Kommt der Nutzer auf die Website und sind UTMs in der URL, dann werden die Daten zu Quelle und Medium aus den UTM-Angaben gezogen und an Google Analytics geschickt.
  • Gibt es keine UTMs, dann schaut sich Google Analytics den document.referrer an und schickt diese Quelle an Google Analytics (mit ein paar Ausnahmen).
  • Sind weder UTMs noch document.referrer vorhanden, fließt der Traffic in “Direct” rein.

Bei einer klassischen Website hätten wir also folgendes Bild (Erklärungen direkt unter der Grafik):

Erklärung:

  • Der Nutzer ist zunächst auf webiste.com unterwegs und klickt dann auf einen Link, der ihn zu domain.com/kontakt bringt.
  • Landet der Nutzer auf domain.com/kontakt, dann beginnt Google Analytics mit der Zählung der Sitzung.
  • Die Frage ist nun, welcher Quelle die Sitzung zugewiesen wird. Würde der Nutzer auf die Seite mit UTMs landen – z.B. domain.com/kontakt?utm_source=link&utm_medium=partner&utm_campaign=sale – dann würde Google Analytics die Infos aus dem UTMs heranziehen.
  • Sind keine UTMs vorhanden, holt sich Google Analytics die Info aus dem document.referrer. Im obigen Screenshot wäre die Quelle ein Verweis von “website.com”.

Bewegt sich der Nutzer nun weiter auf domain.com, dann ist im document.referrer jedes Mal die vorherige Seite – also domain.com – im Referrer zu finden. Google Analytics schickt aber hier den document.referrer nicht mit, da sich die Domain des Referrers von der Domain aus document.location.href nicht unterscheidet. Außer man schickt das Feld “alwaysSendReferrer” mit, dann wird jedes Mal der Referrer mitgeschickt. Jedoch muss man hier auch gleichzeitig sicherstellen, dass die eigene Domain in der Verweis-Ausschlussliste eingetragen ist. Ansonsten hat man das Problem, dass aufgrund der Änderung der Quelle eine neue Sitzung mit der neuen Quelle gestartet wird.

Während also bei einer klassischen Website alle Seitenaufrufe die vorherige URL als Referrer haben, bleibt bei SPAs der ursprüngliche Referrer erhalten. Hier wie eine SPA mit dem Referrer umgeht:

Würde hier der Nutzer auf die Zielseite mit UTMs – also domain.com/kontakt?utm_source=link&utm_medium=partner&utm_campaign=sale – landen, dann würde die Sitzung mit der Quelle aus den UTMs starten. Klickt der Nutzer nun auf einen Link, dann fordert der Browser im Hintergrund die Daten vom Server an. Der Server antwortet und der Browser aktualisiert den Seiteninhalt ohne dass die Seite einen Pagereload erzeugt. Die URL ändert sich jedoch mit und der History API-Trigger löst aus. Da der Referrer unverändert bleibt (fehlender Pagereload) ist dieser weiterhin auf “website.com” gesetzt. Und da nun die Domain in document.referrer und document.location.href nicht übereinstimmen, sendet Google Analytics der document.referrer mit. Dies führt dazu, dass eine neue Sitzung mit der neuen Quelle gestartet wird.

Das Problem ist hier, dass Traffic und Conversions falsch attribuiert werden (können).

Als Lösung könnte man nun verhindern, dass der Referrer (Google-Analytics-Feld: dr) nach dem ersten Hit an Google Analytics mitgeschickt wird. Oder man speichert auf der Landingpage den location-Wert und schickt diesen dann bei jedem darauffolgenden GA-Hit als location-Feld mit. Zusätzlich wird noch das page-Feld für den virtuellen Pageview mitgeschickt. Simo hat dazu eine Vorlage “Persist Campaign Data On The Landing Page” erstellt.