SEO
1766649600

5 нестандартных способов использования Data Layer

Data Layer — это JavaScript-объект, который работает как промежуточное звено между сайтом и системами аналитики типа Google Analytics. Чаще всего его знают исключительно в контексте электронной коммерции: отслеживание покупок, добавление товаров в корзину, переходы между категориями. Однако ограничиваться таким подходом — все равно что пользоваться смартфоном только для звонков.

В статье я рассмотрю пять нестандартных способов использования dataLayer, которые помогут собрать данные, невидимые в стандартной аналитике, и превратить их в инсайты для улучшения UX, конверсии и работы ресурса.

  1. Фиксация ошибок JavaScript в браузере
  2. Отслеживание взаимодействия с интерактивными картами
  3. Отслеживание поведения при валидации форм
  4. Копирование текста
  5. Отслеживание «мертвых кликов»
  6. Как передать данные из DataLayer в GA4

Фиксация ошибок JavaScript в браузере

«Почему конверсия на мобильных устройствах ниже, чем на десктопе?» — вопрос, который мучает многих владельцев eCommerce-проектов. 

А ответ часто кроется в JavaScript-ошибках, которые блокируют критическую функциональность, но незаметны для стандартной аналитики.

Google Analytics и другие системы собирают данные о посещениях, кликах, конверсиях. Но они не видят, когда возникает сбой JavaScript и нарушает функциональность. Пользователь нажимает «Добавить в корзину», ничего не происходит, он уходит к конкурентам — а вы даже не знаете, что случилось.

Есть два основных способа настройки обнаружения JS-ошибок через dataLayer.

Способ 1. С помощью обработчика JavaScript — window.onerror

Глобальный обработчик проблем в логике для JavaScript. Когда на странице возникает JavaScript-ошибка, браузер автоматически вызывает эту функцию.

Способ подходит для простого отслеживания сбоев в коде сайта — когда перестает работать кнопка «Купить сейчас», ломается калькулятор доставки или не работает слайдер на главной странице.

Вот базовый код, который будет автоматически отслеживать все JavaScript-ошибки на странице:

window.onerror = function(errorMessage, url, lineNumber, columnNumber, errorObject) {
  dataLayer.push({
    'event': 'javascript_error',
    'error_message': errorMessage,
    'error_url': url,
    'error_line': lineNumber,
    'error_column': columnNumber,
    'page_url': window.location.href
  });

  return false; // позволяет браузеру также обработать ошибку
};

Минус: не ловит ошибки загрузки ресурсов (картинок, скриптов, стилей), а подключение нескольких обработчиков требует дополнительного кода.

Способ 2. С помощью обработчика JavaScript — window.addEventListener('error')

Более гибкий подход, позволяющий отслеживать различные типы ошибок.

Способ будет полезен, когда нужен комплексный мониторинг — видеть не только сломанный код, но и проблемы с загрузкой картинок товаров, CSS-файлов или JavaScript-библиотек:

window.addEventListener('error', function(event) {
  // Проверяем, что это ошибка загрузки ресурса
  if (event.target !== window) {
    dataLayer.push({
      'event': 'resource_error',
      'error_type': 'resource_loading',
      'resource_url': event.target.src || event.target.href,
      'resource_type': event.target.tagName,
      'page_url': window.location.href
    });
  } else {
    // Это ошибка JavaScript
    dataLayer.push({
      'event': 'javascript_error',
      'error_message': event.message,
      'error_url': event.filename,
      'error_line': event.lineno,
      'error_column': event.colno,
      'page_url': window.location.href
    });
  }
});

Если сайт использует современный JavaScript с async/await (например, для загрузки данных из API без перезагрузки страницы), стоит отдельно отслеживать ошибки в асинхронных операциях. 

Простыми словами: когда сайт загружает данные «в фоне» (например, подтягивает цены товаров без обновления страницы), ошибки в этих процессах могут нарушить функциональность, но вы о них не узнаете из обычных логов.

Чтобы фиксировать такие необработанные ошибки, добавьте код:

window.addEventListener('unhandledrejection', function(event) {
  dataLayer.push({
    'event': 'promise_error',
    'error_message': event.reason.message || 'Promise rejected',
    'page_url': window.location.href
  });
});

