Skip to main content

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 — Тестирование

  1. Откройте страницу с формой в инкогнито с параметрами: https://ваш-домен/страница?utm_source=TEST&utm_campaign=PROOF&fbclid=ABC123
  2. F12 → Console.
  3. В Console проверьте, что поля заполнены:
    document.querySelectorAll('.utm_source input, .utm_campaign input, .fbclid input').forEach(el => {
      console.log(el.name || '(no name)', '=', JSON.stringify(el.value));
    });
    
  4. Сабмитьте форму. В новой сделке/контакте в 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.htmloffer.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% сделок будут с метками.


Как обновлять скрипт

  1. GetCourse → CMS → найти JS-слой с id 92493.
  2. Внести правки в код.
  3. Сохранить — GC автоматически пересчитает hash в URL подключения.
  4. Если по какой-то причине браузер кеширует старую версию — добавить в URL &v=2 (или поднять номер) для принудительного сброса.

Что делать, если метки не пишутся

Чек-лист по убыванию вероятности:

  1. Проверить, что скрипт загружен: Network → найти запрос pl/cms/layout/js?id=92493 → статус 200, в Response виден код.
  2. Проверить first-touch: Console → localStorage.getItem('fm_first_touch') → должен быть JSON с ts и data (если пользователь приходил с UTM).
  3. Проверить наличие полей с классами: Console → document.querySelectorAll('.utm_source, .utm_campaign').length → должно быть > 0.
  4. Проверить, что класс на правильном элементе: класс должен стоять на обёртке поля (Offer field/User field), внутри неё — <input> или <textarea>.
  5. Проверить, что скрипт реально отработал: Console → document.querySelector('.utm_source input').value → не пусто.
  6. Если на лендинге 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.
    • Три попытки заполнения с задержкой.