Tracking

Um Klicks, Besuche, Engagement oder andere Metriken über Ihre Nutzer:innen zu sammeln, können externe Analyse-Systeme angebunden werden. Außerdem liefern die Publikationen Custom Events, auf die gelauscht werden kann.


Integration von Analyse-Systemen

Die Integration erfolgt über den Tab Scripts in der Bearbeitungsmaske einer Publikation.

Es gibt die Möglichkeit, sowohl externe Scripte als auch Inline-Javascript zu integrieren. Das Javascript wird im <head> der Seite eingebunden. Jedes Inline-Javascript wird in einem eigenen <script>-Tag integriert, für den jeweils zusätzliche Attribute hinterlegt werden können.

Für externe Scripte stehen folgende Einstellungsmöglichkeiten zur Verfügung:

  • Source: Das src-Attribut
  • Load method: sync, async, defer oder preload
  • Position: Auswahl, ob das Script vor oder nach den Inline-Scripts geladen werden soll
  • Attributes: Möglichkeit, dem <script>-Tag weitere Attribute hinzuzufügen

Konfiguration eines externen Scripts mit Source, Lademethode, Position und Attributen
Konfiguration eines externen Scripts mit Source, Lademethode, Position und Attributen

Integration am Beispiel von Google Analytics 4

Google Analytics bietet die Möglichkeit, das Tracking via Google Tag Manager oder direkt über das Google-Tag einzubinden.

Google Tag Manager

Für den Google Tag Manager wird kein externes Script benötigt. Das im Google Tag Manager hinterlegte Script kann direkt inline integriert werden.

Inline-Script mit dem Google Tag Manager Code-Snippet
Inline-Script mit dem Google Tag Manager Code-Snippet

Wichtig ist, dass das dataLayer-Objekt vor der Integration des Google Tag Managers definiert wird. Der Code kann direkt im selben oder einem eigenen Code-Block integriert werden.

JavaScript
window.dataLayer = window.dataLayer || [];

Separater Inline-Script-Block für die dataLayer-Initialisierung vor dem Tag Manager
Separater Inline-Script-Block für die dataLayer-Initialisierung vor dem Tag Manager

Damit ist die Integration abgeschlossen. Alle weiteren Einstellungen werden im Google Tag Manager durchgeführt.

Google Tag

Für die direkte Einbindung des Google-Tag muss zunächst ein externes Script für die Bibliothek angelegt werden. Folgende Angaben sind erforderlich:

  • Source: https://www.googletagmanager.com/gtag/js?id=[G-ID]
  • Load method: async
  • Position: „Before inline script"

Achten Sie darauf, dass als Position „Before inline script" ausgewählt wird, da der dataLayer nach der Einbindung des Google-Tag definiert wird.

JavaScript
window.dataLayer = window.dataLayer || [];

Die Integration ist damit abgeschlossen. Für die Übergabe von Events oder anderen Daten kann das dataLayer-Objekt genutzt werden.

Integration am Beispiel von Adobe Analytics

Für die Integration von Adobe Analytics ist die Einbindung des Tags-Script Voraussetzung. Diese erfolgt mithilfe eines externen Scripts innerhalb einer Publikation. Die Attribute sollten wie folgt ausgefüllt werden:

  • Source: https://assets.adobedtm.com/.../launch-[ID].min.js
  • Load method: async
  • Position: „After inline script"

In diesem Fall muss „After inline script" ausgewählt werden. Dies ermöglicht die Übergabe von Objekten an den Adobe Data Layer.

Der Data Layer wird als Inline-Script integriert:

JavaScript
window.adobeDataLayer = window.adobeDataLayer || [];

Inline-Script zur Initialisierung des Adobe Data Layers
Inline-Script zur Initialisierung des Adobe Data Layers

Die Integration ist damit abgeschlossen. Für die Übergabe von Events oder anderen Daten kann der Adobe Data Layer verwendet werden.

Integration am Beispiel der IVW

Bevor Seitenaufrufe gezählt werden können, ist es erforderlich, den INFOnline Measurement Manager zu implementieren. Hierfür stellt die IVW zwei Methoden zur Verfügung:

Mit Preload und Bundle-Loader

Integrieren Sie zunächst die bundle.js als externes Script mit der Methode preload. Erstellen Sie die Attribute as="script" und crossorigin.

Externes Script für die IVW bundle.js mit Preload-Methode
Externes Script für die IVW bundle.js mit Preload-Methode

Die Methode preload generiert keinen <script>-Tag, sondern einen <link>-Tag:

HTML
<link rel="preload" href="//[domain service name]/iomm/latest/manager/base/es6/bundle.js" as="script" crossorigin>

Im nächsten Schritt wird die loader.js ebenfalls mit der Methode preload und denselben Attributen implementiert.

Externes Script für die IVW loader.js mit Preload-Methode
Externes Script für die IVW loader.js mit Preload-Methode

Zuletzt muss die loader.js synchron geladen werden.

Externes Script für die IVW loader.js mit synchroner Lademethode
Externes Script für die IVW loader.js mit synchroner Lademethode

Wenn Sie alle vorangegangenen Einstellungen korrekt implementiert haben, erzeugt dies folgende Ausgabe:

HTML
<link rel="preload" href="//[domain service name]/iomm/latest/manager/base/es6/bundle.js" as="script" crossorigin>

<link rel="preload" href="//[domain service name]/iomm/latest/manager/base/es6/loader.js" as="script" crossorigin>

<script type="text/javascript" src="https://[domain service name]/iomm/latest/bootstrap/loader.js" crossorigin></script>

Nachdem die initialen Scripte implementiert sind, kann die Konfiguration und die eigentliche Zählung stattfinden. Hierzu verwenden Sie ein Inline-Script:

JavaScript
IOMm("configure", { st: "foo", dn: "data-acbd18db4c.example.com" }); // Configure IOMm
IOMm("pageview", { cp: "bar", co: "baz" }); // Count pageview

Inline-Script mit IVW-Konfiguration und Pageview-Event
Inline-Script mit IVW-Konfiguration und Pageview-Event

Damit ist die Integration abgeschlossen. Sollten Sie neue Zählcodes verwenden, müssen diese bei der IVW zugeordnet werden, damit die Messungen korrekt durchgeführt werden.

Ohne Preload und Bundle-Loader

Die Variante ohne Preload und Bundle-Loader unterscheidet sich vor allem in der Reihenfolge der auszuführenden Code-Blöcke.

Initial wird zunächst die stub.js synchron geladen. Hierbei ist zu beachten, dass das externe Script vor dem Inline-Javascript positioniert werden muss.

Externes Script für die IVW stub.js mit synchroner Lademethode, positioniert vor dem Inline-Script
Externes Script für die IVW stub.js mit synchroner Lademethode, positioniert vor dem Inline-Script

Im Nachgang erfolgt die Konfiguration Ihrer Zählung und das Pageview-Event:

JavaScript
IOMm("configure", { st: "foo", dn: "data-acbd18db4c.example.com" }); // Configure IOMm
IOMm("pageview", { cp: "bar", co: "baz" }); // Count pageview

Inline-Script mit IVW-Konfiguration und Pageview-Zählung
Inline-Script mit IVW-Konfiguration und Pageview-Zählung

Im letzten Schritt muss die bundle.js asynchron geladen werden. Hier ist zu beachten, dass das externe Script nach dem Inline-Javascript positioniert wird.

Externes Script für die IVW bundle.js mit async-Lademethode, positioniert nach dem Inline-Script
Externes Script für die IVW bundle.js mit async-Lademethode, positioniert nach dem Inline-Script

Wenn Sie alle vorangegangenen Einstellungen korrekt implementiert haben, erzeugt dies folgende Ausgabe:

HTML
<script type="text/javascript" src="https://[domain service name]/iomm/latest/bootstrap/stub.js" crossorigin></script>

<script type="text/javascript">
IOMm("configure", { st: "foo", dn: "data-acbd18db4c.example.com" }); // Configure IOMm
IOMm("pageview", { cp: "bar", co: "baz" }); // Count pageview
</script>