Также на большинстве сайтов загружаются сторонние скрипты — реклама Google Ads, виджеты социальных сетей, чаты поддержки. Сбои в этих скриптах не связаны с вашим кодом и могут перегрузить аналитику сотнями ненужных записей. Чтобы отслеживать только ошибки вашего ресурса, добавьте фильтрацию:

window.addEventListener('error', function(event) {
  // Игнорируем ошибки со сторонних доменов
  if (event.filename && !event.filename.includes(window.location.hostname)) {
    return;
  }

  // Ваш код отслеживания...
});

Описанные подходы особенно важны для:

  • крупных eCommerce-платформ — проблемы в процессе оформления заказа могут приводить к ежедневным убыткам;

  • SaaS-проектов — JavaScript-баги блокируют ключевую функциональность и снижают удовлетворенность пользователей;

  • сайтов, имеющих много интерактивных элементов — слайдеры, калькуляторы, фильтры, зависящие от JavaScript.

Фиксация в Google Analytics позволяет быстро идентифицировать:

  • страницы, где чаще всего возникают сбои;

  • наиболее проблемные браузеры или устройства;

  • как именно это влияет на конверсию.

Отслеживание взаимодействия с интерактивными картами

«Посетители активно используют карту на главной странице, но конверсия все равно низкая. Что они там делают?» — вопрос, знакомый всем, кто работает с недвижимостью, отелями или локальными услугами.

Карты — это мощный инструмент взаимодействия с пользователями. Они показывают, что именно интересует посетителя: какой район он увеличивает на карте, на какие маркеры нажимает, какую информацию просматривает. Все это — ценные сигналы о намерениях и потребностях, которые можно использовать для персонализации и повышения конверсии.

Карты помогают:

  1. На площадках по продаже недвижимости понять, какие районы наиболее популярны, в каких зонах чаще всего увеличивают масштаб. 

  2. На платформах бронирования отелей — какие локации просматривают чаще всего, насколько далеко от центра готовы рассматривать жилье.

Если на сайте по продаже недвижимости уже интегрирована Google Maps, можно добавить отслеживание к ней. Код для выявления основных взаимодействий:

// Инициализируем карту с обработчиками событий
function initMap() {
  const map = new google.maps.Map(document.getElementById('map'), {
    zoom: 10,
    center: {lat: 50.4501, lng: 30.5234} // Киев
  });

  // Отслеживаем изменения масштаба
  map.addListener('zoom_changed', function() {
    dataLayer.push({
      'event': 'map_zoom_change',
      'zoom_level': map.getZoom(),
      'map_center_lat': map.getCenter().lat(),
      'map_center_lng': map.getCenter().lng(),
      'page_url': window.location.href
    });
  });

  // Отслеживаем перетаскивание карты
  map.addListener('dragend', function() {
    const bounds = map.getBounds();
    dataLayer.push({
      'event': 'map_pan',
      'map_center_lat': map.getCenter().lat(),
      'map_center_lng': map.getCenter().lng(),
      'viewport_ne_lat': bounds.getNorthEast().lat(),
      'viewport_ne_lng': bounds.getNorthEast().lng(),
      'viewport_sw_lat': bounds.getSouthWest().lat(),
      'viewport_sw_lng': bounds.getSouthWest().lng()
    });
  });

  // Добавляем маркеры квартир с отслеживанием кликов
  apartments.forEach(function(apartment) {
    const marker = new google.maps.Marker({
      position: {lat: apartment.lat, lng: apartment.lng},
      map: map,
      title: apartment.title
    });

    marker.addListener('click', function() {
      dataLayer.push({
        'event': 'map_marker_click',
        'property_id': apartment.id,
        'property_type': apartment.type, // 'apartment', 'house', 'commercial'
        'property_price': parseFloat(apartment.price),
        'property_district': apartment.district,
        'marker_lat': apartment.lat,
        'marker_lng': apartment.lng
      });
    });
  });
}

