Google タグ マネージャー(GTM)とカスタムデータ属性で行う、お手軽クリックイベント計測

この記事は [弁護士ドットコム Advent Calendar 2020](https://qiita.com/advent-calendar/2020/bengo4com) 18日目の記事です。
昨日の記事は @mimaun さんの [土善旅館で合宿するといいよというダイマ](https://mimaunes.hatenablog.com/entry/20201214/1607934728) でした。
俺…このコロナ禍が終わったら開発合宿するんだ…。


# はじめに

こんにちは。弁護士ドットコム株式会社でUXエンジニアをやっている白井と申します。
普段は社名にもなっている [弁護士ドットコム](https://www.bengo4.com/) というサイトの改善に関わっています。

この記事では、[Google タグ マネージャー](https://marketingplatform.google.com/intl/ja/about/tag-manager/) (GTM)とHTML5の [カスタムデータ属性](https://developer.mozilla.org/ja/docs/Web/HTML/Global_attributes/data-*) を利用して、[Google アナリティクス](https://www.google.com/analytics/web/?hl=ja) (GA) のクリックイベントを簡単に計測する方法についてご紹介します。

私が所属する組織では、毎週金曜に**Tech Focus Day**という取り組みを行なっています。
この時間では、エンジニアがいわゆる技術的負債を解消するために様々な活動を行なっているのですが、その中で得たGTMに関する知見を共有できればと思います。

Webサイトの改善には数値による判断が必須ですが、適切にGAイベントを実装・管理していくための事例として参考にしていただければ幸いです。


# 前提

この記事では、以下を前提にしています。

* 既にGAのアカウントやプロパティが設定済みであること
* 既にGTMのアカウントやコンテナが設定済みであること
* 読者がGTMの基本的な操作 (トリガーやタグの設定など) に慣れていること

また、GAイベントは従来の `ユニバーサル アナリティクス` を想定して設定を行なっています。
最近出た `Google アナリティクス4` には対応できていませんのでご注意ください。


# 実現したいこと

この記事で実現したいイメージについて説明していきます。

まず、GAのイベントには、以下の内容が含まれます。

* カテゴリ (category)
* アクション (action)
* ラベル (label)
* 値 (value)

今回紹介する方式では、カスタムデータ属性を使い、これらを直接HTML上に記述します。

例えば、以下のようなHTMLで表現されるリンクがあるとします。

```html
<a href="/about">
  このサービスについて
</a>
```

このリンクがクリックされた回数をイベントとして計測したい場合、以下のようにclass属性とカスタムデータ属性を追加します。

```html
<a
  href="/about"
  class="gtm-event-click"
  data-ga-event-category="someCategory"
  data-ga-event-action="someAction"
  data-ga-event-label="someLabel"
  data-ga-event-value="someValue"
>
  このサービスについて
</a>
```

`gtm-event-click` というclass属性が追加されていますが、GTMにどの要素がクリックされたかを認識させるために設定しています。必要に応じて、適宜別の名前を付けてください。

また、 `someCategory`, `someAction`, `someLabel`, `someValue` の部分には分析時の要件に応じて、適当な値を設定してください。
もちろん、PHP等のサーバサイド言語で動的に出力してやっても大丈夫です。

クリックイベントを計測したい要素ごとに、このような設定をしていきます。

属性を追加する対象の要素ですが、以下のような基準で選んでやれば大丈夫です。

* リンクの場合は、 `<a>` タグ
* その他の要素の場合、 `<button>` や `<div>` など、クリックされる領域全体を囲む要素


# この方式の特徴

今回ご紹介する方式には、以下のようなメリット・デメリットがあると考えています。


## メリット

この方式にすると何が嬉しいかについて説明します。


### :thumbsup: 計測内容をエンジニアが管理できる

長くサービスを運営していると、イベントを追加したものの、数年経ってこのイベントがいまだに使われているかわからない…といったケースによく遭遇するのではないでしょうか。

今回の方式では、計測しているイベントの内容がソースコード上に記述されるため、バージョン管理が可能になります。
プルリクもあわせて参照すれば、どのような意図でイベント計測を追加したのか把握することができます。

※GTM側にも `ワークスペース` と呼ばれる、ブランチやプルリクに相当する機能や、変更履歴を表示する機能もあるのですが、個人的にはGit & GitHubの組み合わせほどには使いやすくないと感じています。


### :thumbsup: 計測が壊れにくい

今回の方式では、どのイベントでも共通のclass属性とカスタムデータ属性を利用します。
また、イベント計測を追加するたびにGTM側に設定を追加する必要はありません。

そのため、予期せぬ変更によりある時点から計測が行えなくなってしまった…といった事態が発生しにくくなります。

例えば、当社では以下のような事態がたまに発生していました。

* プロダクト側で使ってなさそうなclass属性を削除したら、実はGTM側でイベント計測のために参照しており、計測できなくなってしまった
* プロダクト側でDOM構造を変更したら、GTM側で想定していたものと異なってしまい、計測できなくなってしまった

計測の設定がプロダクトコード側に一元化されているため、こういった事態が起こりにくくなります。


### :thumbsup: コンテナサイズが肥大化しない

あまり知られていないのですが、GTMには `コンテナサイズ` (設定の量)に上限があります。
コンテナサイズが100%を超えてしまうと、それ以上設定を追加することができなくなります。

また、GTM上の設定は、1つのJavaScriptファイルとして丸ごとWebページに読み込まれます。
設定されている量が多ければ多いほど、ブラウザがJavaScriptを解析する負荷が高くなり、表示パフォーマンスに影響が出る恐れがあります。

今回ご紹介する方法では、計測するイベントが多くなってもGTMの設定は増えないため、コンテナサイズが肥大化しません。


### :thumbsup: 不要な計測を捨てやすい

プロダクトコードから、計測用のclass属性やカスタムデータ属性を削除するだけで、容易にイベント計測を中止することができます。
GTM側の設定変更は不要です。


## デメリット

一方で、デメリットもあります。


### :thumbsdown: エンジニアの稼働が前提

この方式で運用するには、イベント計測を追加する度にエンジニアが稼働できる組織体制が前提となります。

本来、タグマネージャーというプロダクトは、マーケティングやデータ分析の担当者が「エンジニアの手を煩わせることなく広告やアクセス解析のタグを追加できる」という点に価値があったと認識しています。

そういう意味ではマーケティングやデータ分析の担当者の自由度は減ってしまいますのでご注意ください。

(一方で、前述のメリットがあるため、中長期的にはこちらの方式がベターだと考えています)


# GTM上での設定方法

以降では、GTM側の設定方法について見ていきます。

これらの設定により、HTML側にclass属性とカスタムデータ属性を追加するだけでクリックイベントが計測できるようになります。


## 前提知識: クリックを計測するための2種類のトリガー

GTMではタグを発火させるための条件を `トリガー` として設定します。

クリックイベントの計測用に、以下の2種類のトリガーが用意されています。

* `クリック - リンクのみ`
* `クリック - すべての要素` 

あまり知られていないのですが、これらには以下のような違いがあります


### ① `クリック - リンクのみ` トリガー

主に `<a>` タグのクリックを計測するトリガーです。

`<a>` タグをクリックした時、通常はページ遷移が発生します。
ページ遷移が発生すると、ブラウザはタブ内の全てのリソース読み込みをキャンセルしてしまいますので、タグの発火(＝JSや画像の読み込み)が完了しない恐れがあります。

そのため、こちらのトリガーには `タグの配信を待つ` というオプションが用意されています。
これを有効にすると、ページ遷移が一定時間(デフォルトでは最大2000ms)待機され、ページ遷移のレスポンスと引き換えにタグの配信が確実に行われるようにすることができます。

一方、この機能はリンククリック時に発生したイベントを一旦止めてタグの配信を待ち、完了したら再度ページ遷移を発生させるという処理を行います。
リンクに既に設定されているイベントハンドラの挙動を阻害する場合もありますので、十分にテストした上で導入することをお勧めします。


### ② `クリック - すべての要素` トリガー

こちらには、 `クリック - リンクのみ` トリガーと異なり、 `タグの配信を待つ` というオプションは用意されていません。

主に、 `<a>` タグ以外の要素 (`<button>`, `<img>`, `<div>` etc.) のクリックを計測するためのトリガーです。
特に `タグの配信を待つ` オプションを利用しないのであれば、 `<a>` タグにも利用可能なようです。


### トリガーによる「クリックされた要素」の違い

今回の方式を実現する上で最大のポイントとなるのが、GTMがどの要素を「クリックされた要素」として認識するかです。

例えば、以下のようなHTMLを考えてみましょう。
`<a>` タグの中に2つの `<span>` タグが含まれています。


```html
<a
  href="/about"
  class="gtm-event-click"
  data-ga-event-category="someCategory"
  data-ga-event-action="someAction"
  data-ga-event-label="someLabel"
  data-ga-event-value="someValue"
>
  <span class="icon"></span>
  <span class="text">このサービスについて</span>
</a>
```


この `<span>` タグがクリックされた時、2つのトリガーでは「クリックされた要素」が以下のように異なります。

* `クリック - リンクのみ` トリガーの場合
    - `<span>` タグを囲む `<a>` タグが「クリックされた要素」になる
* `クリック - すべての要素` トリガーの場合
    - `<span>` タグが「クリックされた要素」になる

この記事でご紹介する方式では、タグの種類によらず、 `クリック - すべての要素` トリガーを利用していきます。
そのため、カスタムデータ属性が設定された要素 (上記の例では `<a>` タグ) を取得するためには、一手間かける必要が出てきます。


## 設定の流れ

それでは、具体的な設定方法を見ていきましょう。

以下の流れで、GTM側を設定していきます。

1. クリック時に `gtm-event-click` class属性やカスタムデータ属性が設定されている要素を取得する
2. クリック時にその要素が取得できたか判定する
3. カスタムデータ属性から、カテゴリやアクションなどのイベント情報を取得する
4. クリック時に発火するトリガーを作成する
5. イベント計測用タグにイベント情報を埋め込む


## 1. クリック時にカスタムデータ属性が設定された要素を取得する

`gtm-event-click` class属性やカスタムデータ属性が設定された要素を取得します。

この記事の一番の肝はこのセクションです。


### 1-1. `Click Element` 組み込み変数を有効にする

まず、GTMの `Click Element` 組み込み変数を有効にします。

`変数` タブを開くと、画面上部に `組み込み変数` のペインが表示されています。

右上から `設定` を選び、 `クリック` グループの中の `Click Element` にチェックを入れます。

![ClickElement.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/155169/c288eff9-8d90-b401-5844-e7d8d9267ebe.png)

設定が完了すれば、 `組み込み変数` のペイン内に `Click Element` が表示されているはずです。


### 1-2. `カスタム JavaScript` 変数を追加する

次に、カスタムデータ属性が設定された要素を取得するための `変数` を作成します。

`変数` タブの `ユーザー定義変数` ペイン右上の `新規` ボタンを押し、変数の新規追加画面を開きます。

`変数のタイプ` として `カスタム JavaScript` を選択します。

![GetClickElement.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/155169/a77a2875-1393-6b4c-6f06-b808776c84b3.png)

ここには、任意のJavaScriptを記述することができます。
本来であれば、書けるからといってGTM内にJavaScriptを書くのは禁じ手なのですが、カスタムデータ属性が設定された要素を取得するために必要なので、最低限の範囲で利用します。

ここで記述するのは、以下のようなJavaScriptです。

```js
/**
  以下を返す関数を返す
  * - クリックされた要素に最も近い先祖(または自分自身)で「gtm-event-click」classが設定されている要素
  * - 存在しない場合はnull
  */
function () {
  return function () {
    var clickedElement = {{Click Element}};
    if (!clickedElement || typeof clickedElement.closest !== 'function') {
      return null;
    }
    return clickedElement.closest('.gtm-event-click');
  }  
}
```

ポイントは以下のとおりです。

* `{{Click Element}}` のように記述すると、先ほど有効化した `Click Element` 組み込み変数を利用することができます。
    - 前述のとおり、ここにはトリガーの種類に応じたクリックされた要素が代入されます。

* [Element.closest()](https://developer.mozilla.org/ja/docs/Web/API/Element/closest) メソッドを使い、クリックされた要素自身、もしくはその先祖の要素で `gtm-event-click` classを持つ要素を取得しています。
    - なお、このメソッドは [IE等の古いブラウザでは利用できません](https://caniuse.com/?search=closest) ので、必要であればポリフィルを適用してください。
    - 上記の実装では、旧ブラウザ対応をサボり、単に `null` を返しています。

* `return function ()〜` とある通り、この変数の戻り値は関数です。後述しますが、これから作成する他の変数でも再利用するため、このような形にしています。

JavaScriptが記述できたら、例えば `GetClickElement` のような名前を付けて保存します。


## 2. クリック時にカスタムデータ属性が取得できたか判定する

クリックされた要素を基点に、カスタムデータ属性が設定された要素を取得することができました。

これを利用し、クリック時にそのような要素が見つかったかどうか判定する変数を作成します。
この変数は、後ほどトリガーの発火条件に利用します。

先ほどと同様に、 `カスタム JavaScript` タイプの `ユーザー定義変数` を新しく追加します。

これには以下のようなJavaScriptを記述します。

```js
/**
  * クリックされた要素に最も近い先祖(または自分自身)に「gtm-event-click」classが設定されているものがあるか調べる
  * 存在する場合はtrue, そうでなければfalseを返す
  */
function () {
  var clickElement = {{GetClickElement}}();
  return Boolean(clickElement);
}
```

ポイントは以下のとおりです。

* `{{GetClickElement}}` とすると、先ほど作成した同名の変数がここに展開されます。よく見ると `()` が付いていますので、戻り値である関数が実行され、カスタムデータ属性が設定された要素が取得されます。
* `Boolean()` で戻り値を真偽値に変換しています。

JavaScriptが記述できたら、例えば `ClickElement_Exists` のような名前を付けて保存します。


## 3. カスタムデータ属性からイベント情報を取得する

次に、カスタムデータ属性が設定された要素からイベント情報を取り出します。

再び `カスタム JavaScript` タイプの `ユーザー定義変数` を新しく追加します。

これには以下のようなJavaScriptを記述します。

```js
/**
  * クリックされた要素の「data-ga-event-category」属性を取得する
  */
function () {
  var clickElement = {{GetClickElement}}();
  return clickElement ? clickElement.dataset.gaEventCategory : null;
}
```

ポイントは以下のとおりです。

* `{{GetClickElement}}` 関数で取得した要素の `.dataset.gaEventCategory` プロパティにアクセスしています。
    - [datasetプロパティ](https://developer.mozilla.org/ja/docs/Web/API/HTMLOrForeignElement/dataset) を利用すると、その要素のカスタムデータ属性を取得することができます。
    - その際、以下のルールで属性の名前を指定してあげる必要があります。
        - 先頭の `data-` は取り除く
        - 単語を区切る `-` は取り除き、単語は大文字で始める
    - つまり、 `data-ga-event-category` 属性には `.dataset.gaEventCategory` プロパティでアクセスすることができるというわけです。

JavaScriptが記述できたら、 `ClickElement_GaEventCategory` のような名前で保存しておきます。

同様に、以下の属性についても新しく変数を作成します。

* `data-ga-event-action` 
* `data-ga-event-label` 
* `data-ga-event-value` 

以上で変数の作成は終了です。


## 4. クリック時に発火するトリガーを作成する

続いて、クリック時に発火するトリガーを作成します。

`トリガー` タブを開き、画面右上の `新規` ボタンを押下します。

トリガーのタイプには、 `クリック` グループの `すべての要素` を選択します。

![ClickAllTrigger.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/155169/93572600-353b-041b-fb75-4c111dd57791.png)


`このトリガーの発生場所` には `一部のクリック` を選択し、以下の条件を設定します。

* `ClickElement_Exists`
* `等しい`
* `true`

これにより、

* クリックされた
* クリックされた要素自身、または先祖の要素に `gtm-event-click` class属性が設定されたものが見つかった

という条件が揃った時にイベント計測用タグが発火するようになります。

設定が完了したら、例えば `GaEvent_Click` のような名前を付けて保存します。


### [参考] なぜ ` クリック - すべての要素` トリガーを利用するか？

前述のとおり、 `<a>` タグをクリックした場合はページ遷移が発生するため、計測精度が下がってしまいます。

一方で、イベント計測したい要素に応じて異なるトリガーを選択するのは運用上面倒なので、ここでは一律で `クリック - すべての要素` トリガーを利用しています。

計測精度が気になる方は、適宜 `クリック - リンクのみ` トリガーの利用も検討してください。
(`gtm-event-link-click` classが付与された要素の場合は `クリック - リンクのみ` トリガーが発火するようにする等)


## 5. イベント計測用タグにイベント情報を埋め込む

最後に、イベント計測用タグの設定を行います。


### 5-1. イベント計測用タグの作成

`タグ` タブを開き、画面右上の `新規` ボタンを押下します。

`タグの設定` ペインを押下し、タグタイプとして `Google アナリティクス: ユニバーサル アナリティクス` を選択します。

(この記事では、新しい `Google アナリティクス: GA4イベント` タグには未対応ですのでご注意ください)


![Tag.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/155169/cd94d958-83af-316e-c4f1-700bdbdb2de8.png)


`トラッキングタイプ` には `イベント` を選択します。

また、`イベント トラッキング パラメータ` には、カスタムデータ属性の値が読み込まれるように、これまで作った変数を以下のとおり設定します。

* カテゴリ: `{{ClickElement_GaEventCategory}}`
* アクション: `{{ClickElement_GaEventAction}}`
* ラベル: `{{ClickElement_GaEventLabel}}`
* 値: `{{ClickElement_GaEventValue}}`

その他の項目についても適宜設定を行なってください。


### 5-2. トリガーの設定

次に、 `トリガー` ペインを選択します。

設定済みのトリガーの一覧が表示されますので、先ほど作成した `GaEvent_Click` トリガーを選択します。

適当な名前を付け、タグを保存します。

GTMの設定は以上となります。


## 6. 計測テスト

最後に、忘れてはいけないのが動作確認です。

`gtm-event-click` class属性とカスタムデータ属性が設定された要素を準備します。

GTMの `プレビューモード` を利用し、クリック時に実際にイベントが計測されているか確認しましょう。
具体的な手順は以下のアユダンテ様の記事が参考になります。

* [新しいGTMプレビューモードの使い方](https://ayudante.jp/column/2020-10-28/11-00/)

うまく設定されていれば `Click` イベントにてタグが発火し、カスタムデータ属性に設定したイベント情報が取得できているはずです。


# おわりに

この記事では、HTMLにclass属性とカスタムデータ属性を追加するだけで、簡単にGAのクリックイベントを計測する方法についてご紹介しました。

イベント計測の実装や管理に悩むエンジニアの皆様の参考になれば幸いです。


明日は @amashigeseiji さんの記事です。