<script async type="text/javascript" src="https://[domain service name]/iomm/latest/manager/base/es5/bundle.js" crossorigin></script>

Custom Events

Wenn Nutzer:innen mit Ihrer Publikation interagieren, werden Custom Events ausgelöst, die von Tracking-Systemen entgegengenommen und analysiert werden können.

Custom Events werden wie folgt getriggert:

JavaScript
window.dispatchEvent(new CustomEvent(EventAction, {
    detail: EventValue,
}));

EventValue ist ein Objekt, das wiederum Objekte, Strings oder Integer-Werte enthalten kann.

Die Events teilen sich in zwei Kategorien auf:

  • Allgemeine Events — werden in jeder Publikation gefeuert, unabhängig vom Rätseltyp.
  • Rätselspezifische Events — gelten nur für das jeweilige Spiel.

Allgemeine Events

Diese Events sind in allen Publikationen verfügbar:

EventAction Interaktion EventValue
PageView Beim Aufruf der Publikation und bei jedem weiteren Seitenaufruf darin (virtuelle Page-Impression).
JSON
{
  "from": "Object",
  "to": "Object"
}
Details siehe Virtuelle Seitenaufrufe.
Auth Sobald ein(e) Nutzer:in sich über die SSO erfolgreich authentifiziert. –
PaywallTriggered Ein Inhalt hinter der Paywall wurde aufgerufen. Das Event wird bei allen Paywall-Varianten gesendet, auch wenn die Publikation die Paywall selbst anzeigt.
JSON
{
  "state": "String",
  "trigger": "String",
  "path": "String",
  ...
}
Details siehe Paywall-Kontakte.
ShareResult Spiel-Ergebnis wird über die Teilen-Funktion (z. B. WebShare-API) geteilt. Rätselspezifischer Payload — siehe jeweilige Tabelle.
CopyResult Spiel-Ergebnis wird in die Zwischenablage kopiert. –
ClickOtherGame Klick auf eine verlinkte andere Publikation (z. B. im Footer).
JSON
{
  "game": "String"
}
Name der Publikation.
SwitchSetup Eine Einstellung (z. B. der schwierige Modus) wurde umgeschaltet.
JSON
{
  "setup": "String",
  "value": "Boolean | String"
}
setup z. B. hardMode oder wordlist_sort. value ist ein Boolean bei An/Aus-Einstellungen, sonst ein String bei mehrwertigen Einstellungen (z. B. Sortier-Modus newest / alpha / points).
UseHeaderIcon Ein Icon im Header (Hilfe, Statistiken, Login) wurde angeklickt.
JSON
{
  "icon": "String"
}
z. B. help, stats, auth.
UseOffCanvasMenuItem Ein Eintrag im Off-Canvas-Menü wurde ausgewählt.
JSON
{
  "item": "String"
}
Titel des Eintrags.

Worteck

Zusätzlich zu den Allgemeinen Events:

EventAction Interaktion EventValue
GameStarted Wird einmalig ausgelöst, wenn ein(e) Nutzer:in zum ersten Mal mit dem Rätsel interagiert. Bei einem Neustart oder der Wiederaufnahme eines bereits begonnenen Spiels wird das Event nicht erneut gefeuert.
JSON
{
  "game_nr": "Integer"
}
GameSucceeded Ein Wort wurde erfolgreich erraten.
JSON
{
  "game_nr": "Integer",
  "game_success_row": "Integer"
}
GameFinished Ein Spiel wurde beendet, unabhängig davon ob das Wort erraten wurde oder nicht.
JSON
{
  "game_nr": "Integer",
  "game_success_row": "Integer"
}
GameFailed Ein Spiel wurde beendet, aber das Wort nicht erraten.
JSON
{
  "game_nr": "Integer",
  "game_success_row": "Integer"
}
RowCompleted Eine Reihe wurde vervollständigt (Wort ist valide und existiert).
JSON
{
  "game_nr": "Integer",
  "game_current_row": "Integer"
}
NewGame Nach Beendigung eines Spiels wird ein neues gestartet (nur wenn mehr als ein Wort pro Tag möglich ist).
JSON
{
  "game_nr": "Integer",
  "remaining_games": "Integer"
}
AllGamesCompleted Ein(e) Nutzer:in hat alle zur Verfügung gestellten Worte zu einem Datum beendet.
JSON
{
  "date": "String"
}
Format YYYY-MM-DD.
ShareResult Payload für das Allgemeine Event.
JSON
{
  "game_nr": "Integer",
  "game_success_row": "Integer"
}