Что означают события:

  1. map_zoom_change — срабатывает, когда посетитель меняет масштаб карты — кнопками +/-, колесиком мыши или жестами на мобильном. Показывает, насколько подробно человек хочет рассмотреть локацию. Частое увеличение масштаба в определенном районе сигнализирует о высоком интересе к этой зоне.

  2. map_pan — срабатывает после того, как посетитель перетащил карту в другое место. Помогает понять, какие географические зоны интересуют пользователей. Viewport bounds (границы видимой области) показывают, какую часть карты видит пользователь на экране.

  3. map_marker_click — активируется при нажатии на маркер конкретного объекта (квартиры, дома, коммерческой недвижимости). Это прямой сигнал интереса к конкретному предложению. 

Для платформ бронирования отелей логика схожа, но с акцентом на рейтинг, цену и расстояние до центра города. Для начала нужен массив с данными об отелях — обычно информация поступает из базы данных или API. Ниже приведен пример структуры такого массива:

// Пример структуры данных отелях
// Замените названия полей на те, которые используются в вашей базе данных
const hotels = [
  {
    id: 'hotel_123', // id отеля
    name: 'Отель "Киевский"', // название отеля
    rating: 4.5, // рейтинг отеля
    pricePerNight: 2500, // цена за ночь в грн
    distanceToCenter: 1.2, // расстояние до центра в км
    lat: 50.4501, // координаты широты
    lng: 30.5234, // координаты долготы
  },
  {
    id: 'hotel_456',
    name: 'Отель "Днепр"',
    rating: 4.8,
    pricePerNight: 3200,
    distanceToCenter: 0.5,
    lat: 50.4547,
    lng: 30.5238,
  }
  // ... другие отели
];

Теперь создайте функцию для отслеживания взаимодействий и добавьте обработчики событий:

function trackHotelMapInteraction(hotelData, interactionType) {
  dataLayer.push({
    'event': 'hotel_map_interaction',
    'interaction_type': interactionType, // 'marker_click', 'info_window_open', 'price_filter'
    'hotel_id': hotelData.id,
    'hotel_name': hotelData.name,
    'hotel_rating': parseFloat(hotelData.rating),
    'hotel_price_per_night': parseFloat(hotelData.pricePerNight),
    'distance_to_center': parseFloat(hotelData.distanceToCenter), // в км
    'location_lat': hotelData.lat,
    'location_lng': hotelData.lng,
  });
}

// Отслеживание кликов по отелям с различными характеристиками
marker.addListener('click', function() {
  trackHotelMapInteraction(hotelData, 'marker_click');
});

Значение типов взаимодействий:

  1. marker_click — нажатие непосредственно на маркер отеля на карте. Показывает первоначальный интерес к конкретному объекту. Позволяет отслеживать всю информацию об отеле, чтобы понять, какие характеристики (цена, рейтинг, локация) больше всего привлекают внимание.

  2. info_window_open — открытие информационного окна (popup) с подробностями об отеле. Это более сильный сигнал интереса по сравнению с простым кликом по маркеру — пользователь хочет узнать больше.

  3. price_filter — применение фильтра по цене непосредственно на карте (если такая функция предусмотрена). Дает понимание, на какой ценовой сегмент направлен спрос и какие диапазоны интересуют аудиторию больше всего.

Кроме базовых взаимодействий, полезно отслеживать время, которое посетитель проводит на карте:

// Отслеживание времени взаимодействия с картой
let mapEngagementStart = null;

// Фиксируем начало взаимодействия при движении курсора по карте
map.addListener('mousemove', function() {
  if (!mapEngagementStart) {
    mapEngagementStart = Date.now();
  }
});

// Фиксируем конец взаимодействия, когда курсор покидает карту
map.addListener('mouseout', function() {
  if (mapEngagementStart) {
    const timeSpent = Math.round((Date.now() - mapEngagementStart) / 1000);

    // Отслеживаем только если пользователь провел на карте хотя бы 2 секунды
    if (timeSpent >= 2) {
      dataLayer.push({
        'event': 'map_engagement',
        'engagement_duration': timeSpent, // секунды
        'page_url': window.location.href
      });
    }

    mapEngagementStart = null;
  }
});

Что означают эти события:

  1. mousemove — срабатывает при движении курсора по карте. Используется для фиксации начала активного взаимодействия с картой.

  2. mouseout — срабатывает, когда курсор покидает область карты. Фиксирует завершение взаимодействия и показывает общее время, которое пользователь провел на карте. Стоит отслеживать только взаимодействия от 2 секунд, чтобы отфильтровать случайные наведения курсора.

