Взломать собственные события GTM для автоматического заполнения контекстной разметки данных

Получите лучший способ использовать атрибуты данных HTML5 для настройки отслеживания. Чистая разметка. Меньше селекторов CSS.

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

Но почему? Взяв множество примеров из повседневной жизни практикующего, он подробно объясняет, как имеет смысл использовать атрибуты data-HTML для хранения информации, относящейся к отслеживанию.

Я не буду здесь пересказывать все примеры, вам обязательно стоит перейти к его статье и проверить их самостоятельно.

Звучит не слишком увлекательно? Тем не менее, это так. Правда, использование атрибутов данных само по себе не ново. Я также часто работаю с атрибутами данных.

Иехошуа, однако, выступает за то, чтобы вывести атрибуты данных на новый уровень, расширив собственные события GTM (например, Click Trigger, Visibility Trigger) для сбора всех атрибутов данных из семантического контекста элемента gtm.element события в данный момент. событие помещается на уровень данных.

То есть все атрибуты data-, которые можно найти прикрепленными к gtm.element OR, прикрепленным к одному из родительских элементов gtm.element (мы называем это прохождением DOM вверх), будут хорошо доступны рядом с GTM. встроенные переменные, такие как elementId.

Подождите, проходите через DOM? Да, если это действительно важно, описанный здесь метод может даже помочь вам оптимизировать метод Симо Ахава Захват правильного элемента. Если вы хотите собрать больше типов атрибутов, чем только данные, вы можете взять код из этой статьи и расширить его в соответствии с вашими потребностями.

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

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

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

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

  1. Один пример электронной коммерции, вкл. все скриншоты, чтобы понять выгоду
  2. Код реализации, который нужно скопировать и вставить
  3. Подробное и техническое объяснение того, как был получен код

Вот так.

Один пример электронной коммерции, вкл. все скриншоты, чтобы понять выгоду

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

Так сейчас выглядит страница категории / beach-relax / accessoires / в этой демонстрации. Вы можете увидеть типичную сетку продуктов, здесь по два элемента в строке.

Теперь давайте проверим (сокращенную) разметку HTML для таблицы товаров. Все, что вы должны здесь отметить, это то, что Shopware уже по умолчанию заполняет атрибуты данных на различных уровнях модели DOM. Так что потенциально есть что отслеживать даже до добавления пользовательских атрибутов данных.

<!-- First the product grid's container -->
<div class=”listing” data-ajax-wishlist=”true” data-compare-ajax=”true” data-infinite-scrolling=”true” data-loadprevioussnippet=”Vorherige Artikel laden” data-loadmoresnippet=”Weitere Artikel laden” data-categoryid=”59" data-pages=”1" data-threshold=”4" data-pageshortparameter=”p”>
  <!-- Then child divs for all the product cards -->
  <div class=”product--box box--image” data-page-index=”1" data-ordernumber=”SW10414" data-category-id=”59">
    [...]
    <!-- In the child divs we have links to the product detail pages, for example wrapping the image.. -->  
    <a href=”[...]" title=”[...]” class=”product--image”>
      <span class=”image--element”>
        <span class=”image--media”>
          <img srcset="[...] 2x” alt=”[...]” title=”[...]”>
        </span>
      </span>
    </a>
    <!-- ... or the product title -->   
    <a href="[...]" class=”product--title” title=”[...]”>
      LEICHTES TUCH TAUPE HELL
    </a>
    [...]
  
  </div>
  [...]
</div>

Предположим теперь, что нас интересует отслеживание кликов по ссылкам в карточках продуктов (типичное действие электронной торговли, называемое щелчком по продукту).

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

Учитывая разметку HTML выше и простой триггер "Все элементы" в Диспетчере тегов, как выглядит событие при нажатии на ссылку с такой информацией о продукте? Вот как:

Помимо встроенных переменных Менеджера тегов, таких как gtm.element и т. Д., Не так много информации, верно?

И как это же событие будет выглядеть, если применить код, приведенный в этой статье? Проверить это:

Вот это потрясающее зрелище! Рядом со встроенными переменными у нас есть еще одно свойство, называемое dataContext, которое содержит всю информацию, собранную из атрибутов данных, найденных при перемещении DOM вверх от gtm.element.

В этом случае с Shopware у вас внезапно появляется информация, например, активна ли бесконечная прокрутка или нет прямо в событии NATIVE GTM click.

Внутри Диспетчера тегов теперь вы можете просто добавить переменную DataLayer «dataContext» и получить доступ ко всем ее свойствам, когда это необходимо, как в этом макете:

Код реализации, который нужно скопировать и вставить

Замените исходный фрагмент GTM расширенной версией, как показано в Суть ниже:

Подробное и техническое объяснение того, как был получен код

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

Начнем с концептуализации, то есть с вопроса о том, как достичь цели.

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

Однако, когда вы посмотрите window.dataLayer.push, вы увидите, что классический Array.prototype.push уже заменен другим библиотекой GTM.

И наше доминирующее условие состоит в том, что мы не можем (читайте: НЕ ДОЛЖНЫ) просто изменить код минифицированной библиотеки GTM, поскольку мы хотим оставаться совместимыми с новыми выпусками самого.

Но наша отправная точка - это именно эта библиотека GTM. Если вы немного приукрасите, то натолкнетесь на этот фрагмент кода:

ze = function() {
            var a = La("dataLayer", []),
                b = La("google_tag_manager", {});
            b = b["dataLayer"] = b["dataLayer"] || {};
            Tc(function() {
                b.gtmDom || (b.gtmDom = !0, a.push({
                    event: "gtm.dom"
                }))
            });
            qe(function() {
                b.gtmLoad || (b.gtmLoad = !0, a.push({
                    event: "gtm.load"
                }))
            });
            var c = a.push;
            a.push = function() {
                var b;
                if (0 < J.SANDBOXED_JS_SEMAPHORE) {
                    b = [];
                    for (var e = 0; e < arguments.length; e++) b[e] = new le(arguments[e])
                } else b = [].slice.call(arguments, 0);
                c.apply(a, b);
                for (re.push.apply(re, b); 300 < this.length;) this.shift();
                return xe()
            };
            re.push.apply(re, a.slice(0));
            A(ye)
        };

Это та часть, где сразу после отправки события gtm.load в переменную dataLayer библиотека GTM заменяет функцию push dataLayer (унаследованную от Array.prototype) пользовательской.

Что это значит для нашей концепции? Это означает, что мы должны найти способ реализовать следующую логику:

  1. Подождите, пока GTM переопределит dataLayer.push
  2. Затем немедленно сохраните функцию push GTM и снова замените dataLayer.push нашей собственной функцией, которая
  3. сначала обрабатывает наши пользовательские данные, а затем вызывает сохраненную функцию push GTM с нашим результатом

Мое решение - это код, представленный в этой статье. Давайте быстро пройдемся по нему:

Запуск уровня данных

Мы инициируем переменную dataLayer и уже делаем резервную копию функции push в свойстве pushStashed

window.dataLayer = window.dataLayer || [];
window.dataLayer.pushStashed = window.dataLayer.push;

Мы определяем push dataLayer с помощью дескрипторов доступа

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

Object.defineProperty(window.dataLayer, 'push', {
  set(y) {

Что происходит, когда GTM заменяет dataLayer.push

Когда GTM хочет переопределить dataLayer.push сейчас, наш установщик запускается с переменной y, содержащей пользовательскую функцию push GTM. Затем мы сохраняем эту функцию в pushStashed. Затем мы переопределяем dataLayer.push, возвращаясь к дескрипторам данных. То есть dataLayer.push снова будет иметь значение, которое является нашей пользовательской функцией.

    this.pushStashed = y;
    Object.defineProperty(this, 'push', {
      writable: true,
      value: function() {

Наша индивидуальная функция

С этого момента наша настраиваемая функция вызывается, когда где-то на нашем веб-сайте вызывается dataLayer.push (). Функция принимает все аргументы - для случая, когда передано несколько аргументов, как в dataLayer.push ({event: 1}, {event: 2}). Сопоставляя все аргументы, ищем свойство gtm.element в каждом из которых. В случае обнаружения мы проходим по DOM вверх, собирая, таким образом, все атрибуты данных и заполняя dataContext.

arguments = [].map.call(arguments, function(event) {
          if (event && event["gtm.element"]) {
            event.dataContext = {}, elem = event["gtm.element"];
            for (; elem && elem !== document; elem = elem.parentNode) {
              [].forEach.call(elem.attributes, function(attr) {
                if (/^data-/.test(attr.name)) {
                  var camelCaseName = attr.name.substr(5).replace(/-(.)/g, function($0, $1) {
                    return $1.toUpperCase();
                  });
                  event.dataContext[camelCaseName] = event.dataContext[camelCaseName] || attr.value;
                }
              });
            }
          }
          return event;
        });

Пусть GTM сделает все остальное

Наконец, мы передаем наш результат функции push GTM, которую теперь можно найти в dataLayer.pushStashed.

        return this.pushStashed.apply(null,arguments);
      }
    });
  },
  get() {
    return this.pushStashed
  },

Обязательный материал

Не забудьте установить configurable: true при переключении на дескрипторы доступа, иначе dataLayer.push не может быть переопределен снова.

  configurable: true
});

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