Sudoku

Zusätzlich zu den Allgemeinen Events:

EventAction Interaktion EventValue
GameStarted Wird einmalig ausgelöst, wenn ein(e) Nutzer:in zum ersten Mal mit dem Rätsel interagiert. Bei einem Neustart oder der Wiederaufnahme eines bereits begonnenen Spiels wird das Event nicht erneut gefeuert.
JSON
{
  "game_nr": "Integer"
}
GameFinished Ein Sudoku wurde erfolgreich beendet.
JSON
{
  "game_nr": "Integer",
  "timer": "Integer"
}
timer in Sekunden.
GameFailed Alle Zahlen wurden ausgefüllt, aber es sind noch Fehler enthalten.
JSON
{
  "game_nr": "Integer",
  "timer": "Integer"
}
timer in Sekunden.
RestartGame Ein Sudoku wurde zurückgesetzt und erneut begonnen.
JSON
{
  "game_nr": "Integer",
  "timer": "Integer"
}
timer = Zeitpunkt der Zurücksetzung in Sekunden.
PrintGame Ein Sudoku wurde gedruckt.
JSON
{
  "game_nr": "Integer"
}
ShareResult Payload für das Allgemeine Event.
JSON
{
  "game_nr": "Integer",
  "timer": "Integer"
}

Wortwabe

Zusätzlich zu den Allgemeinen Events:

EventAction Interaktion EventValue
GameStarted Wird einmalig ausgelöst, wenn ein(e) Nutzer:in zum ersten Mal mit dem Rätsel interagiert. Bei einem Neustart oder der Wiederaufnahme eines bereits begonnenen Spiels wird das Event nicht erneut gefeuert.
JSON
{
  "game_nr": "Integer"
}
GameFinished Ein Spiel wurde erfolgreich beendet.
JSON
{
  "game_nr": "Integer",
  "count_words": "Integer",
  "deviation": "Integer",
  "count_isograms": "Integer"
}
WordFound Ein Wort wurde entdeckt.
JSON
{
  "game_nr": "Integer",
  "word": "String",
  "is_isogram": "Boolean"
}
word in Großbuchstaben.
ShuffleLetters Die Buchstaben werden manuell gemischt.
JSON
{
  "game_nr": "Integer"
}
ExpandWordList Die Liste der gefundenen Wörter wurde aufgeklappt.
JSON
{
  "game_nr": "Integer"
}
ShareResult Payload für das Allgemeine Event.
JSON
{
  "game_nr": "Integer",
  "count_words": "Integer",
  "top_score": "Boolean",
  "count_isograms": "Integer"
}

Kreuzworträtsel

Zusätzlich zu den Allgemeinen Events:

EventAction Interaktion EventValue
GameStarted Wird einmalig ausgelöst, wenn ein(e) Nutzer:in zum ersten Mal mit dem Rätsel interagiert. Bei einem Neustart oder der Wiederaufnahme eines bereits begonnenen Spiels wird das Event nicht erneut gefeuert.
JSON
{
  "game_nr": "Integer"
}
GameFinished Das Kreuzworträtsel wurde erfolgreich beendet.
JSON
{
  "game_nr": "Integer",
  "timer": "Integer"
}
timer in Sekunden.
RestartGame Das Kreuzworträtsel wurde zurückgesetzt und erneut begonnen.
JSON
{
  "game_nr": "Integer",
  "timer": "Integer"
}
timer = Zeitpunkt der Zurücksetzung in Sekunden.
ShareResult Payload für das Allgemeine Event.
JSON
{
  "game_nr": "Integer",
  "errors": "Integer",
  "seconds": "Integer"
}