Отслеживание поведения при валидации форм

Обычно мы видим только конечный результат: пользователь либо отправил форму, либо нет. Но что происходит на всем пути? Сколько было попыток отправки? На каком поле возникло трение? Появлялись ли вообще сообщения об ошибках валидации?

Именно эти «темные зоны» поведения скрывают ответы на вопрос, почему проседает конверсия. И именно их dataLayer помогает сделать видимыми.

Допустим, есть простая контактная форма с полями email и телефон. Нужно отслеживать все попытки отправки — успешные и неудачные — чтобы понять, на каких именно полях чаще всего возникают ошибки. 

Вот базовый пример для контактной формы. Код срабатывает при каждой попытке отправки, даже если форма не прошла валидацию:

// Счетчик попыток для каждой формы
let formAttempts = {};

function trackFormValidation(formId, validationResult, errorFields = [], errorTypes = []) {
  // Увеличиваем счетчик попыток
  if (!formAttempts[formId]) {
    formAttempts[formId] = 0;
  }
  formAttempts[formId]++;

  dataLayer.push({
    'event': 'form_validation_attempt',
    'form_id': formId,
    'form_name': document.getElementById(formId).getAttribute('name') || formId,
    'validation_success': validationResult,
    'error_fields': errorFields,
    'error_types': errorTypes,   // типы ошибок: required, format, length
    'attempt_number': formAttempts[formId],
    'page_url': window.location.href
  });
}

// Пример использования при отправке формы
document.getElementById('contact-form').addEventListener('submit', function(e) {
  let errors = [];
  let errorTypes = [];
  let isValid = true;

  // Проверяем email
  const email = this.querySelector('[name="email"]').value;
  if (!email) {
    errors.push('email');
    errorTypes.push('required');
    isValid = false;
  } else if (!/^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(email)) {
    errors.push('email');
    errorTypes.push('format');
    isValid = false;
  }

  // Проверяем телефон
  const phone = this.querySelector('[name="phone"]').value;
  if (!phone) {
    errors.push('phone');
    errorTypes.push('required');
    isValid = false;
  } else if (!/^[\d\+\-\s\(\)]{10,}$/.test(phone)) {
    errors.push('phone');
    errorTypes.push('format');
    isValid = false;
  }

  // Отслеживаем попытку валидации
  trackFormValidation('contact-form', isValid, errors, errorTypes);

  if (!isValid) {
    e.preventDefault(); // Останавливаем отправку
  }
});

Для форм заказа можно отслеживать дополнительные параметры — этап оформления, сумму заказа, способ доставки:

// Счетчики для каждого шага checkout
let checkoutAttempts = {};

function trackCheckoutValidation(step, validationData) {
  // Уникальный ключ для каждого шага
  const stepKey = `checkout_${step}`;

  if (!checkoutAttempts[stepKey]) {
    checkoutAttempts[stepKey] = 0;
  }
  checkoutAttempts[stepKey]++;

  dataLayer.push({
    'event': 'checkout_validation',
    'checkout_step': step, // 'shipping', 'payment', 'review'
    'validation_success': validationData.isValid,
    'error_fields': validationData.errorFields,
    'error_types': validationData.errorTypes, 
    'attempt_number': checkoutAttempts[stepKey],
    'order_value': parseFloat(validationData.orderValue),
    'payment_method': validationData.paymentMethod || 'unknown'
  });
}

Чтобы адаптировать этот код под конкретный проект, объект validationData должен содержать полный набор данных о состоянии формы и заказа. Структура выглядит так:

const validationData = {
  isValid: true,                    // true/false -- прошла ли форма валидацию
  errorFields: ['address', 'city'], // массив названий полей с ошибками
  errorTypes: ['required', 'format'], // массив типов ошибок (required, format, length)
  orderValue: '2500.00',            // сумма заказа (строка или число)
  paymentMethod: 'card'              // способ оплаты: 'card', 'cash', 'paypal' и т. д.
};

