Przejdź do głównej zawartości

Analityka: eventy i GTM

OctaForms sam wysyła eventy analityczne do dataLayer. Nie wklejasz żadnego snippetu i niczego nie włączasz: jeśli na stronie działa Google Tag Manager, eventy już tam są. Twoja rola sprowadza się do zbudowania w GTM triggera i tagu GA4.

Strona opisuje kontrakt eventów w wersji 1 (octaFormsSchema: 1). Ten sam kontrakt, z tymi samymi nazwami i kluczami, obowiązuje we wszystkich produktach OctaForms, więc jeden kontener GTM obsługuje je wszystkie bez podwójnego liczenia.

Mechanizm ma dwie warstwy:

  1. Eventy DOM. Widget emituje zdarzenia CustomEvent w przestrzeni nazw octaforms:. Eventy formularza odpalają się na elemencie <form>, eventy widgetu kontaktowego na document. Wszystkie bąbelkują, więc nasłuch podpinasz zawsze na document, a e.target mówi, skąd event przyszedł.
  2. Mostek GTM. Wbudowany moduł nasłuchuje eventów DOM i tłumaczy je na dataLayer.push(). Tworzy dataLayer, jeśli go nie ma: to z definicji kolejka, więc pushe sprzed załadowania kontenera GTM zostaną przetworzone przy jego starcie. Działa to też przy kontenerach ładowanych z opóźnieniem (zgoda w CMP, optymalizacje szybkości strony).

Sam push jest bezczynny: to dopisanie obiektu do tablicy w pamięci przeglądarki. Nic nigdzie nie jest wysyłane, dopóki nie zbudujesz triggera w GTM. Payload nie zawiera żadnych danych osoby odwiedzającej: slug i location to Twoje etykiety, status to kod HTTP, a phone to numer telefonu właściciela strony pokazywany w widgecie.

Najwygodniejszy setup to jeden generyczny tag GA4, który przenosi nazwę eventu 1:1:

  1. Zmienne. Utwórz zmienne typu „Zmienna warstwy danych“ dla kluczy, których potrzebujesz, np. octaFormSlug, octaFormLocation, octaFormStatus.
  2. Trigger. Utwórz trigger typu „Zdarzenie niestandardowe“, nazwa zdarzenia octaforms_.*, z zaznaczoną opcją „Użyj dopasowania do wyrażeń regularnych“.
  3. Tag. Utwórz tag „Google Analytics: zdarzenie GA4“. W polu „Nazwa zdarzenia“ wstaw wbudowaną zmienną {{Event}}, w parametrach zdarzenia podepnij zmienne z kroku 1. Przypnij trigger z kroku 2.
  4. Konwersje. W GA4 (Administracja, „Kluczowe zdarzenia“) oznacz jako kluczowe np. octaforms_form_success i octaforms_phone_call.

Po publikacji kontenera każdy event z katalogu poniżej pojawi się w GA4 pod swoją nazwą, bez dalszej konfiguracji per event.

Jeśli Twój kontener GTM używa przemianowanej warstwy danych (inaczej niż standardowe dataLayer), podaj jej nazwę na skrypcie widgetu, inaczej eventy trafią do tablicy, której nikt nie czyta:

<script src="https://app.octaforms.pl/widget/v1/octaforms.js" data-gtm-layer="mojaWarstwa" async></script>

Konwersje przy przekierowaniu na stronę podziękowania

Dział zatytułowany „Konwersje przy przekierowaniu na stronę podziękowania”

Jeśli formularz ma ustawione przekierowanie po wysłaniu, event octaforms_form_success odpala się jeszcze przed opuszczeniem strony, a tag GA4 wysyła dane przez sendBeacon, który przeżywa przejście na kolejną stronę. W praktyce konwersja dolatuje.

Jest jednak jedno „ale“: jeśli kontener GTM w chwili wysyłki jeszcze nie wstał (typowo przez bramkę zgody w CMP), push czeka w kolejce dataLayer, a przekierowanie zabiera tę kolejkę razem ze stroną. Dlatego przy przekierowaniu najpewniejszym pomiarem konwersji jest odsłona samej strony podziękowania: trigger w GTM na jej adres nie ma z czym się ścigać. Event octaforms_form_success traktuj wtedy jako sygnał pomocniczy.

Żeby odróżnić prawdziwą konwersję od wejścia prosto na adres strony podziękowania, dopisz do adresu przekierowania własny znacznik, np. ?f={form_id}: dodajemy go tylko przy realnym przekierowaniu po zgłoszeniu, a mówi on, który formularz zadziałał, nie kto go wypełnił. Do triggera nie bierz dopisywanego id, bo to identyfikator konkretnego zgłoszenia (powody niżej, „Dlaczego eventy nie niosą identyfikatora zgłoszenia“). Adres strony podziękowania trafia do GA4 jako page_location, więc obowiązuje na nim to samo ostrzeżenie, co w Osadzaniu: żadnych danych osobowych.