Kinonym

Zusätzlich zu den Allgemeinen Events:

EventAction Interaktion EventValue
GameStarted Wird einmalig ausgelöst, wenn ein(e) Nutzer:in zum ersten Mal mit dem Rätsel interagiert, also einen Begriff aufdeckt oder einen Titel rät. Bei der Wiederaufnahme eines bereits begonnenen Spiels wird das Event nicht erneut gefeuert.
JSON
{
  "game_nr": "Integer"
}
RevealClue Ein weiterer Begriff wurde aufgedeckt. revealed nennt die Anzahl der danach sichtbaren Begriffe.
JSON
{
  "game_nr": "Integer",
  "revealed": "Integer"
}
GameFinished Ein Spiel wurde beendet, unabhängig davon ob der Titel erraten wurde. won unterscheidet die beiden Fälle, earned nennt die erreichten Punkte (bei einer Niederlage 0), revealed die Anzahl der aufgedeckten Begriffe und wrong_guesses die Anzahl der Fehlversuche.
JSON
{
  "game_nr": "Integer",
  "won": "Boolean",
  "earned": "Integer",
  "revealed": "Integer",
  "wrong_guesses": "Integer"
}
GameFailed Ein Spiel wurde beendet, aber der Titel nicht erraten. Das schließt das Aufgeben ein. Der Payload entspricht GameFinished.
JSON
{
  "game_nr": "Integer",
  "won": "Boolean",
  "earned": "Integer",
  "revealed": "Integer",
  "wrong_guesses": "Integer"
}

Events im iFrame

Ist Ihre Publikation per Iframe oder Script eingebunden, endet ein Custom-Event an der Grenze des Iframes. Ihre Publikation reicht deshalb jedes der oben genannten Events per postMessage an die umgebende Seite weiter. Dort lassen sie sich an ein Analyse-System weiterleiten oder zur Steuerung einer Paywall nutzen.

Jede postMessage-Nachricht trägt drei Properties:

Property Bedeutung
source Immer oliwol. Daran erkennt Ihre Seite die Nachrichten der Publikation.
event Der Name des Events, etwa PageView oder PaywallTriggered.
detail Der Payload des Events, unverändert aus den Tabellen oben.

Auf Ihrer Seite nimmt ein EventListener die Nachrichten entgegen:

JavaScript
window.addEventListener('message', (event) => {
    if (event.origin !== 'https://sudoku.example.com') {
        return;
    }

    if (event.data?.source !== 'oliwol') {
        return;
    }

    console.log(event.data.event, event.data.detail);
}, false);

Virtuelle Seitenaufrufe

Da es sich bei allen Publikationen um Single Page Applications (SPA) handelt, wird beim Wechseln der URL kein Seiten-Reload ausgelöst. Einige Analyse-Systeme lauschen auf das popstate-Event und können das Navigieren innerhalb von SPAs tracken.

Alle Publikationen senden das Custom-Event PageView, sobald eine Seite aufgerufen wird. Im Payload werden die Properties to und from übergeben: to liefert die Daten zur angesteuerten Seite, from enthält die Daten zur Ausgangs-URL.

Auch der erste Aufruf der Publikation wird gemeldet. Dort ist from gleich null, denn es gibt keine Seite, von der aus er erreicht wurde. In einer Einbettung ist das der einzige Hinweis auf die Ankunft im Rätsel: Die umgebende Seite zählt ihren eigenen Aufruf, nicht den des Rahmens.

JavaScript
{
  to: {
    fullPath: "/",
    hash: "",
    name: "Home",
    params: {},
    path: "/",
    query: {}
  },
  from: null
}

Bei jedem weiteren Seitenaufruf tragen beide Properties eine Seite:

JavaScript
{
  to: {
    fullPath: "/schwierig",
    hash: "",
    name: "Home",
    params: {
        level: "schwierig"
    },
    path: "/schwierig",
    query: {}
  },
  from: {
    fullPath: "/",
    hash: "",
    name: "Home",
    params: {
        level: "leicht"
    },
    path: "/",
    query: {}
  }
}