Чтобы интегрировать решение в реальный процесс оформления заказа:

  1. Определите этапы checkout — например, shipping (доставка), payment (оплата), review (проверка).

  2. Соберите данные о заказе — сумма обычно уже есть на странице в скрытом поле или переменной JavaScript.

  3. Замените названия полей — errorFields должно содержать реальные названия полей из вашей формы (например, 'billing_address', 'card_number').

  4. Вызывайте функцию на каждом этапе отправки, передав соответствующий step и validationData.

Важный нюанс для многостраничных форм: после перезагрузки страницы счетчик попыток отправки сбрасывается. 

Чтобы сохранить данные между обновлениями, используйте sessionStorage:

// Сохраняем счетчики в sessionStorage
function getAttemptCount(formId) {
  const stored = sessionStorage.getItem(`form_attempts_${formId}`);
  return stored ? parseInt(stored) : 0;
}

function incrementAttemptCount(formId) {
  const currentCount = getAttemptCount(formId) + 1;
  sessionStorage.setItem(`form_attempts_${formId}`, currentCount.toString());
  return currentCount;
}

Копирование текста (copy-to-clipboard)

«Промокод показывается 10 000 раз в месяц, а используется всего 500. Его копируют или просто игнорируют?» — классическая загадка для eCommerce- и маркетинговых команд.

Копирование текста — одно из самых недооцененных взаимодействий. Оно всегда дает сигнал о намерении:

  • копируют промокод — собираются сделать покупку;

  • копируют email или телефон — хотят связаться с вами вне сайта;

  • копируют фрагмент статьи — сохраняют или делятся контентом.

По умолчанию эти микроконверсии незаметны, но они могут рассказать много интересного о поведении пользователей и эффективности контента.

Как отследить все копирования на странице

Браузер предоставляет событие copy, которое срабатывает каждый раз, когда посетитель копирует текст. Можно отслеживать все копирования на странице или только в конкретных зонах.

Вот код для общего отслеживания копирования на всей странице:

document.addEventListener('copy', function(e) {
  // Получаем скопированный текст
  const copiedText = window.getSelection().toString();

  // Отслеживаем только осмысленный текст (более 3 символов)
  if (copiedText.length > 3) {
    dataLayer.push({
      'event': 'content_copy',
      'copied_text': copiedText.substring(0, 100), // первые 100 символов
      'copied_length': copiedText.length,
      'page_url': window.location.href,
      'page_title': document.title
    });
  }
});

Как отследить отдельные важные элементы

Гораздо полезнее отслеживать копирование конкретных важных элементов — промокодов, контактов, фрагментов статей, чтобы понять, что именно интересует пользователей.

Представим, что есть eCommerce-сайт с промокодами, размещенными в разных местах — на баннере главной страницы, в поп-апе при выходе, на странице оформления заказа. Задача — понять, откуда чаще всего копируют промокоды, какие типы скидок их больше всего интересуют и влияет ли факт копирования на завершение покупки. 

Код для отслеживания:

// Отслеживание копирования промокодов
document.querySelectorAll('.promo-code').forEach(function(promoElement) {
  promoElement.addEventListener('copy', function(e) {
    const promoCode = this.textContent.trim();

    dataLayer.push({
      'event': 'promo_code_copy',
      'promo_code': promoCode,
      'promo_location': this.getAttribute('data-location') || 'unknown', // banner, popup, checkout
      'promo_discount': this.getAttribute('data-discount') || 'unknown', // 10%, 15%, 20%
      'page_url': window.location.href
    });
  });
});

Если посетители активно копируют промокоды, но не используют их — возможно, процесс оформления заказа слишком сложен.

Как отслеживать копирование контактной информации

Для этого добавьте код:

// Отслеживание копирования email и телефонов
document.querySelectorAll('[data-contact-type]').forEach(function(contactElement) {
  contactElement.addEventListener('copy', function(e) {
    const contactType = this.getAttribute('data-contact-type'); // email, phone, address
    const contactValue = this.textContent.trim();

    dataLayer.push({
      'event': 'contact_copy',
      'contact_type': contactType,
      'contact_value': contactValue,
      'contact_location': this.closest('section')?.id || 'unknown', // header, footer, contact-page
      'page_url': window.location.href
    });
  });
});