Event w dataLayer Event DOM Kiedy się odpala
octaforms_form_start octaforms:form-start Pierwsza interakcja z formularzem (focus w polu), raz na zamontowaną instancję.
octaforms_form_submit octaforms:form-submit Kliknięcie „Wyślij“, po przejściu walidacji po stronie przeglądarki, przed wysyłką. Raz na gest wysłania.
octaforms_form_success octaforms:form-success Serwer przyjął zgłoszenie (odpowiedź 2xx). Emitowany przed przekierowaniem na stronę podziękowania, więc nie ginie.
octaforms_form_error octaforms:form-error Wysyłka zakończyła się ostatecznym błędem. octaFormStatus niesie kod HTTP, a przy błędzie sieci 0 z octaFormReason: "network".
octaforms_widget_open octaforms:widget-open Otwarcie okna pływającego widgetu kontaktowego (klik w przycisk lub dymek).
octaforms_tab_switch octaforms:tab-switch Zmiana zakładki w widgecie kontaktowym (telefon, oddzwonienie, wiadomość).
octaforms_phone_call octaforms:phone-call Klik w numer telefonu (link tel:) w widgecie kontaktowym.
octaforms_phone_copy octaforms:phone-copy Klik w „Kopiuj numer“; octaFormSuccess mówi, czy kopiowanie się udało.

Cztery eventy formularza układają się w lejek: form_start (ktoś zaczął wypełniać), form_submit (kliknął wyślij i przeszedł walidację), form_success albo form_error (rozstrzygnięcie). Różnica między form_start a form_submit to porzucenia w trakcie wypełniania, a między form_submit a form_success to problemy techniczne i odrzucenia serwera.

Te sytuacje nie emitują nic. To decyzje projektowe, dzięki którym dane w lejku są czyste:

  • Błędna walidacja w przeglądarce. Jeśli formularz nie przeszedł walidacji lokalnie i nic nie poleciało do serwera, nie ma ani form_submit, ani form_error. Gest, który nie wyszedł poza przeglądarkę, nie jest próbą wysyłki.
  • Ciche ponowienia. Widget potrafi sam ponowić wysyłkę (np. gdy zabezpieczenie antybotowe każe chwilę odczekać). Ponowienie nie emituje drugiego form_submit ani fantomowego form_error przed sukcesem.
  • Programowe ustawianie focusu. Zarządzanie focusem wewnątrz widgetu (np. pułapka focusu w oknie modalnym) nie odpala form_start. Event wymaga realnej interakcji w polu formularza.

Każdy push niesie komplet kluczy. Klucze nieużywane przez dany event mają jawnie wartość null: model danych GTM pamięta wartości między pushami, więc bez zerowania octaFormStatus z błędu przykleiłby się do następnego sukcesu.

Klucz Typ Znaczenie
octaFormSlug string | null Identyfikator formularza (ten sam, którego używasz w data-octa-form).
octaFormLocation string | null Twoja etykieta miejsca osadzenia (patrz niżej). null, gdy jej nie nadasz.
octaFormStatus number | null Kod HTTP błędu przy form_error (422, 429, 403…), 0 przy błędzie sieci.
octaFormReason string | null Doprecyzowanie błędu o statusie 0. W widgecie osadzanym zawsze "network".
octaFormTab string | null Zakładka widgetu kontaktowego (phone, callback, message). Przy widget_open to zakładka startowa.
octaFormPhone string | null Numer telefonu pokazywany w widgecie (numer właściciela strony, nie gościa).
octaFormTrigger string | null Skąd padł klik w numer. W widgecie osadzanym: "widget".
octaFormSuccess boolean | null Wynik kopiowania numeru przy phone_copy.
octaFormsSchema number Wersja kształtu danych. Obecnie 1. Rośnie tylko przy zmianie łamiącej.

Ten sam formularz często stoi w kilku miejscach: w stopce, na stronie kontaktu, w wyskakującym oknie. Żeby rozróżnić je w raportach, nadaj każdemu miejscu własną etykietę:

<div data-octa-form="ID_FORMULARZA" data-octa-location="stopka"></div>
<a href="#" data-octa-trigger="ID_FORMULARZA" data-octa-location="baner-glowna">Napisz do nas</a>
<script src="https://app.octaforms.pl/widget/v1/octaforms.js"
data-octa-widget="ID_WIDGETU" data-octa-location="sluchawka" async></script>

Etykieta trafia do każdego eventu z tego miejsca jako octaFormLocation (w DOM: detail.location). Przy montowaniu programowym przekaż ją w opcjach:

OctaForms.render('#kontakt', 'ID_FORMULARZA', { location: 'strona-kontakt' });

Formularze wewnątrz pływającego widgetu kontaktowego dziedziczą etykietę z elementu data-octa-widget.

Jeśli nie używasz GTM albo chcesz zareagować na event własnym kodem, słuchaj bezpośrednio na document. Eventy to standardowe CustomEvent z danymi w e.detail:

document.addEventListener('octaforms:form-success', (e) => {
console.log('Lead wysłany', e.detail.slug, e.detail.location);
});

Nasłuch możesz podpiąć w dowolnym momencie, także przed załadowaniem skryptu widgetu: listener na document nie zależy od kolejności ładowania.

Event Pola w detail
octaforms:form-start slug, location
octaforms:form-submit slug, location
octaforms:form-success slug, location
octaforms:form-error slug, location, status (number), reason? ("network")
octaforms:widget-open location, mode (zakładka startowa), widgetId
octaforms:tab-switch location, tab, widgetId
octaforms:phone-call location, phone, trigger, widgetId
octaforms:phone-copy location, phone, success (boolean), widgetId

widgetId to pole dodatkowe widgetu osadzanego (identyfikator z data-octa-widget). Mostek GTM go nie przenosi; jest dostępne tylko w nasłuchu DOM.

  • Eventy formularza odpalają się na elemencie <form class="octa-forms"> i bąbelkują. Dzięki temu w nasłuchu na document odróżnisz instancje po e.target, np. gdy dwa formularze stoją na jednej stronie.
  • Eventy widgetu kontaktowego odpalają się na document (dotyczą elementu pływającego, nie fragmentu treści strony).
  • Formularz otwierany w oknie modalnym renderuje się w <body>, ale jego eventy również bąbelkują do document. Nic nie renderuje się w shadow DOM.

Jeżeli na Twojej stronie działa jeszcze ręcznie wklejony snippet przepisujący eventy OctaForms do dataLayer (starsza wersja tej instrukcji), usuń go. Wbudowany mostek robi to samo automatycznie, a dwa mostki naraz oznaczają podwójne liczenie konwersji.

Wbudowany mostek zabezpiecza się flagą window.octaFormsGtmBridge: pierwszy mostek na stronie ustawia flagę, każdy kolejny widzi ją i nie podpina nasłuchu. Chroni to przed duplikatami, gdy na jednej stronie działa kilka produktów OctaForms. Stare ręczne snippety nie znają tej flagi, dlatego trzeba je usunąć ręcznie.

Push do dataLayer jest domyślnie włączony i nie ma przełącznika w panelu (nie ma czego wyłączać: bez triggera w GTM dane nigdzie nie płyną). Jeśli mimo to chcesz go wyłączyć, masz dwie równoważne drogi:

<!-- 1. Atrybut na skrypcie widgetu -->
<script src="https://app.octaforms.pl/widget/v1/octaforms.js" data-gtm="off" async></script>
<!-- 2. Zmienna globalna ustawiona PRZED załadowaniem skryptu -->
<script>window.octaFormsGtm = false;</script>
<script src="https://app.octaforms.pl/widget/v1/octaforms.js" async></script>

Wyłączenie dotyczy tylko mostka GTM. Eventy DOM (octaforms:*) emitowane są zawsze, więc własny nasłuch dalej działa.

W payloadach celowo nie ma identyfikatora leada i prosimy, żeby go nie dodawać po swojej stronie:

  • Warstwa analityczna opisuje interakcję, nie rekord. Do pracy na rekordach służą webhooki i API.
  • Zgłoszenia odfiltrowane jako spam dostają odpowiedź nieodróżnialną od sukcesu (patrz wyżej). Identyfikator w takiej odpowiedzi byłby częściowo szumem, którego nie da się odróżnić od prawdziwych identyfikatorów, a próba jego pominięcia zdradzałaby botom, że zostały wykryte.
  • Dzięki temu payload nie zawiera żadnego klucza łączącego dane analityczne z konkretnym zgłoszeniem osoby, co upraszcza Ci analizę zgodności (RODO).

Ta sama zasada dotyczy adresu strony podziękowania. Do przekierowania dopisujemy id zgłoszenia z myślą o Twoim własnym kodzie na tej stronie (np. dociągnięciu zgłoszenia przez API), a nie o analityce. Jeśli mierzysz konwersje odsłoną strony podziękowania, jej adres trafia do GA4 razem z tym parametrem, więc rozważ przyjęcie zgłoszenia pod adresem bez id albo usunięcie parametru z adresu po odczytaniu go w JavaScripcie.

Push do dataLayer nie zapisuje niczego na urządzeniu gościa, nie wysyła żadnego żądania sieciowego i nie niesie danych odwiedzającego. To, czy i kiedy dane trafią do GA4 lub innych narzędzi, kontrolujesz w GTM: tam działa Consent Mode i tam podpinasz swój CMP. OctaForms nie obchodzi tych mechanizmów, bo niczego nie wysyła samodzielnie.

Nazwy eventów, nazwy kluczy i ich typy to stabilny kontrakt (wersja 1). Zmiany łamiące podniosą wartość octaFormsSchema, więc możesz zbudować w GTM trigger blokujący nieznaną wersję, jeśli Twoje tagi są wrażliwe na kształt danych. Nowe eventy i nowe klucze mogą dochodzić w ramach tej samej wersji.