Um die Daten beim Seitenwechsel an ein Analyse-System weiterzuleiten, können Sie auf das Custom-Event PageView lauschen und den Payload übergeben. Hierfür nutzen Sie ein Inline-Script und integrieren den EventListener.

Virtuelle Seitenaufrufe mit GA4

GA4 lauscht in der Standard-Konfiguration auf das popstate-Event – daher müssen virtuelle Seitenaufrufe nicht manuell übergeben werden.

GA4-Einstellung für erweiterte Analysen: Option zum automatischen Tracking von Seitenänderungen im Browser-Verlauf
GA4-Einstellung für erweiterte Analysen: Option zum automatischen Tracking von Seitenänderungen im Browser-Verlauf

Wenn Sie diese Option deaktivieren, können Seitenaufrufe wie folgt übergeben werden:

JavaScript
window.addEventListener("PageView", (e) => {
    gtag("event", "page_view", {
        page_title: document.title,
        page_location: e.detail.to.fullPath
    })
});

Virtuelle Seitenaufrufe mit Adobe Analytics

Für Adobe Analytics müssen Sie keinen EventListener im oliwol Publisher Tool integrieren. Das Regelset für Custom Events integrieren Sie direkt in Adobe Tags.

Virtuelle Seitenaufrufe mit IVW

JavaScript
window.addEventListener("PageView", (e) => {
    IOMm("pageview", { cp: "[code]" });
});

Weitere Anwendungsmöglichkeiten

Das Custom-Event PageView kann auch dazu genutzt werden, Werbemittel zu aktualisieren. Nachfolgend ein Beispiel, wie Werbemittel beim Wechseln der Seite aktualisiert werden, sofern es sich bei der Zielseite und der vorherigen Seite nicht um den Statistik-Layer handelt:

JavaScript
window.addEventListener("PageView", (e) => {
    if (e.detail && e.detail?.to?.name !== "Stats" && e.detail?.from?.name !== "Stats") {
        if (typeof OBR !== "") {
            OBR.extern.refreshWidget();
        }
    }
});

Paywall-Kontakte

Erreichen Nutzende einen Inhalt hinter der Paywall, wird das Custom-Event PaywallTriggered gesendet. Das geschieht bei allen Paywall-Varianten, also auch dann, wenn die Publikation die Paywall selbst anzeigt. Bei der Variante Individuell ist es zugleich das Signal, das eigene Angebot einzublenden.

Der Payload beschreibt die Situation, die zur Paywall geführt hat:

Property Immer enthalten Bedeutung
state ja Der Zustand, der die Paywall ausgelöst hat.
trigger ja Die Art des Auslösers: page, content, navigation, feature oder archive.
path ja Der Pfad, den Nutzende angesteuert haben.
type – Die Variante der Paywall: internal, piano oder custom.
paywall – Die Kennung der konfigurierten Paywall.
page – Der interne Name der betroffenen Seite.
title – Der Titel der betroffenen Seite.
feature – Der Schlüssel einer Funktion hinter der Paywall, etwa printing.
date – Der Tag eines archivierten Rätsels im Format JJJJ-MM-TT.

Die Auslöserarten im Einzelnen:

trigger Situation
page Eine geschützte Seite wurde aufgerufen.
content Ein geschützter Abschnitt innerhalb einer Seite wurde erreicht.
navigation Ein geschützter Eintrag in der Navigation wurde angeklickt.
feature Eine geschützte Funktion wurde benutzt.
archive Ein archiviertes Rätsel oder eine größere Archivtiefe wurde angesteuert.
JavaScript
window.addEventListener("PaywallTriggered", (event) => {
    const paywall = event.detail;

    if (paywall.trigger === "feature") {
        // Ein Angebot, das die Funktion hinter der Paywall benennt.
        console.log(paywall.feature);
    }
});

Der Payload enthält keine Angaben zur Person: weder eine Kennung noch den Anmeldestatus oder die Mitgliedschaft.