Отслеживание iframe с помощью Google Tag Manager
В этой статье я собрал все лучшие мировые практики, примеры и способы отслеживания различных действий в iframe с помощью Google Tag Manager, актуальные на сегодняшний день. Материал большой, но интересный! Приятного чтения.
Что такое iframe?
Когда вы заходите на сайт, вы видите на его странице различные элементы. Это могут быть: логотип компании, кнопки, ссылки, строка поиска, текстовые блоки, карта с адресом, форма для ввода информации, видеопроигрыватели, рекламные баннеры, всплывающие окна (попапы), какие-то внешние виджеты (онлайн-чат, форма обратного звонка, социальные сети) и многое другое.
Не все эти элементы на странице могут быть созданы вами (вашей командой или разработчиком) и располагаться на том же домене, что и ваш сайт. И ни ко всем им у вас может быть доступ, особенно когда речь идет о сторонних решениях. Например, на сайте kinopoisk.ru есть возможность приобрести билеты в кино. Не покидая сайт (на Кинопоиске используется виджет от Яндекс Афиши widget.afisha.yandex.ru), вы можете выбрать понравившийся вам фильм, кинотеатр, удобную дату и время, места в зрительном зале и оплатить покупку:
Или когда пользователя перенаправляет на сайт платежной системы с целью ввода его банковских данных и оплаты заказа онлайн. В этом случае действует тот же принцип - во всплывающем окне открывается другой сайт, домен которого отличен от вашего, и в нем происходит часть действий. Затем пользователь может вернуться на ваш сайт и продолжить с ним взаимодействия.
Виджет от платежного агрегатора, который вы используете на сайте, как правило, устанавливается через iframe (но могут быть и другие реализации). Видео YouTube, код которого вы копируете с сайта и вставляете к себе, то же добавляется через iframe:
Так что же такое iframe? Iframe (от англ. inline frame) - один из двух тегов языка HTML (есть теги <iframe> и <frame>), обозначающих фрейм, то есть отдельный/независимый HTML-документ, позволяющий встраивать на веб-страницу документы, видео и интерактивные медиафайлы и прочие части содержимого из других источников. Используя тег <iframe>, вы можете добавить на свой сайт элемент с другого сайта. Например, Google Форму, песню с SoundCloud:
Или даже отчет Google Data Studio:
Другими словами, Iframe - веб-сайт (страница, документ или конкретный элемент), который загружается внутри другого веб-сайта. Тег <iframe> используется, преимущественно, для встраивания другого HTML-документа в текущий. Поскольку не все решения, которые существуют на рынке, необходимо разрабатывать с нуля, мы можем взять уже готовое и внедрить к себе на сайт через iframe. Это безопасный способ для ваших пользователей взаимодействовать со сторонними веб-сайтами, не покидая ваш сайт.
Код с iframe может иметь такой вид:
1 |
<iframe width="500" height="350" src="https://docs.google.com/forms/d/e/1FAIpQLScv046CeWNaDpOc8ZJ3ZITFaf-bq5fxer-rTsVtWV20e81IhA/viewform?embedded=true" frameborder="0" marginheight="0" marginwidth="0">Загрузка…</ iframe> |
А на сайте выглядеть так:
У тега <iframe> есть целый ряд атрибутов, которые вы можете добавить дополнительно:
- align - определяет как фрейм будет выравниваться по краю, а также способ обтекания его текстом;
- allowtransparency - устанавливает прозрачный фон фрейма, через который виден фон страницы;
- frameborder - устанавливает, отображать границу вокруг фрейма или нет;
- height - высота фрейма;
- hspace - горизонтальный отступ от фрейма до окружающего контента;
- marginheight - отступ сверху и снизу от содержания до границы фрейма;
- marginwidth - отступ слева и справа от содержания до границы фрейма;
- name - имя фрейма;
- sandbox - позволяет задать ряд ограничений на контент загружаемый во фрейме;
- scrolling - способ отображения полосы прокрутки во фрейме;
- seamless - определяет, что содержимое фрейма должно отображаться так, словно оно является частью документа;
- src - путь к файлу, содержимое которого будет загружаться во фрейм;
- srcdoc - хранит содержимое фрейма непосредственно в атрибуте;
- vspace - вертикальный отступ от фрейма до окружающего контента;
- width - ширина фрейма.
Есть ли на вашем сайте iframe?
Самый простой способ проверить существование тега <iframe> на странице - это перейти на нее, открыть в браузере консоль разработчика, потом и на вкладку Console и написать следующий код, нажав Enter:
1 |
document.getElementsByTagName('iframe') |
Метод getElementsByTagName возвращает коллекцию элементов, учитывая имя тега. Поскольку мы хотим найти все элементы с тегом <iframe>, то в скобках введите именно такой тег:
Выделяя курсором мыши поочередно каждый из них, он будет подсвечиваться на странице:
С помощью конструкции:
1 |
document.getElementsByTagName('iframe').length |
или аналогичной конструкции в jQuery:
1 2 |
$('iframe').length // вариант №1 jQuery('iframe').length // вариант №2 |
В консоли разработчика вам вернется значение количества iframe, найденных на странице:
Вы также можете перейти в инспектор элементов и в структуре DOM-дерева выделить тот элемент, который считаете Iframe. Если у него будет присутствовать тег <iframe>, то значит он встроен в ваш сайт как отдельный/независимый HTML-элемент.
Другой способ убедиться в том, что перед вами действительно Iframe - это выделить нужный элемент и с помощью контекстного меню (правая клавиша мыши). Если там присутствует вариант Просмотр кода фрейма и Перезагрузить фрейм, то данный элемент на странице является фреймом:
Выбрав этот пункт, вам откроется отдельная страница с HTML-кодом этого элемента. Например, такой:
По сути, это веб-сайт (страница, документ или конкретный элемент), который загружается внутри другого веб-сайта.
Можно ли отслеживать действия в iframe?
Когда вы захотите отслеживать действия в <iframe>, будь то это клик по кнопке или отправка формы, стандартные способы настройки триггеров и тегов в Google Tag Manager перестают работать, а в режиме отладки на шкале событий вы, скорее всего, не увидите никаких действий. Поскольку Iframe - это веб-сайт (страница, документ или конкретный элемент), который загружается внутри другого веб-сайта, то в нем тоже должен быть установлен контейнер GTM. Если его там нет, то у вас нет никаких способов трекинга событий в нет.
Исходя из этого, существует всего два варианта:
- когда у вас нет доступа к исходному коду iframe -> вы не можете отслеживать события с помощью Google Tag Manager;
- когда у вас есть доступ к исходному коду iframe -> вы можете отслеживать события с помощью диспетчера тегов Google.
К сожалению, но это так. Если у вас нет доступа к стороннему решению/виджету (например, Ticketscloud или виджеты от Elfsight), вы не можете установить в него GTM, то и настроить передачу данных между веб-сайтами вы не сможете. Представьте, что на вашем сайте образовалась черная дыра.
Единственный способ, при котором возможно отслеживание действий в iframe - это попросить разработчиков данного решения установить внутрь их iframe контейнер Google Tag Manager, и тогда вы сможете отслеживать необходимые события. Но как показывает практика, это делается крайне редко, даже по официальному запросу к той компании, которая предоставляет доступ к своему виджету. Некоторые сторонние сервисы имеют возможность передавать данные в инструменты аналитики, но только при определенных условиях. Например, отслеживать события, связанные с просмотров видео на YouTube с помощью диспетчера тегов от Google, можно благодаря стандартного триггеру Видео на YouTube.
Рекомендуется к прочтению:
Но что делать, когда готовых решений вашего нашего проекта в Google Tag Manager нет?
Отслеживание iframe с помощью Google Tag Manager
Если у вас есть возможность добавить в сторонний фрейм/виджет код диспетчера тегов от Google, тогда эта настройка для вас. Для удобства создайте отдельный контейнер GTM и попросите разработчика установить данный код в сторонний iframe, который вы будете размещать на своем сайте.
Схематично это будет выглядеть так:
Таким образом, контейнер Google Tag Manager должен быть установлен и на вашем сайте, включая страницу, на которой установлен iframe, и в сам iframe.
Разберем практический пример: на одном сайте (kafedra.digital) располагается вся основная информация об услуге. Виджет с формой подписки размещен на стороннем сайте (gtm-exam.ru) и устанавливается на сайт kafedra.digital с помощью <iframe>:
Чтобы отслеживать заполнение полей формы, отправку формы, клик по кнопке и другие взаимодействия внутри фрейма, код контейнера Google Tag Manager должен быть установлен как на сайте kafedra.digital (основном сайте), так и в iframe, то есть на домене gtm-exam.ru. Если вы не можете добавить код в iframe, вы не сможете отследить взаимодействия с ним.
Примечание: контейнер GTM, установленный на основном сайте kafedra.digital, не сможет отслеживать события пользователей, происходящие внутри iframe. Поэтому его требуется добавлять и там, и там.
Убедитесь, что код диспетчера тегов от Google установлен в iframe. Для этого выделите его и с помощью контекстного меню просмотрите код фрейма:
В нем должен быть установлен контейнер Google Tag Manager, к которому у вас есть доступ.
Проверить наличие установленных контейнеров GTM можно с помощью расширения Google Tag Assistant. Оно покажет вам на странице все найденные теги Google, включая контейнер самого сайта и iframe.
А нажав на сам контейнер iframe, в Tag Assistant вы увидите дополнительную вкладку IFrame, как раз свидетельствующую об установке контейнера таким способом.
Нажав на ссылку напротив IFrame, вам откроется новая вкладка с кодом самого фрейма. Теперь можно переходить к отслеживанию.
Но перед тем, как вы будете настраивать отслеживание, необходимо определиться с последующей терминологией. Поскольку у нас есть основной сайт и сторонний виджет, установленный через iframe, то часто используют такие понятия, как родительский фрейм (parent frame) и дочерний фрейм (child frame).
- Родительский фрейм - это ваш основный сайт, на котором размещена вся информация и установлен код Google Tag Manager;
- Дочерний фрейм - это веб-сайт/страница, которая размещена на вашем основном сайте через iframe, и на которой тоже установлен свой код Google Tag Manager.
В моем примере - kafedra.digital является родительским фреймом, а форма подписки, установленная через iframe с сайта gtm-exam.ru, является дочерним фреймом. При такой реализации мы сможем отслеживать действия, которые совершают пользователи в дочернем фрейме (в iframe), отправлять об этом сообщение, а затем прослушивать его в родительском GTM и использовать в качестве триггера активации с последующей передачей данных в аналитику.
Примечание: все основные настройки будут осуществляться внутри контейнера Google Tag Manager для дочернего фрейма (в iframe), а в родительском мы будем только перехватывать эти события и отправлять информацию в счетчики аналитики.
После того, как вы установите Google Tag Manager в двух фреймах, вам необходимо определиться с перечнем отслеживаний. Клик по кнопке, отправка формы, заполнение полей или иные события?
1. Отслеживание событий от дочернего фрейма к родительскому
Предлагаю начать с самого простого - отслеживания кликов внутри iframe, например, по кнопке Отправить:
Для этого в дочернем фрейме найдите данный элемент и его уникальный атрибут, за который можно будет зацепиться и настроить условие активации триггера в контейнере Google Tag Manager. В моем блоге есть подробная статья и алгоритм отслеживания любых кликов на сайте. Прочитать ее можно здесь. Если кратко, то старайтесь придерживаться следующим настройкам:
- если у элемента есть id, то в качестве условия активации триггера использовать Click ID содержит/равно
- если у элемента есть class, то в качестве условия активации триггера использовать Click Classes содержит/равно
- если нет 1 и 2, то в качестве условия активации триггера использовать Click Element соответствует селектору CSS
Вы можете активировать режим отладки и перейти на основной сайт, чтобы проверить текущие настройки. Примечательно, что поскольку коды контейнера GTM установлены как на сайте, так и внутри iframe, то сам режим отладки отобразит два идентификатора, между которыми вы можете переключаться:
И когда вы перейдете на саму страницу, то при наличии двух контейнеров GTM у вас будет отображаться два блока уведомления об активированном Tag Assistant - один для основного сайта и его GTM, а другой для iframe и его GTM:
Именно так это и работает при наличии iframe на странице и двух контейнеров GTM.
Определившись с условием активации триггера клика по кнопке, создайте его в контейнере iframe. В моем примере клик по кнопке Отправить будет иметь такое условие:
Сохраните изменения и затем создайте тег типа Пользовательский HTML, скопировав нижеприведенный код:
1 2 3 4 5 6 7 8 9 10 11 |
<script> try { var postObject = JSON.stringify({ event: 'iframeClick', form: 'Subscribe' }); parent.postMessage(postObject, 'https://site.ru'); } catch(e) { window.console && window.console.log(e); } </script> |
, где вместо https://site.ru в методе parent.postMessage пропишите домен своего основного сайта (родительского фрейма). У меня это kafedra.digital:
Примечание: в конце домена не должно быть косой черты, поскольку она является частью компоненты пути, а не источника.
В качестве триггера активации выберите триггер, созданный шагом ранее. Для моего примера - это Клик - Все элементы по кнопке Отправить. Итоговый тег в GTM будет выглядеть так:
Вы также можете изменить название события iframeClick на свое (в зависимости от того, какое событие вы отслеживаете) и передать внутри другие параметры события и их значения, а не только form: 'Subscribe', как в примере выше. Вы сами для себя и для своего проекта определяете перечень передаваемых данных.
Примечание: оригинал архива вы можете скачать по ссылке, а затем импортировать контейнеры к себе в диспетчер тегов Google.
Текущий код взят с сайта measureschool.com. В нем используется интерфейс postMessage. Он позволяет общаться друг с другом окнам и фреймам с разных доменов, но только в том случае, если оба сайта согласны и вызывают соответствующие JavaScript-функции. Это делает общение безопасным для пользователя.
Поскольку внешний виджет (форма с подпиской) на сайте kafedra.digital подключен через iframe с другого домена gtm-exam.ru, то нам нужен механизм отправки запроса с дочернего фрейма на родительский, который мы можем прослушать с помощью Google Tag Manager и использовать его как триггер для последующей активации тега и отправки данных в аналитику.
В результате, наше отслеживание сводится к трем основным этапам:
- опубликовать сообщение из нашего дочернего iframe (gtm-exam.ru);
- прослушать сообщение в нашем родительском фрейме (kafedra.digital);
- когда это сообщение будет получено, отправить событие на уровень данных Google Tag Manager.
С помощью пользовательского HTML-тега, который вы создали на предыдущем шаге, как раз и отправляется это сообщение из нашего дочернего iframe. postMessage является методом нашего объекта parent, а строка кода parent.postMessage отправляет сообщение в родительский фрейм. Для этого используются два аргумента:
1 |
parent.postMessage(data, targetOrigin) |
- data (данные для отправки - сообщение, которое мы хотим опубликовать, должно быть строкой. Именно поэтому вы должны использовать метод JSON.stringify на сложных объектах, чтобы браузер смог это поддержать);
- targetOrigin (определяет источник для окна-получателя (родительского фрейма), только окно с данного источника имеет право получить это сообщение).
Если значения в targetOrigin не совпадают с нашим родительским фреймом, сообщение не будет отправлено, поэтому убедитесь, что они верны. Если окна имеют одинаковый источник (протокол, домен, порт), то они могут делать друг с другом все, что угодно. Другими словами, мы можем отправить сообщение с https://site1.ru на https://site2.ru. Таким образом, targetOrigin будет https://site2.ru и домен получателя также будет https://site2.ru.
Все это мы и сделали в коде. Сначала объявили переменную var postObject, которой присвоили объект со строковым типом данных, и в нем передали событие с названием iframeClick и параметр form со значением Subscribe. Как только триггер активации по клику по кнопке Отправить сработает, с помощью команды parent.postMessage будет отправлено сообщение на родительский фрейм https://kafedra.digital, который мы сможем прослушать с помощью еще одного кода.
Для этого перейдите в контейнер основного сайта Google Tag Manager (не контейнер iframe!) и создайте тег типа Пользовательский HTML, скопировав нижеприведенный код:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 |
<script type="text/javascript"> (function(window) { addEvent(window, 'message', function(message) { try{ var data = JSON.parse(message.data); var dataLayer = window.dataLayer || (window.dataLayer = []); if (data.event) { dataLayer.push({ 'event': data.event, 'postMessageData': data }); } }catch(e){} }); // Cross-browser event listener function addEvent(el, evt, fn) { if (el.addEventListener) { el.addEventListener(evt, fn); } else if (el.attachEvent) { el.attachEvent('on' + evt, function(evt) { fn.call(el, evt); }); } else if (typeof el['on' + evt] === 'undefined' || el['on' + evt] === null) { el['on' + evt] = function(evt) { fn.call(el, evt); }; } } })(window); </script> |
В качестве триггера активации выберите All Pages (Просмотр страницы), но можно выбрать активацию только на странице странице с iframe. В Google Tag Manager это выглядит так:
В этом коде вам ничего менять не нужно. Он универсален для всех проектов.
Как только мы начали отправлять сообщение с iframe методом parent.postMessage, нам нужно начать прослушивать его в родительском фрейме. Мы можем это сделать с помощью, прикрепив EventListener к window объекту родительского фрейма. Делается это с помощью данной конструкции:
1 2 3 4 5 6 7 8 9 10 11 12 13 |
function addEvent(el, evt, fn) { if (el.addEventListener) { el.addEventListener(evt, fn); } else if (el.attachEvent) { el.attachEvent('on' + evt, function(evt) { fn.call(el, evt); }); } else if (typeof el['on' + evt] === 'undefined' || el['on' + evt] === null) { el['on' + evt] = function(evt) { fn.call(el, evt); }; } } |
Теперь мы можем прослушать наше сообщение, используя следующий код:
1 2 3 |
addEvent(window, 'message', function(message) { // Сделать/Передать что-то с сообщением }); |
Когда у нас есть код для отправки и перехвата сообщения, осталось только добавить некоторую логику для обработки сообщения всякий раз, когда оно появляется. Для этого используется конструкция dataLayer.push():
1 2 3 4 5 6 7 8 9 10 11 12 |
addEvent(window, 'message', function(message) { try{ var data = JSON.parse(message.data); var dataLayer = window.dataLayer || (window.dataLayer = []); if (data.event) { dataLayer.push({ 'event': data.event, 'postMessageData': data }); } }catch(e){} }); |
Как только наш dataLayer станет доступен, вы будете готовы обработать сообщение. Строка, которая отправляется с методом parent.postMessage, хранится в свойстве data того сообщения, что вы прослушиваете. И все это помещается на уровень данных, с которым вы в дальнейшем можете работать в диспетчере тегов Google:
1 2 3 |
dataLayer.push({ 'event': data.event, 'postMessageData': data |
Вы можете извлечь значение из postMessageData, создав пользовательскую переменную типа Переменная уровня данных. А триггером будет являться то событие, которое вы назначили в контейнере с iframe. В моем примере - это iframeClick.
Сохранив тег, активируйте предварительный просмотр и совершите нужное событие. Если вы все сделали верно, то:
- на дочернем домене (iframe) вы увидите сработанный триггер и активированный тег Sender - postMessage - Click:
- на родительском контейнере GTM отобразится событие, которое вы обозначили в контейнере iframe. В моем примере - это iframeClick:
Открыв API Call, вы увидите, как на уровень данных в переменной postMessageData передались необходимые значения, включая event и form. Чтобы извлечь значение Subscribe из form, создайте переменную уровня данных с именем postMessageData.form:
Тогда эту информацию можно будет передать в счетчики аналитики вместе с совершенным событием:
Если на уровень данных вы передаете и другие параметры события, а не только form, то извлечь их значения можно так же с помощью переменной уровня данных postMessageData.form, только вместо form вы подставляете ваше значение.
После того, как вы с помощью прослушивателя на основном сайте проверили и зафиксировали сообщение, отправляемое с iframe, а также создали переменную уровня данных для передачи дополнительной информации в аналитику, вам осталось создать триггер специального события с вашим названием. У меня - это iframeClick:
Примечание: если вы будете импортировать контейнеры от measureschool.com к себе в диспетчер тегов Google, тогда переменную и триггер создавать не нужно, она будут добавлены к вам в контейнер автоматически. Ваша задача - проверить, чтобы все настройки из архива соответствовали вашим.
После того, как вы это сделаете, останется только создать соответствующие теги для отправки отслеживаемого события в инструменты аналитики. Для Universal Analytics - это тег с типом отслеживания Событие, используя любые параметры, которые лучше всего подходят для вашей настройки отслеживания. Например, так:
Для Google Analytics 4 настройки могут быть такими:
Аналогичным образом в iframe можно отслеживать не только клики по кнопкам, но и другие события - заполнение полей формы, саму отправку формы и т.д. В этом случае в дочернем фрейме и контейнере GTM вы должны создать соответствующие триггеры и добавить их к пользовательскому HTML-тегу, который будет так же отправлять сообщение о совершенном событии, а на основном домене с помощью прослушивателя мы их будем отлавливать и далее использовать для передачи данных в аналитику.
Описанный выше пример является самым простым и эффективным способом отслеживания событий в iframe, когда из дочернего фрейма мы отравляем сообщение, а на родительском фрейме просто его считываем и производим последующие настройки и отправку данных в инструменты аналитики. Самое главное - это получить доступ к iframe, а для этого нужно попросить разработчика или владельца стороннего решения, которое вы используете, установить контейнер Google Tag Manager в этот виджет.
Отдельный контейнер GTM необязателен. Вы можете использовать одну учетную запись диспетчера тегов Google и создать в одном контейнере триггеры таким образом, чтобы они срабатывали только в соответствующем фрейме. Просто это будет чуть сложнее (нужно задавать дополнительные условия по активации триггеров и тегов на соответствующих доменах), и вы можете легко запутаться.
Поскольку наша задача - не только настроить механизм отслеживания различных событий и передать эту информацию от дочернего фрейма на родительский (это лишь один из способов настройки), но еще и связать все данные, которые совершает пользователь, с его уникальным идентификатором в системе аналитики. Отслеживание взаимодействий пользователя на разных доменах (дочернем и родительском), в условиях, когда на обоих присутствуют счетчики аналитики (Universal Analytics, Google Analytics 4), тесно связано с таким понятием, как междоменное отслеживание.
Что это значит? Когда страница загружается в <iframe>, она загружается в стороннем контексте (на другом домене), и любой доступ к данным браузера пользователя внутри <iframe> потребует от браузера разрешения сторонних файлов cookie для <iframe> сайта. Другими словами, контент, размещенный через iframe, имеет другое доменное имя, которое отличается от основного домена, и нам нужен к нему доступ. В этом мы убедились практически, когда настраивали прослушиватель в контейнере диспетчера тегов Google на основном сайте.
Например, есть список разных сайтов:
- osipenkov.ru
- umetrics.ru
- graphanalytics.ru
- techniqa.ru
Если какой-либо из вышеперечисленных сайтов загрузит любой другой сайт из списка в <iframe>, это содержимое будет загружено между сайтами, и любые операции с файлами cookie на встроенной странице потребуют доступа сторонних файлов cookie.
А если список сайтов будет относиться к одному и тому же домену, например:
- edu.osipenkov.ru
- qa.osipenkov.ru
- learn.osipenkov.ru
- osipenkov.ru
То любое взаимодействие между страницами из списка выше будет происходить в контексте одного и того же сайта, и доступ к файлам cookie не будет ограничен.
Примечание: о том, как настраивается междоменное отслеживание в Universal Analytics, читайте здесь; для Google Analytics 4 - здесь.
У Симо Ахавы (simoahava.com) в блоге есть несколько статей, посвященных отслеживанию междоменных фреймов. Некоторые из них уже устарели и не актуальны (те, что связаны с передачей файлов cookie от одного домена к другому), поскольку технологии не стоят на месте, и большинство браузеров теперь имеют защиту от сторонних файлов cookie.
Но самый последний материал очень полезен и имеет для нас ценность. Это отслеживание межсайтовых фреймов без файлов cookie. В нем добавлена информация про Google Analytics 4. В своей статье Симо как раз подробно описывает два способа отслеживания событий в iframe между двумя сайтами без использования файлов cookie. Один из его способов заключается в том, что дочерний фрейм отправляет каждое отдельное dataLayer сообщение родительскому фрейму, так что дочернему фрейму не нужно ничего отслеживать самому. Вместо этого родитель обрабатывает отслеживание дочернего кадра. Фактически, это то, о чем шла речь в этой статье выше. У Симо есть отдельная схема, иллюстрирующая принцип работы:
Вот как работает родительская страница:
- родительская страница начинает прослушивать сообщения от дочернего фрейма сразу после загрузки диспетчера тегов от Google;
- как только родительская страница получает сообщение от дочернего фрейма, она отвечает своим собственным сообщением;
- если дочерний фрейм отправляет сообщение в dataLayer (совместимом формате), родительская страница помещает это сообщение в свой собственный уровень данных.
А вот как реагирует дочерняя страница (iframe):
- страница <iframe> начинает отправлять сообщение родительскому фрейму, как только загружается Google Tag Manager;
- как только родительская страница отправит сообщение, дочерний фрейм получит его и отправит все переданные ему сообщения методом dataLayer.push() на родительскую страницу.
2. Передача идентификатора клиента от родительскому фрейма к дочернему
Другой способ, напротив, передает уникальный идентификатор пользователя (Client ID) от родительского фрейма к дочернему, то есть наоборот. У Симо есть схема, которая демонстрирует принцип работы данного способа:
Вот как работает родительская страница:
- родительская страница начинает прослушивать сообщения <iframe> сразу после загрузки диспетчера тегов от Google;
- как только родительская страница получает сообщение от дочернего фрейма, она начинает опрос трекера Google Analytics до тех пор, пока не будет достигнуто максимальное время ожидания;
- как только средство отслеживания Google Analytics становится доступным, родительская страница отправляет идентификатор клиента обратно в домен <iframe>.
А вот как реагирует дочерняя страница (iframe):
- страница <iframe> начинает отправлять сообщение родительскому фрейму, как только загружается Google Tag Manager;
- как только родительская страница ответит идентификатором клиента (или истечет время ожидания), дочерняя страница перестанет отправлять сообщение.
- дочерняя страница записывает идентификатор клиента (Client ID) в dataLayer.
В блоге Симо оба решения объединены в один набор пользовательских тегов HTML, которые вам необходимо создать в своем контейнере GTM. Один - для родительской страницы, а другой - для дочерней <iframe>. Давайте сделаем это прямо сейчас.
Настройка родительской страницы
Я буду продолжать демонстрацию работы на двух сайтах - kafedra.digital (родительская страница, родительский фрейм) и gtm-exam.ru (дочерняя страница, дочерний фрейм, iframe). Перейдите в свой контейнер Google Tag Manager и создайте тег типа Пользовательский HTML, вставив следующий код:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 |
<script> (function() { // Universal Analytics tracking ID whose _ga cookie to use. // If using GA4, you can leave this setting untouched. var trackingId = 'UA-XXXXX-Y'; // Maximum time in milliseconds to wait for GA tracker to load. // Again, irrelevant for GA4. var maxGATime = 2000; // Set to the origin ("https://www.domain.com") of the iframe you want to communicate with var childOrigin = 'https://www.iframe-domain.com'; // Don't touch anything that follows var pollInterval = 200; var postCallback = function(event) { if (event.origin !== childOrigin) return; if (event.data !== 'childReady' && !event.data.event) return; if (event.data === 'childReady') { // Send event that parent is ready event.source.postMessage('parentReady', event.origin); var pollCallback = function() { // Stop polling if maxTime reached maxGATime -= pollInterval; if (maxGATime <= 0) window.clearInterval(poll); // Only proceed if GA loaded and tracker accessible var ga = window[window['GoogleAnalyticsObject']]; if (ga && ga.getAll) { // Get tracker that matches the Tracking ID you provided var tracker = ga.getAll().filter(function(t) { return t.get('trackingId') === trackingId; }).shift(); // Send message back to frame with Client ID if (tracker) { event.source.postMessage({ event: 'clientId', clientId: tracker.get('clientId') }, event.origin); } // Stop polling if not already done so window.clearInterval(poll); } }; // Start polling for Google Analytics tracker var poll = window.setInterval(pollCallback, pollInterval) } // Push dataLayer message from iframe to dataLayer of parent if (event.data.event) { window.dataLayer.push(event.data); } }; // Start listening for messages from child frame window.addEventListener('message', postCallback); })(); </script> |
Если вы используете отслеживания для Universal Analytics, то в строке:
1 |
var trackingId = 'UA-XXXXX-Y' |
Вам необходимо изменить значение идентификатора отслеживания UA-XXXXX-Y на свой собственный для родительской страницы. Взять его можно в интерфейсе Universal Analytics на уровне ресурса Администратор - Отслеживание - Код отслеживания:
При использовании Google Analytics 4 вы можете оставить этот параметр нетронутым.
В еще одной строке:
1 |
var childOrigin = 'https://www.iframe-domain.com'; |
Вы должны прописать домен дочерней страницы (iframe). Крайне важно, что в конце домена не должно быть косой черты, поскольку она является частью компоненты пути, а не источника. Для моего примера - это будет https://kafedra.digital. В качестве триггера активации вы можете использовать триггер на Все страницы (All Pages), а можете задать дополнительное условие, которое срабатывает только на той странице(ах), где размещен iframe. Итоговый тег в GTM будет выглядеть так:
Симо в своей статье подробно поясняет как работает данный код. Я не буду здесь приводить расшифровку каждой строчки, каждого куска кода. Вы можете открыть его статью и почитать подробности там. Если кратко, то когда дочерний фрейм отправляет методом postMessage на родительскую страницу, прослушиватель запускает и выполняет функцию postCallback. Если полученное сообщение не ожидалось (например, пришло из другого места <iframe> или имеет неправильное содержимое), обратный вызов останавливает выполнение. Если сообщение пришло из нужного <iframe>, первое, что нужно сделать, это уведомить дочерний фрейм о том, что родитель получил сообщение и готов начать двустороннее общение.
Затем window.setInterval начинает опрашивать родительскую страницу каждые 200 миллисекунд до 2 полных секунд по умолчанию. При каждом опросе скрипт проверяет, создан ли трекер Google Analytics. Если да, то скрипт берет уникальный идентификатор пользователя (clientId) из трекера и отправляет обратно в <iframe>, использующий event.source.postMessage(). После этого опрос вручную останавливается, чтобы избежать многократной отправки идентификатора клиента в <iframe>.
По сути, родительская страница должна ждать две вещи:
- чтобы дочерняя страница сигнализировала о готовности получать сообщения;
- чтобы был доступен трекер Google Analytics, чтобы с него можно было вытащить Client ID.
Последний блок кода Симо проверяет, содержит ли сообщение из дочернего фрейма объект со свойством event, и в этом случае он проталкивает весь этот объект на родительскую страницу в dataLayer. Таким образом, родительская страница будет прослушивать dataLayer, пересылаемый из встроенного <iframe>.
Теперь вы можете создавать теги, триггеры и переменные, которые реагируют на эти сообщения. Все триггеры будут иметь триггер специального события. Это связано с тем, что все события, отправленные из фрейма, будут добавляться с префиксом iframe.
Например, если вы хотите активировать тег при нажатии на кнопку Отправить в <iframe> (мой пример выше), триггер на родительской странице будет выглядеть так:
Но это же событие можно прослушивать и на дочерней странице. Так или иначе требуется много ручной настройки в GTM, чтобы все это заработало. Но это может сделать вашу настройку отслеживания гибкой, так как вам не нужно будет встраивать дополнительный код в файл <iframe>.
Каждое сообщение, отправленное из <iframe>, автоматически дополняется некоторой информацией на уровне страницы:
1 2 3 4 5 6 |
iframe: { pageData: { url: "https://gtm-exam.ru/dom/index.php", title: "Пример отслеживания отправки формы" } } |
Если вы хотите обновить теги родительской страницы для использования данных <iframe> на уровне страницы, вам необходимо создать переменные уровня данных для iframe.pageData.url (URL-адрес <iframe> страницы) и iframe.pageData.title (заголовок страницы).
Как только вы все сделаете, вы готовы настроить <iframe> страницу.
Настройка дочерней страницы
В этом руководстве (Симо - в своем, я - в своем) рассматривается довольно типичный случай использования, когда с <iframe> контентом взаимодействуют только как со встроенным элементом на странице. Для этого нужно будет сделать три вещи в контейнере Google Tag Manager на <iframe> странице:
- создать пользовательский тег HTML, который взаимодействует с родительской страницей;
- обновить настройки для всех ваших тегов Google Analytics;
- обновить триггеры для ваших тегов Google Analytics, чтобы они не срабатывали, пока не получат идентификатор клиента (Client ID) от родителя.
Выполним их все поэтапно. Создайте новый пользовательский HTML тег со следующим кодом:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 |
<script> (function() { // If not in iframe, do nothing try { if (window.top === window.self) return; } catch(e) {} // Set to false to prevent dataLayer messages from being sent to parent var sendDataLayerMessages = true; // Set the prefix that will be used in the event name, and under which all // the dataLayer properties will be embedded var dataLayerMessagePrefix = 'iframe'; // Set to parent origin ("https://www.domain.com") var parentOrigin = 'https://www.parent-domain.com'; // Maximum time in milliseconds to poll the parent frame for ready signal var maxTime = 2000; // Don't touch anything that follows var pollInterval = 200; var parentReady = false; var postCallback = function(event) { if (event.origin !== parentOrigin) return; if (event.data.event !== 'clientId' && event.data !== 'parentReady') return; if (event.data.event === 'clientId') { window.dataLayer.push({ event: 'clientId', clientId: event.data.clientId }); } if (event.data === 'parentReady' && !parentReady) { window.clearInterval(poll); if (sendDataLayerMessages) startDataLayerMessageCollection(); parentReady = true; } }; var pollCallback = function() { // If maximum time is reached, stop polling maxTime -= pollInterval; if (maxTime <= 0) window.clearInterval(poll); // Send message to parent that iframe is ready to retrieve Client ID window.top.postMessage('childReady', parentOrigin); }; var createMessage = function(obj) { if (!Array.isArray(obj) && typeof obj === 'object') { var flattenObj = JSON.parse(JSON.stringify(obj)); var message = {}; // Add metadata about the page into the message message[dataLayerMessagePrefix] = { pageData: { url: document.location.href, title: document.title } }; for (var prop in flattenObj) { if (flattenObj.hasOwnProperty(prop) && prop !== 'gtm.uniqueEventId') { if (prop === 'event') { message.event = dataLayerMessagePrefix + '.' + flattenObj[prop]; } else { message[dataLayerMessagePrefix][prop] = flattenObj[prop]; } } } if (!message.event) message.event = dataLayerMessagePrefix + '.Message'; return message; } return false; }; var startDataLayerMessageCollection = function() { // Send the current dataLayer content to top frame, flatten the object window.dataLayer.forEach(function(obj) { var message = createMessage(obj); if (message) window.top.postMessage(message, parentOrigin); }); // Create the push listener for future messages var oldPush = window.dataLayer.push; window.dataLayer.push = function() { var states = [].slice.call(arguments, 0); states.forEach(function(arg) { var message = createMessage(arg); if (message) window.top.postMessage(message, parentOrigin); }); return oldPush.apply(window.dataLayer, states); }; }; // Start polling the parent page with "childReady" message var poll = window.setInterval(pollCallback, pollInterval); // Start listening for messages from the parent page window.addEventListener('message', postCallback); })(); </script> |
В строке:
1 |
var parentOrigin = 'https://www.parent-domain.com'; |
Добавьте домен родительской страницы. Крайне важно, что в конце домена не должно быть косой черты, поскольку она является частью компоненты пути, а не источника. Для моего примера - это будет https://kafedra.digital.
В качестве триггера активации используйте триггер Просмотр страницы с дополнительным условием активации, которое срабатывает только на той странице(ах), где размещен iframe:
Примечание: если <iframe> - это одностраничное приложение (SPA-сайт), вы все равно должны активировать пользовательский HTML-тег только для триггера Просмотр страницы, а не при каждом изменении страницы SPA.
Итоговый тег в GTM будет выглядеть так:
Подробнее о том, как работает этот код, читайте в блоге у Симо Ахавы. Если совсем кратко, то все, что добавляется в массив dataLayer внутри <iframe>, перенаправляется на родительскую страницу, чтобы родительская страница также могла обрабатывать отслеживание взаимодействий и событий внутри <iframe>.
После проделанных настроек вы можете активировать режим отладки Google Tag Manager и проверить, начали ли прослушиваться все текущие события. Если да, то на шкале отладки на родительской странице вы увидите события с префиксом iframe., а на дочерней странице событие clientId, где на уровне данных передается параметр clientId с вашим текущим идентификатором:
Когда вы активируете режим отладки в первый раз, чтобы не запутаться в том, какая страница, где загружается и с каким количеством событий, ориентируйтесь по иконке напротив.
Родительская страница прослушивает dataLayer сообщения, пересылаемые из iframe. Это могут быть iframe.clienId, iframe.gtm.load, iframe.gtm.dom. iframe.gtm.js и т.д. Следовательно, когда вы будете создать триггеры специального события, то вам нужно это будет делать так же с префиксом iframe. Например, все то же отслеживание клика по кнопке Отправить внутри iframe вызывается событие iframe.gtm.click:
Создание триггеров, переменных и тегов
Чтобы отслеживать действия и отправлять их в Google Analytics, определите, где вы их будете отслеживать - на родительской странице или дочерней (iframe). От этого будут зависеть дальнейшие настройки. Поскольку код Симо - универсальный в плане отслеживания (вы можете настраивать отслеживания как на родительской странице, так и на дочерней), то именно вы выбираете ту реализацию, которая подходит для вашего проекта. Если вы будете отслеживать на родительской странице, то все переменные и триггеры будут идти с префиксом iframe. Если на дочерней, то достаточно обычных настроек.
Главное, чтобы ни один из ваших тегов не срабатывал, если Client ID еще недоступен. В этом и заключается основное условие корректной работы междоменного отслеживания - не разрывать сеанс от одного домена к другому, не менять источник трафика и передавать идентификатор клиента (Client ID) от одного сайта к другому. Поэтому вам потребуется обновить существующие триггеры, чтобы они не срабатывали до тех пор, пока clientId не получит допустимое значение.
Но перед этим создайте пользовательскую переменную уровня данных с именем clientId:
Событие clientId на дочерней странице (iframe) передает на уровень данных уникальный идентификатор пользователя. Это можно наблюдать в режиме предварительного просмотра:
Для извлечения этого значения мы и создали переменную уровня данных DLV - clientId. Аналогично с родительской страницей, куда передаются события с префиксом iframe.
Чтобы отслеживать данные в Google Analytics с <iframe> страницы, вам необходимо настроить все теги Google Analytics со следующими параметрами. Для Universal Analytics Поля, которые необходимо задать:
- storage - none (чтобы избежать сбоя трекера, если он не может записать файл cookie идентификатора клиента);
- clientId - переменная уровня данных DLV - clientId, созданная на предыдущем шаге (извлекает значение из dataLayer);
Поскольку файлы cookie в <iframe> больше не будут использоваться для сохранения Client ID, вам необходимо установить значение none для поля storage, а для поля clientId задать значение переменной уровня данных.
Примечание: если вы переопределяете настройки в каждом теге Google Analytics, то вам необходимо добавить поля storage и clientId для всех тегов вручную. Если у вас используется переменная типа Настройки Google Аналитики, тогда вам нужно сделать это один раз в ней.
Вам так же следует обновить триггеры для ваших тегов Google Analytics, чтобы они не срабатывали, пока не получат идентификатор клиента от родителя. Ведь если даже при просмотре страницы тег Google Analytics активируется, но Client ID не будет передан, это приведет к некорректному сбору данных в счетчике:
Для этого создайте триггер типа Специальное событие с названием clientId:
Для тега Universal Analytics с типом отслеживания Просмотр страницы установите такие настройки:
Помимо полей, которые необходимо задать, в качестве триггера активации наряду с All Pages добавьте триггер специального события с clientId, а также триггер исключения от дочерней страницы iframe, в котором дополнительно укажите условие активации - DLV - clientId содержит undefined:
Если Client ID будет содержать undefined (не будет определен), то триггер не будет активировать тег, потому что он добавлен в качестве исключения.
Активировав режим отладки, вы можете проверить промежуточный шаг. Если бы мы использовали в теге только триггер активации All Pages (Container Loaded), тогда бы он сработал до того момента, как Client ID был бы доступен на странице. Но благодаря дополнительному триггеру специального события и триггеру исключения, которые были установлены ранее, тег Universal Analytics активируется после того, как уникальный идентификатор пользователя был определен. Следовательно, данные в аналитику будут отправлены корректно вместе с нужным Client ID пользователя:
Вернемся к примеру выше, когда мы настраиваили клик по кнопку Отправить внутри <iframe>. Используя нужный триггер (у меня это Клик - Все элементы), добавьте к нему дополнительное условие активации триггера DLV - clientId не содержит undefined согласно рекомендации Симо, чтобы он не срабатывал до тех пор, пока не получит допустимое значение Client ID.
Тогда тег для Universal Analytics будет выглядеть так:
Для Google Analytics 4 необходимо использовать способ №1, когда отслеживание событий ведется от дочернего фрейма к родительскому. Поскольку для GA3 и GA4 используются разные подходы в настройке, я рекомендую создавать два отдельных контейнера GTM и устанавливать каждый из них на соответствующий домен. Так меньше вероятность запутаться в этих отслеживаниях.
Резюме
Тема отслеживания iframe на двух разных сайта - одна из самых сложных настроек Google Tag Manager. Про это пишет и сам Симо в своей статье; могу с уверенностью подтвердить и я, после того, как на основе большей части его материала (и других авторов) пытался разобраться в том, как это все устроено, и написать свой. Безусловно, если у вас появилась такая задача, вы должны изначально продумать следующее:
- возможно ли установить контейнер Google Tag Manager на iframe страницу? Если нет, то отслеживание в принципе невозможно;
- в какой счетчик аналитики вы будете отправлять данные? В Universal Analytics или в Google Analytics 4? Или в оба? От этого будет зависеть дальнейшая реализация. Междоменное отслеживание в Universal Analytics выполняется как с использованием параметра связывания allowLinker, так и без использования файлов cookie, что является приоритетной настройкой из-за постоянных ограничений самих браузеров и защиты от сторонних файлов cookie;
- какие события вы хотите отслеживать? Список того, что требуется отслеживать, не менее важен, чем сам способ выполнения. Если это одно событие - это одна степень сложности. Если таких событий много (и скроллинг, и клики по кнопкам, и отправка формы, и отслеживание каких-то вложенных элементов), то это совсем другой уровень настройки. Это нужно продумать с самого начала.
Все, что происходит внутри дочернего фрейма iframe, не будет известно родительскому фрейму, если только не будет отправлено сообщено iframe. И если домен iframe отличается от родительского фрейма, значения cookie будут отличаться по умолчанию.
Именно поэтому самый лучший вариант - это вообще не использовать на сайте iframe, по крайней мере специально. Но если это невозможно в силу разных обстоятельств (у вас должен стоять сторонний виджет оплаты, бронирования, какое-то решение, которое принимает обращения на сайте и т.д.), тогда сначала старайтесь использовать возможности самого сервиса. Некоторые из них имеют готовые настройки отслеживания для самых популярных система аналитики. Если такой функционал у них не заложен, попробуйте данную задачу решить командно вместе с разработчиками данного решения. Если и это затруднительно, попросите их хотя бы установить контейнер Google Tag Manager, к которому у вас есть доступ, а дальше уже выполнять отслеживание.
Ну и если после прочитанной статьи у вас осталось больше вопросов, чем ответов, то начните свое отслеживание iframe с помощью Google Tag Manager именно со способа №1 и кодов из архива от measureschool.com в самом начале статьи. И только спустя какое-то время, когда у вас появится примерное понимание как все это работает, переходить на решение от Симо Ахавы.
Список используемой литературы
- Двусторонняя аналитика партнерского iframe-виджета с помощью Google Tag Manager (habr.com)
- Tracking Iframes: How to Track Conversions in Iframes with Google Tag Manager (measureschool.com)
- How to Track iframes with Google Tag Manager (marketlytics.com)
- Tracking Form Submissions In Iframes - Google Analytics & Iframes, Pt 1 (bounteous.com)
- Tracking Complex Interactions In Iframes On The Same Domain – Google Analytics & Iframes, Pt 2 (bounteous.com)
- Iframe cross-domain tracking in Google Tag Manager (simoahava.com)
- Tracking cross-domain iframes - Upgraded solution (simoahava.com)
- Cross-Domain Measurement for iFrames in Google Analytics (measurementmarketing.io)
- Setting Up Cross-Domain Measurement with Iframes, no cookies (measurementmarketing.io)