Если копируют контакты вместо того, чтобы воспользоваться формой — это может свидетельствовать о двух проблемных сценариях:

  • форма работает некорректно;

  • форма вызывает недоверие, и пользователь выбирает более безопасный для себя способ связи.

Как отслеживать копирование из блоков кода

Если ваш сайт содержит техническую документацию, туториалы или учебные материалы с примерами кода — важно знать, какие именно фрагменты копируются чаще всего.

Это поможет:

  • определить самые популярные примеры, чтобы расширить документацию по этим темам;

  • выявить сложные секции — если код копируют много, но на странице высокий показатель отказов, возможно, пример непонятен;

  • выявить типы фрагментов кода, которые интересуют вашу аудиторию.

 Это можно определить с помощью следующего кода:

// Отслеживание копирования из блоков кода
document.querySelectorAll('pre code').forEach(function(codeBlock, index) {
  codeBlock.addEventListener('copy', function(e) {
    const copiedCode = window.getSelection().toString();
    const language = this.className.match(/language-(\w+)/)?.[1] || 'unknown';

    dataLayer.push({
      'event': 'code_snippet_copy',
      'code_language': language,
      'code_length': copiedCode.length,
      'code_block_index': index,
      'article_title': document.title,
      'page_url': window.location.href
    });
  });
});

Отслеживание «мертвых кликов» (rage clicks)

«Почему кнопка «Купить» получает 5000 кликов, а в корзину добавляется только 1000 товаров?» — вопрос, который может стоить бизнесу десятки тысяч долларов упущенной выгоды.

Представим ситуацию: человек нашел нужный товар, готов купить, нажимает «Добавить в корзину» — и ничего. Он нажимает снова. И еще раз. Три, четыре, пять раз. Раздражение растет, пользователь покидает сайт и переходит к конкурентам. Бизнес теряет продажу, даже не догадываясь о проблеме.

Rage clicks или клики ярости — это серии быстрых повторных кликов по одному элементу,  когда он не реагирует, медленно загружается, работает не так, как ожидается, или вообще сломан.  Стандартная аналитика этого не замечает: она видит только количество кликов, но не их контекст.

Принцип обнаружения прост: когда по одному элементу выполнено 3+ клика за короткий промежуток (1–2 секунды), это сигнал фрустрации. Важно зафиксировать селектор элемента, количество кликов и временной интервал.

Вот базовый код для выявления rage clicks:

// Сохраняем данные о кликах
let clickTracking = {};

document.addEventListener('click', function(e) {
  const element = e.target;
  const elementPath = getElementPath(element);
  const currentTime = Date.now();

  // Инициализируем данные для элемента, если их еще нет
  if (!clickTracking[elementPath]) {
    clickTracking[elementPath] = {
      clicks: [],
      element: element
    };
  }

  // Добавляем время клика
  clickTracking[elementPath].clicks.push(currentTime);

  // Оставляем только клики за последние 2 секунды
  clickTracking[elementPath].clicks = clickTracking[elementPath].clicks.filter(
    time => currentTime - time < 2000
  );

  // Если 3+ клика за 2 секунды — это rage click
  if (clickTracking[elementPath].clicks.length >= 3) {
    dataLayer.push({
      'event': 'rage_click',
      'element_type': element.tagName.toLowerCase(),
      'element_id': element.id || 'no_id',
      'element_class': element.className || 'no_class',
      'element_text': element.textContent?.substring(0, 50) || '',
      'element_path': elementPath,
      'click_count': clickTracking[elementPath].clicks.length,
      'page_url': window.location.href,
      'viewport_width': window.innerWidth,
      'viewport_height': window.innerHeight
    });

    // Сбрасываем счетчик, чтобы избежать дублирования событий
    clickTracking[elementPath].clicks = [];
  }
});

// Функция для получения уникального пути к элементу
function getElementPath(element) {
  const path = [];
  let current = element;

  while (current && current.tagName) {
    let selector = current.tagName.toLowerCase();

    if (current.id) {
      selector += '#' + current.id;
      path.unshift(selector);
      break;
    } else if (current.className) {
      selector += '.' + current.className.split(' ').join('.');
    }

    path.unshift(selector);
    current = current.parentElement;
  }

  return path.join(' > ');
}

