widget-utm-collector.js — универсальный сбор UTM/click-id для форм GetCourse
widget-utm-collector.js — универсальный сбор UTM/click-id меток в форму GetCourse
Скрипт, который автоматически прокидывает рекламные метки (UTM + click-id) из URL и из first-touch storage в скрытые поля любой формы GetCourse. Работает с любыми виджетами и лендингами — не только с квизом Fitness Mama.
Заменяет vakas-tools utm_to_gk_field.js, у которого есть три критичных бага для современных лендингов (см. ниже).
Зачем нужен (vs vakas)
| Проблема vakas | widget-utm-collector |
|---|---|
Слушает DOMContentLoaded — если виджет загружается позже (через инжект скрипта на действие пользователя), listener не сработает |
readyState-чек: отрабатывает в любой момент после загрузки |
Захватывает только utm_* — fbclid/gclid/yclid теряются |
Ловит все 9 параметров |
| Нет first-touch: при возврате на сайт по закладке/прямому заходу метки потеряны | Сохраняет first-touch в localStorage на 90 дней |
Заполняет только <input> |
Заполняет <input> и <textarea> |
| Не диспатчит события — GC может игнорировать значение | Диспатчит input + change |
| Одна попытка заполнения | Три попытки (0мс, 500мс, 1500мс) для поздно отрисованных полей |
Где лежит скрипт
Файл хостится в CMS GetCourse (раздаётся с того же домена gc.fitnessmama.co — нет CORS, нет задержек):
https://gc.fitnessmama.co/pl/cms/layout/js?id=92493&hash=abc932387ed49dc892bf87e6f0fa586d&bundle=1
hash пересчитывается автоматически при изменении файла в CMS — кэш сбрасывается сам.
Исходник в проекте: widget-utm-collector.js (в репозитории квиза).
Универсальная настройка — для любого виджета и лендинга
Чтобы подключить скрипт к новой форме / новому лендингу, нужно 3 шага.
Шаг 1 — Добавить скрытые поля в форму GetCourse
В настройках виджета формы добавьте Offer field или User field (по одному на каждую метку, которую хотите сохранять). Какие именно — зависит от того, какие данные нужны в сделке/контакте.
Минимальный набор (рекомендую):
| CSS-класс поля | Какое значение запишется | Куда сохранять (поле в GC) |
|---|---|---|
utm_source |
utm_source | deal_utm_source или user_utm_source |
utm_medium |
utm_medium | deal_utm_medium |
utm_campaign |
utm_campaign | deal_utm_campaign |
utm_content |
utm_content | deal_utm_content |
utm_term |
utm_term | deal_utm_term |
Расширенный набор (если ведёте таргет Facebook/Google/Yandex):
| CSS-класс | Значение |
|---|---|
fbclid |
Facebook click ID |
gclid |
Google click ID |
yclid |
Yandex click ID |
utm_group |
Кастомный параметр (мы используем для URL квиза с ответами) |
Важно:
- В каждом поле должна стоять галочка «hidden» (скрытое от пользователя).
- Имя самого поля в GC (например
deal_utm_source) — на ваше усмотрение, скрипт работает по CSS-классу, а не по имени поля. - CSS-класс пишется в раздел «CSS element class» в настройках поля.
- Можно к одному классу написать несколько слов через пробел:
utm_source hidden— скрипт смотрит только наutm_source.
Шаг 2 — Подключить скрипт внутри виджета
В настройках виджета формы добавьте HTML-блок (или используйте существующий) и вставьте:
<script src="https://gc.fitnessmama.co/pl/cms/layout/js?id=92493&hash=abc932387ed49dc892bf87e6f0fa586d&bundle=1"></script>
Расположение HTML-блока в форме не важно — лучше в конце, чтобы поля успели отрисоваться к моменту запуска. Но скрипт всё равно перезапустится трижды с задержкой, так что критичности нет.
Шаг 3 — Тестирование
- Откройте страницу с формой в инкогнито с параметрами:
https://ваш-домен/страница?utm_source=TEST&utm_campaign=PROOF&fbclid=ABC123 - F12 → Console.
- В Console проверьте, что поля заполнены:
document.querySelectorAll('.utm_source input, .utm_campaign input, .fbclid input').forEach(el => { console.log(el.name || '(no name)', '=', JSON.stringify(el.value)); }); - Сабмитьте форму. В новой сделке/контакте в GetCourse метки должны появиться.
Дополнительно для лендингов с многошаговой логикой
Если лендинг сложный (квиз, многоэкранник, SPA) — форма может появляться через минуты после загрузки страницы, и URL к этому моменту может быть уже изменён (history.pushState/replaceState). В этом случае нужно продублировать first-touch логику на самом лендинге, чтобы метки сохранились в localStorage ещё при первой загрузке.
Минимальный код для лендинга
Вставьте в <head> или в начало <body> лендинга:
<script>
(function(){
var TRACK = ['utm_source','utm_medium','utm_campaign','utm_content','utm_term','utm_group','fbclid','gclid','yclid'];
var KEY = 'fm_first_touch';
var TTL = 90 * 86400000;
var sp = new URLSearchParams(location.search || '');
var data = {}, any = false;
TRACK.forEach(function(k){ var v = sp.get(k); if (v){ data[k] = v; any = true; }});
if (any) {
try {
if (!localStorage.getItem(KEY)) {
localStorage.setItem(KEY, JSON.stringify({ts: Date.now(), data: data}));
}
} catch(e){}
}
})();
</script>
Это «отщёлкивает» метки в storage сразу при первой загрузке, до того как сайт начнёт менять URL или пользователь перейдёт на другую страницу.
widget-utm-collector.js потом достанет их из того же localStorage ключа.
Если на лендинге есть редирект на форму с параметрами
Когда лендинг строит URL для перехода на форму (как наш quiz-pink.gc.html → offer.html), важно протащить метки через query string. Пример паттерна — функция buildOfferUrl() в quiz-pink.gc.html:1185:
// собираем все метки из URL/storage
const INCOMING_UTM = /* ... читаем url + localStorage ... */;
function buildOfferUrl() {
const params = new URLSearchParams();
// ... данные с лендинга ...
// и в конце — дописываем метки
for (const [k, v] of INCOMING_UTM) {
params.set(k, v);
}
return OFFER_URL + '?' + params.toString();
}
Тогда форма получит метки и через URL (быстрый путь), и через localStorage (резервный).
Где НЕ сработает (фундаментальные ограничения)
Никакой клиентский скрипт не решит:
- ❌ Прямой/органический трафик — нечего ловить.
- ❌ Инкогнито-режим — localStorage стирается при закрытии вкладки.
- ❌ Запрещённые cookies/storage — скрипт молча игнорирует.
- ❌ Кросс-девайс (зашёл с телефона, купил с ноута) — localStorage привязан к устройству.
- ❌ Прошло >90 дней с первого захода — TTL истёк.
- ❌ JS отключён — скрипт не выполнится.
Для перекрытия этих случаев нужна серверная атрибуция (Facebook CAPI, Google Enhanced Conversions, GTM Server-side) — это отдельная задача, не для этого скрипта.
Реалистичный охват для рекламного трафика с UTM: 90-95% сделок будут с метками.
Как обновлять скрипт
- GetCourse → CMS → найти JS-слой с id 92493.
- Внести правки в код.
- Сохранить — GC автоматически пересчитает
hashв URL подключения. - Если по какой-то причине браузер кеширует старую версию — добавить в URL
&v=2(или поднять номер) для принудительного сброса.
Что делать, если метки не пишутся
Чек-лист по убыванию вероятности:
- Проверить, что скрипт загружен: Network → найти запрос
pl/cms/layout/js?id=92493→ статус 200, в Response виден код. - Проверить first-touch: Console →
localStorage.getItem('fm_first_touch')→ должен быть JSON сtsиdata(если пользователь приходил с UTM). - Проверить наличие полей с классами: Console →
document.querySelectorAll('.utm_source, .utm_campaign').length→ должно быть > 0. - Проверить, что класс на правильном элементе: класс должен стоять на обёртке поля (Offer field/User field), внутри неё —
<input>или<textarea>. - Проверить, что скрипт реально отработал: Console →
document.querySelector('.utm_source input').value→ не пусто. - Если на лендинге URL подменяется — убедиться, что first-touch код из секции «Дополнительно для лендингов» вставлен до любых
history.replaceState/pushState.
Конкретные применения
| Где | Виджет | Лендинг | Статус |
|---|---|---|---|
| Квиз Fitness Mama | Форма 1602182 | gc.fitnessmama.co/quiz |
✅ работает с 2026-05-18 |
При добавлении новых форм/лендингов — обновите эту таблицу.
История изменений
- 2026-05-18 — первая версия. Замена vakas-tools/utm_to_gk_field.js.
- readyState-чек (исправляет неотработку при поздней загрузке виджета).
- Поддержка fbclid/gclid/yclid.
- First-touch в localStorage на 90 дней.
- Заполнение
<textarea>в дополнение к<input>. - Диспатч событий
input/change. - Три попытки заполнения с задержкой.