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, оту vakas-tools.которого Используетсяесть втри виджетекритичных формыбага 1602182для насовременных страницелендингов квиза(см. ниже).gc.fitnessmama.co/quiz
Зачем заменилинужен (vs vakas)
В
DOMContentLoaded — если виджет загружается позжеreadyState-чек: utm_* — fbclidgclidyclid Из-за этого в выгрузке deal_export.xlsx много сделок без меток, хотя пользователи приходили с рекламы.
Что делает новый скрипт
utm_sourceutm_mediumutm_campaignutm_contentutm_termutm_groupfbclidgclidyclidlocalStorage fm_first_touchlocalStorage<input><input> и <textarea>
.utm_source.utm_group.fbclidreadyStateDOMContentLoadedзначение
input change
Где лежит и как подключёнскрипт
Файл скрипта хостится в CMS GetCourse: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
В настройках виджета формы 1602182добавьте вOffer HTML-блоке:
<scriptили src="https://gc.fitnessmama.co/pl/cms/layout/js?id=92493&hash=abc932387ed49dc892bf87e6f0fa586d&bundle=1"></script>User field (по одному на каждую метку, которую хотите сохранять). Какие поляименно — зависит от того, какие данные нужны в форме
сделке/контакте.
ЧтобыМинимальный скрипт знал, куда писать значения, в форме виджета должны быть полянабор (User field или Offer field) со следующими CSS-классами:рекомендую):
| CSS-класс поля |
|---|
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):
utm_groupCSS-класс
fbclid
Facebook click ID
gclid
Google click ID
yclid
Yandex click ID
utm_group
Кастомный параметр (мы используем для URL квиза с ответами)
ИменаВажно:
deal_utm_sourceoffer_quiz_url) — на ваше 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)html
На→ сторонеoffer.html), квизаважно работаетпротащить параллельнаяметки логикачерез query string. Пример паттерна — функция buildOfferUrl() в quiz-pink.gc.:htmlhtml: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 (резервный).
Где НЕ сработает (фундаментальные ограничения)
Никакой клиентский скрипт не решит:
❌INCOMING_UTMпри загрузке страницы тоже захватывает все параметры из спискаTRACK_PARAMS.
localStoragefm_first_touchbuildOfferUrl()Таким образом метки переживают: переходы между страницами квиза, F5, history.replaceState, возврат по закладке.
Чего скрипт НЕ решает
Для перекрытия этих случаев нужна серверная атрибуция (Facebook CAPI, Google Enhanced Conversions, GTM Server-side) — это отдельная задача, не для этого скрипта.
Реалистичный охват для рекламного трафика с UTM: 90-95% сделок будут с метками.
Как обновлять скрипт
Зайти вGetCourse → CMS → найти JS-слой с id92493.92493.- Внести
правки.правки в код. - Сохранить — GC автоматически пересчитает
hashв URLи браузеры подтянут новую версию.подключения. - Если
по какой-то причинеhashнебраузерпоменялсякеширует старую версию — добавить в URL&v=2в(илиURLподнятьподключенияномер) длясбросапринудительногокэша.сброса.
Как протестировать
https://gc.fitnessmama.co/quiz?utm_source=TEST&utm_campaign=PROOF&fbclid=ABC123Object.assign(answers, {name:'Test', currentWeight:'70', targetWeight:'60', height:'165'});
cur = STEPS.findIndex(s => s.type === 'email');
render(cur, 'forward');
document.querySelectorAll('.utm_source input, .utm_campaign input, .fbclid input, .utm_group input, .utm_group textarea').forEach(el => {
console.log(el.tagName, 'name=', el.name || '(no name)', 'value=', JSON.stringify(el.value));
});
Что делать, если метки не пишутся
Чек-лист по убыванию вероятности:
- Проверить, что скрипт загружен:
вNetworkNetwork→ найти запросpl/cms/layout/js?id=92493—→ статус 200, в Response виден код. - Проверить first-
touch в localStorage:touch:вConsole →localStorage.getItem('fm_first_touch')—→ должен быть JSON сtsиdata(если пользователь приходил с UTM). - Проверить наличие полей с
нужнымиклассами:вConsole →document.querySelectorAll('.utm_source, .utm_group'utm_campaign').length—→большедолжно быть > 0. - Проверить, что класс
стоитна правильном элементе: класс долженбытьстоять на обёртке поля (Offerfield /field/Userfield в билдере GC)field),авнутри неёуже находится—<input>или<textarea>. - Проверить, что скрипт реально отработал: Console →
document.querySelector('.utm_source input').value→ не пусто.
history.replaceState/pushState.
Конкретные применения
gc.fitnessmama.co/quiz
✅ работает с 2026-05-18
При добавлении новых форм/лендингов — обновите эту таблицу.
История изменений
- 2026-05-18 — первая версия. Замена vakas-tools/utm_to_gk_field.js.
ДобавленreadyState-чекreadyState-чек.(исправляет неотработку при поздней загрузке виджета).Добавлена поддержкаПоддержка fbclid/gclid/yclid.ДобавленFirst-touchfirst-touchв localStorage на 90дней в localStorage.дней.- Заполнение
<textarea>в дополнение к<input>(для длинных полей вродеoffer_quiz_url). - Диспатч событий
input/change.
после - Три
установкипопыткизначения.заполнения с задержкой.