Для более точного мониторинга можно отслеживать только важные элементы — кнопки покупки, формы, фильтры, навигацию. Это уменьшит шум в аналитике и позволит сфокусироваться именно на критических проблемах, влияющих на конверсию.

Представим eCommerce-сайт, где особенно важны кнопки «Добавить в корзину» и «Оформить заказ». Ниже — пример, как отслеживать rage clicks именно на таких элементах:

// Отслеживание rage clicks на критических кнопках
const criticalButtons = document.querySelectorAll('.add-to-cart, .checkout-button, .apply-filter');
let buttonClicks = new Map();

criticalButtons.forEach(button => {
  button.addEventListener('click', function(e) {
    const buttonId = this.getAttribute('data-product-id') || this.id || 'unknown';
    const currentTime = Date.now();

    if (!buttonClicks.has(buttonId)) {
      buttonClicks.set(buttonId, []);
    }

    const clicks = buttonClicks.get(buttonId);
    clicks.push(currentTime);

    // Оставляем только клики за последние 1,5 секунды
    const recentClicks = clicks.filter(time => currentTime - time < 1500);
    buttonClicks.set(buttonId, recentClicks);

    // 3+ клика за 1,5 секунды
    if (recentClicks.length >= 3) {
      dataLayer.push({
        'event': 'rage_click_critical',
        'button_type': this.classList.contains('add-to-cart') ? 'add_to_cart' : 'checkout',
        'product_id': this.getAttribute('data-product-id') || null,
        'button_text': this.textContent.trim(),
        'click_count': recentClicks.length,
        'page_url': window.location.href,
        'user_agent': navigator.userAgent
      });

      // Сбрасываем
      buttonClicks.set(buttonId, []);
    }
  });
});

Помимо фиксации кликов ярости, полезно собирать контекст, чтобы точнее определить причину проблемы. Например, элемент может быть перекрыт другим блоком, находиться за пределами viewport или JavaScript не успел загрузиться. 

Приведенный код помогает выяснить причину rage clicks — он проверяет видимость кнопки, ее положение на экране и другие технические характеристики:

function analyzeElement(element) {
  const rect = element.getBoundingClientRect();
  const computedStyle = window.getComputedStyle(element);

  return {
    'is_visible': computedStyle.display !== 'none' && computedStyle.visibility !== 'hidden',
    'is_in_viewport': rect.top >= 0 && rect.bottom <= window.innerHeight,
    'has_pointer_events': computedStyle.pointerEvents !== 'none',
    'z_index': computedStyle.zIndex,
    'element_width': rect.width,
    'element_height': rect.height
  };
}

// Использование при rage click
if (recentClicks.length >= 3) {
  const diagnostics = analyzeElement(this);

  dataLayer.push({
    'event': 'rage_click_critical',
    'button_type': this.classList.contains('add-to-cart') ? 'add_to_cart' : 'checkout',
    'click_count': recentClicks.length,
    ...diagnostics, // добавляем диагностические данные
    'page_url': window.location.href
  });
}

Главное — не игнорировать эти сигналы. Если видите rage clicks на важных элементах, это приоритет номер один для исправления.

Как передать данные из Data Layer в GA4

Чтобы данные попали в Google Analytics или другие системы аналитики, нужен посредник — Google Tag Manager (GTM). Он «слушает» события в dataLayer и передает их в выбранные инструменты.

Процесс выглядит так.

Шаг 1. Пользователь совершает действие на сайте.

Шаг 2. JavaScript фиксирует событие через dataLayer.push()

Шаг 3. GTM получает сигнал через настроенный триггер.

Шаг 4. GTM-тег отправляет данные в GA4/Meta/другие системы.

Шаг 5. Данные появляются в отчетах аналитики.

Разберу на конкретном примере с rage clicks.

Шаг 1. Создаем переменные в GTM

Сначала нужно создать переменные (Variables), которые будут извлекать данные из dataLayer. 

  1. Перейдите в GTM. Выберите меню Variables и нажмите New.

  1. Выберите тип переменной — Data Layer Variable.

  1. Создайте переменные для каждого параметра:

Название переменной: DL - Rage Click - Element Type
Имя переменной уровня данных: element_type

