
Вимірюємо взаємодію зі сторінкою правильно: час на активній вкладці + скролінг
Назва цієї статті – відверта відсилка до старої доброї статті сенсея.
Сама ідея проста і витончена – ми відправляємо івент engagement (назва може бути будь-якою) в GA4 в момент, коли юзер провів на активній вкладці Х часу і проскролив сторінку вниз до Y %. В Universal Analytics цей метод допомагав правильно виміряти показник відмов, особливо для односторінкових сайтів. GA4, у свою чергу, покращив показник взаємодії і не вважає відмовою відвідування однієї сторінки, якщо людина провела на ній більше 10 секунд. Технічно у стандартних звітах у пріоритеті показник залучення, а не відмов, але це просто дзеркально протилежні показники. GA4 також “подбав” про те, що якщо для вас 10 секунд – замалий термін, щоб сказати, що юзер свідомо провзаємодіяв з контентом (насправді це замало для всіх), то ви можете змінити цей час в налаштуваннях. Але все одно це налаштування відноситься до рівня сесії, і цей показник не скаже, з якою саме сторінкою “якісно” провзаємодіяв користувач. Тому, спосіб, описаний нижче, допоможе зрозуміти кількість “якісних” взаємодій окремо для кожної сторінки. Критерії якості взаємодії визначаєте ви, задаючи відсоток вертикального скролу і мінімальний час, який юзер має провести на цій сторінці.
Для повноти контексту дуже раджу перечитати оригінальну статтю, а потім повернутися сюди. Первинне рішення досі робоче, і ви можете його використати, якщо у вас сайт побудований не за технологією SPA, і на ньому присутня бібліотека jQuery. В свою чергу метою написання цієї статті стала адаптація підходу, щоб його можна було використати на будь-якому сайті, оскільки jQuery використовується все рідше, а SPA сайтів стає все більше. Одним з таких є сайт вашої улюбленої академії. Логіка ідеї збережена повністю, і цей варіант має працювати на всіх без винятку сайтах. Якщо у вас не запрацювало, пишіть мені в коментарях.
SPA (single page application) – фактично додаток у браузері. На відміну від “звичайних” сайтів, він підвантажує контент на фоні. При переходах на інші “сторінки” не відбувається їхнього стандартного завантаження. Через це, для GTM ви наче залишаєтесь на тій же сторінці. Переходи між сторінками він може “побачити” через тригер зміни історії, але це не допоможе правильно відпрацьовувати тригеру скроллу. Далі в статті я поясню чому.
Окрім того, ця стаття має на меті показати, як можна адаптувати рішення в інтернеті під свою задачу, якщо по якійсь причині це рішення вам не підходить. При тому для такої адаптації не потрібно бути розробником, достатньо знати базові речі і вміти пояснити чату GPT, що вам потрібно. Це рішення передбачає написання певних функції на JavaScript, але не лякайтесь. Я покажу вам, що це не складно, а після проходження PRO GTM – взагалі доволі просто. Найскладнішу частину ми делегуємо якраз чату.
План сьогоднішньої статті:
Детальніше про мету налаштування
На початку я вже трохи писала, що саме дозволяє відстежити це налаштування. Оригінальна ідея була про правильний показник відмов в Universal Analytics. Але в GA4 визначення відмови або сесії із взаємодією дещо відрізняється, а саме сеансом із взаємодією вважається сеанс, коли людина проглянула дві і більше сторінок або виконала ключовий івент, або тривалість сеансу була більша, ніж кількість часу в Adjust timer for engaged sessions.
В свою чергу наше налаштування не має на меті впливати на показник відмов. Його мета інша – зрозуміти чи взаємодіють з нашим контентом, і якщо взаємодіють, то з яким саме.
Насправді, GA4 зробив великий крок вперед у відстеженні взаємодій з сайтом. Автоматично відстежуються такі події, як user_engagement та scroll. Давайте про ці івенти детальніше:
user_engagement– автоматична подія, яка відправляється при вивантаженні сторінки. Є і інші моменти, але зараз не суть. Вивантаження відбувається, коли ви або закриваєте вкладку / браузер, або коли ви на “звичайному” сайті переходите на іншу сторінку. Довідкаscroll– ще одна автоматична подія, яка відправляється, коли юзер доскролює сторінку вниз до 90%. І ще одна довідка.
Як на мене, жоден з цих івентів не вказує на фактичну взаємодію з контентом – user_engagement може відправлятись, навіть коли людина не дивилась далі початкового екрану сторінки, а scroll на 90% може спрацювати, просто коли людина швидко і не задумуючись прогортала сторінку в самий низ.
У випадку цього налаштування ми можемо задати критерій спрацювання при одночасному виконанні двох умов – в моєму випадку я вважатиму взаємодією перебування на активній вкладці 40 секунд і скрол до половини сторінки.
Скролінг на 50%
Якщо у вас “звичайний” сайт, вам достатньо налаштувати тригер один в один, як в оригінальній статті.
З SPA не вийде скористатись вбудованим тригером, оскільки він запускає прослуховування скролу тільки на трьох івентах:

Всі три спрацьовують в момент завантаження сторінки. Для SPA за класикою це відбувається один раз – при заході на сайт, але не при переході між сторінками. Переходи між сторінками як раз і може “побачити” тригер історії і перезапустити прослуховування скролу, але, як бачите, в переліку його немає. А перезапуск прослуховування потрібен, оскільки спрацьовує такий тригер тільки один раз після завантаження сторінки. Розглянемо кейс:
- Ви налаштували тригер скролу на 50%.
- Людина заходить на головну. Скролить її до середини – спрацьовує тригер скролу – відправляється івент в аналітику.
- Людина переходить на другу сторінку. Перезавантаження, а отже оновлення прослуховування скролу не відбувається. Людина скролить цю сторінку до середини – тригер не спрацьовує – івент на другій сторінці не відправляється, бо тригер вже спрацював на попередній сторінці.
Ще один кейс:
- Ви налаштували тригер скролу на 50%.
- Людина заходить на головну. Не скролить її, а з кнопки меню переходить на другу сторінку, і тут вже скролить до середини – спрацьовує тригер скролу – відправляється івент в аналітику.
- Людина переходить на третю сторінку і скролить її теж до середини – тригер не спрацьовує – івент на третій сторінці не відправляється, тому що тригер вже спрацював на попередній сторінці.
Що ж робити? Писати власний слухач на JavaScript… Це саме той момент, коли мені на допомогу прийде GPT.
В мене немає глибоких знань JS, але для подібної задачі достатньо розуміти його синтаксис. А розуміти його потрібно, тому що ніхто не ідеальний, навіть GPT теж може помилятись. Але зі свого досвіду можу сказати, що кількість його помилок прямо пропорційна якості моїх запитів. Тому чим краще ви описуєте задачу, тим більше вірогідність, що чат вам зможе допомогти.
Після попереднього абзацу вам може здатись, що я – експертний промт-інженер. І це справді так, оскільки мій промт навіть з lavascripot (бо писалось це в неділю ввечері) чат одразу зрозумів саме так, як я просила, правда не зовсім так, як я думала :)

GPT поки не вміє читати думки, тому достатньо було уточнити, що мені треба не будь-який скрол, а тільки до 50%, щоб чат видав мені робоче рішення:

Звісно, з коробки воно не заведеться, і GTM покаже вам таке:

Але це не тому, що код не робочий. Причина в тому, що GTM просто не сприймає ключові слова для оголошення змінних let i const. Тому в коді їх треба замінити на var. І це ще не все:
- Чат нам люб’язно пропонує код в коментарі, який буде відправляти івент в GA4 через функцію gtag, і за замовчуванням буде писати в консоль, що юзер доскролив до 50%. Звісно, що в цьому немає необхідності, бо ми налаштовуємо передачу в dataLayer, але потрібно розуміти де в коді повинно бути те, що генерує певну команду при досягненні цілі. І там, де “Trigger any action”, замість console.log і коментарів про gtag, я додаю звичний dataLayer.push з назвою потрібного мені івенту –
scroll_to_50_pct. - Ще однією модифікацією буде запис у localStorage. Оскільки мені треба досягнення обох цілей – скрол і час – мені треба якось відмітити досягнення скролу до 50%, якщо воно сталось раніше, ніж людина провела 40 секунд на сторінці. Тому я додаю ще одну команду localStorage.setItem, яка запише 0 в localStorage при переході на сторінку, і 1 – при досягненні скролу 50% на ній. Якщо це пояснення поки не дуже зрозуміле, далі буде детальніше, тому продовжуйте читати.
Фінальний код, який відправляє в dataLayer івент scroll_to_50_pct і записує потрібні значення в localStorage:
<script>
Storage ? localStorage.setItem('gtm_scroll_to_50_pct', '0') : undefined;
var hasReached50Percent = false; // Flag to ensure event fires only once
function trackScrollTo50Percent() {
var scrollPosition = window.scrollY || window.pageYOffset;
var windowHeight = window.innerHeight;
var documentHeight = document.documentElement.scrollHeight;
var scrollableHeight = documentHeight - windowHeight;
// Calculate scroll percentage
var scrollPercentage = (scrollPosition / scrollableHeight) * 100;
// Check if scroll is at or past 50%
if (scrollPercentage >= 50 && !hasReached50Percent) {
hasReached50Percent = true; // Set flag to true so it doesn't fire again
dataLayer.push({
'event': 'scroll_to_50_pct'
});
Storage ? localStorage.setItem('gtm_scroll_to_50_pct', '1') : undefined;
}
}
// Add event listener for scroll event
window.addEventListener('scroll', trackScrollTo50Percent);
</script>Ця стаття має на меті показати, що при правильному запиті GPT може дати вам потрібне рішення під вашу конкретну задачу. І при цьому це рішення не буде потребувати значних правок. Саме так було в моєму випадку – мені потрібен був слухач скролу на 50%, я його отримала. Хороший розробник зробив би змінну напочатку, де можна було б за потреби задати поріг скролу, відмінний від 50%. Але тут я можу обійтись без розробника, і базових знань JS буде достатньо, щоб зрозуміти, що для скролу на 60% буде достатньо змінити в коді 50 на 60 там, де зустрічається 50. Ну і ніхто не забороняє вам продовжити спілкуватись з GPT, якщо вам треба написати код якось по-іншому)
Додаємо цей код в тег типу Custom HTML. В тригерах додаємо 2 таких:
- Мені потрібно відстежувати подію на сторінках блогу, тому я обираю тригер перегляду сторінки, шлях якої починається на
/blog/. Якщо вам треба всі сторінки, обирайте All Pages. Цей тригер спрацьовує при завантаженні сторінки.

2. Другий – це кастомний івент з назвою page_view. У мене він теж обмежений сторінками блогу, ви можете не додавати умови:

Цей івент генерує бібліотека GA4 при віртуальних переходах між сторінками. В GTM і dataLayer він виглядає так:

На виході маємо таке налаштування:

Таймер часу на активній вкладці
Для того, щоб працював код таймеру зі статті Макса, на сайті має бути бібліотека jQuery. Якщо на вашому сайті вона не використовується, її можна додати власноруч через GTM, але це не дуже добре рішення, бо навантажує сайт зайвими бібліотеками.
Тому цей код треба переписати на звичайний JS. І я це теж делегувала GPT.
Насправді, я це зробила ще на курсі, не пам’ятаю для чого, але тоді не було блогу, щоб можна було цим поділитись)
Так от, сам код:
<script>
var TIME_WHEN_SEND_DATA = 40; // змініть на потрібну кількість секунд
var invisibility_time = 0;
var window_invisibility_time = 0;
function fixTime() {
var dateObj = new Date();
return Math.floor(dateObj.getTime() / 1000);
}
Storage ? localStorage.setItem('gtm_active_time', '0') : undefined;
function onVisibilityChange() {
var current_timestamp;
if (document.visibilityState === "hidden") {
invisibility_time = fixTime();
} else {
current_timestamp = fixTime();
window_invisibility_time += (current_timestamp - invisibility_time);
}
}
document.addEventListener('visibilitychange', onVisibilityChange, false);
if (document.visibilityState === "visible") {
if (typeof dataLayer === 'undefined') {
console.log('dataLayer is not defined!!!');
} else {
var startLiveDoc = fixTime();
var check_time = function() {
if (document.visibilityState === "visible" && fixTime() - startLiveDoc - window_invisibility_time === TIME_WHEN_SEND_DATA) {
dataLayer.push({
'event': 'active_time_' + TIME_WHEN_SEND_DATA
});
Storage ? localStorage.setItem('gtm_active_time', '1') : undefined;
} else {
setTimeout(check_time, 1000);
}
};
check_time();
}
}
</script>За замовчуванням він відраховує 40 секунд. Якщо вам потрібне інше значення, змініть його в першому рядку var TIME_WHEN_SEND_DATA = 40. Тоді і назва івенту підтягне це значення і буде мати назву за принципом “active_time_{{кількість секунд}}”
Додатково я додала команду, яка буде записувати в localStorage ще один запис – зі значенням 0 при завантаженні сторінки, і 1 при досягненні 40 секунд на цій сторінці. Додаю такі ж 2 тригери, як для скролу, і маємо на виході таке:

Якщо у вас “звичайний” сайт, вам треба використати тільки тригер Page View (All Pages).
Додаємо змінні з localStorage
Зовсім скоро вам стане зрозумілим навіщо ми щось писали в localStorage. Але поки що нам треба створити 2 змінні, які будуть забирати звідти значення, які записали наші теги.
Переходимо в GTM у розділ Змінні (Variables) і створюємо 2 змінні типу Custom JavaScript:
- Перша забирає значення з ключа
gtm_scroll_to_50_pct:
function() {
return Storage ? localStorage.getItem('gtm_scroll_to_50_pct') : undefined;
}- Друга – з ключа
gtm_active_time:
function() {
return Storage ? localStorage.getItem('gtm_active_time') : undefined;
}Просто копіюєте по черзі код змінних, вставляєте у відповідне поле, називаєте змінну, і зберігаєте. Приклад однієї з них:

Налаштовуємо івент engagement
Після всіх приготувань тепер збираємо це все в івент взаємодії.
Знову ж таки, якщо у вас “звичайний” сайт, вам достатньо зробити, як в оригінальній статті – через тригер типу “Група тригерів”.
Його принцип такий:
- Людина заходить на сайт.
- Скролить до 50% - спрацьовує івент скролу.
- На цій же сторінці проводить 40 секунд - спрацьовує івент часу.
- Спрацьовує група тригерів, бо на цій сторінці спрацювали 2 потрібні тригери.
- Відправляється івент
engagementв GA4. - Людина переходить на іншу сторінку > час 40 секунд і скрол 50% > група тригерів > engagement.
Якщо на цій сторінці не спрацює або час 40 секунд, або скрол 50%, група тригерів не запуститься, і engagement не відправиться.
З SPA історія інша. Для групи тригерів таке ж обмеження, як і для тригеру скролу – вона спрацьовує тільки один раз після завантаження сторінки. Таким чином івент відправиться тільки один раз, і якщо юзер буде переходити на інші SPA сторінки, група тригерів вже не буде спрацьовувати.

Через це ми не можемо налаштувати Групу тригерів, і маємо зробити відстеження досягнення обох умов самостійно. Для цього створюємо 2 тригери:
- Перший спрацьовує на івенті
scroll_to_50_pct(коли досягнуто 50%), тільки якщо значення gtm_active_time в localStorage дорівнює 1. Тобто на момент скролу на 50% людина вже провела на цій сторінці 40 секунд.

- І другий тригер дзеркальний – спрацьовує на івенті
active_time_40,тільки якщо значення gtm_scroll_to_50_pct в localStorage дорівнює 1. Тобто на момент проведення 40 секунд на цій сторінці людина вже скролила її до 50%.

Залишилось налаштувати тег engagement. Обираємо тип тегу – GA4 Event, додаємо Measurement ID, пишемо назву івенту engagement і додаємо 2 тригери, які ми тільки що створили:

Зберігаємо, тестуємо, публікуємо.

На завершення
Стаття вийшла доволі об’ємною, але тільки тому, що мені хотілось пояснити навіщо потрібні всі ці кроки.
А ще для того, щоб поділитись робочим процесом людини, яка не є розробником на JavaScript, і показати, що розробником бути необов’язково, щоб робити цікаві і корисні налаштування. Вашим помічником може стати GPT. Але саме помічником, не більше. Вам не обов’язково знати JS на рівні написання коду з нуля, але вам точно потрібно знати його на тому рівні, щоб розуміти, що вам видає “помічник”.
Сподіваюсь, у вас вийшло зробити це налаштування, і тепер ви точно будете знати чи взаємодіяли користувачі з вашим контентом і з якими саме сторінками.

Завантаження коментарів…