Название переменной: DL - Rage Click - Element ID
Имя переменной в слое данных: element_id

Название переменной: DL - Rage Click - Click Count
Имя переменной Data Layer: click_count

Префикс DL помогает быстро найти переменные dataLayer среди других.

Шаг 2. Создаем триггер

Теперь нужен триггер, который будет срабатывать, когда в dataLayer появляется событие rage_click.

  1. В пункте Triggers нажмите New.

  1. Выберите тип триггера — Custom Event.

  1. В поле Event name укажите rage_click (точно так же, как в коде).

  2. Сохраните настройки.

Шаг 3. Создаем тег для GA4

Создайте тег для отправки данных в Google Analytics.

  1. Перейдите в раздел Tags и нажмите New.

  1. Выберите тип тега — Google Analytics: GA4 Event.

  1. Введите Measurement ID из GA4 или выберите ваш GA4 Config.

  2. В поле Event Name задайте rage_click.

  3. В поле Event Parameters добавьте параметры:

Parameter Name: element_type
Value: {{DL - Rage Click - Element Type}}

Parameter Name: element_id 
Значение: {{DL - Rage Click - Element ID}}

Parameter Name: click_count
Значение: {{DL - Rage Click - Click Count}}

Имя параметра: page_path
Значение: {{Page Path}}

  1. Перейдите в пункт Triggering и выберите созданный триггер rage_click. Сохраните настройки.

Шаг 4. Тестируем

Перед публикацией обязательно протестируйте корректность работы. Для этого:

  1. Включите режим предварительного просмотра. В GTM нажмите Preview и откройте сайт в режиме отладки. Справа появится панель GTM Debug.

  2. Создайте событие. Сделайте несколько быстрых кликов по любому элементу на странице, имитируя rage click. 

  3. Проверьте данные в Debug View. В окне GTM Preview должно появиться событие rage_click. Разверните его и убедитесь, что все переменные заполнены правильно:

  • click_element — название элемента;
  • click_count — количество кликов;
  • page_url — URL страницы.
  1. Проверьте отправку в GA4. Убедитесь, что тег сработал без ошибок. Это можно увидеть в разделе Tags Fired в GTM Debug View.

Шаг 5. Смотрим данные в GA4

После публикации контейнера GTM события начнут поступать в Google Analytics. Проверить их можно несколькими способами:

  • Reports → Realtime — для проверки в реальном времени;

  • Reports → Events — список всех событий, там появится rage_click;

  • Explore → Free form — для детального анализа с параметрами.

Есть возможность создать кастомный отчет, чтобы видеть:

  • элементы, которые чаще всего вызывают rage clicks;

  • страницы, где это происходит;

  • среднее количество кликов в каждом случае;

  • зависимость от типа устройства или браузера.

Тот же подход работает и для других событий из статьи:

  • javascript_error — фиксация ошибок JS;

  • form_validation_attempt — попытки отправки форм;

  • map_marker_click — клики по карте;

  • promo_code_copy — копирование промокодов.

Для каждого события настройте отдельные переменные, триггер и тег. Сначала это выглядит трудоемким, но после первых двух-трех настроек процесс становится почти автоматическим.

Вывод

Data Layer — это не только инструмент для eCommerce. Пять способов, рассмотренных в статье, помогут вам:

  1. Отслеживать JavaScript-ошибки — выявлять технические сбои, которые нарушают функциональность сайта.

  2. Отслеживать взаимодействие с картами — понять географические интересы посетителей.

  3. Фиксировать проблемы с валидацией форм — понять, почему пользователи не завершают оформление заказа.

  4. Анализировать копирование контента — увидеть, какие промокоды или примеры кода наиболее интересны для аудитории.

  5. Выявлять rage clicks — найти элементы, которые работают некорректно и вызывают раздражение.

Главное — каждое событие в dataLayer должно отвечать на конкретный бизнес-вопрос: почему снижается конверсия? Какие элементы работают некорректно? Только тогда аналитика становится полезной, а не кучей цифр в отчетах.

Экспериментируйте с этими примерами, адаптируйте их под свой проект — и ваша аналитика станет гораздо глубже.

Узнайте больше
0
0
0
Обнаружили ошибку? Выделите ее и нажмите Ctrl + Enter.