Google タグマネージャーで子孫要素のクリックイベントもきっちり検出する

[Google タグ マネージャー](https://marketingplatform.google.com/intl/ja/about/tag-manager/) （以下 GTM と表記）にはクリックイベントを検出するためのトリガータイプとして「すべての要素」と「リンクのみ」の二種類が用意されています。「すべての要素」を利用するとリンクだけでなくボタンやフォームなどの要素もターゲットに指定することができますが、ターゲットの子孫要素のイベントは検出してくれません。

この仕様は Google Analytics （以下 GA と表記）などの外部サービスにクリックのイベントデータを送出するといったユースケースで問題となる可能性があります。例えば以下のような HTML において `<button>` をイベントターゲットに指定した場合、子要素の `<i>` がクリックされてもトリガーはそのイベントを検出しません。

```html
<button>  <!-- button 要素のクリックは検出される -->
  <i class="material-icons">close</i>  <!-- icon 要素のクリックは検出されない！ -->
</button>
```

この記事では、子孫要素のクリックイベントも含めてターゲットのイベントとして扱う方法について考えてみます。

## TL;DR

- トリガーの発生条件に CSS セレクタでターゲット要素とその子孫要素の両方を指定する。
- カスタム JavaScript の変数として、クリックされた要素に最も近いターゲット要素（の属性値など）を返す関数を作成する。

## シチュエーション

GTM を経由して、クリックイベントをトリガーに GA へイベントデータを送出するシチュエーションについて考えます。例えば以下のような HTML について考えます。

```html:
<body>
  <a href="https://example.com/products/999"
     target="_blank"
     data-tracking-category="product-list"
     data-tracking-action="click-product"
     data-tracking-label="thumbnail">
    <div class="thumbnail-wrapper">
      <p class="product-name"></p>
      <img class="thumbnail" src="thumbnail.jpg">
      <div class="logo-wrapper">
        <img class="logo" src="logo.jpg">
      </div>
    </div>
  </a>
</body>
```

ここで [GTM のコンテナスニペット](https://developers.google.com/tag-manager/quickstart?hl=ja)は省略しています。

ターゲット要素に `data-tracking-category` といった属性を仕込んでおき、この値によってどの要素がクリックされたか識別できるようにしています。 GA のイベント要素については[公式のヘルプページ](https://support.google.com/analytics/answer/1033068?hl=ja)を参照してください。

## Google Tag Manager の設定

### トリガー

まず `data-tracking-category` を属性に持つ要素、およびその子孫要素のクリックイベントを検出するトリガーを作成します。今回は `Click - Data Tracking` という名前にして、以下のように設定します。

- トリガーのタイプ: 「クリック - すべての要素」
- このトリガーの発生場所: 「一部のクリック」
- イベント発生時にこれらすべての条件が true の場合にこのトリガーを配信します: 
    - 「Click Element」
    - 「CSS セレクタに一致する」
    - 「`[data-tracking-category], [data-tracking-category] *`」

`[data-tracking-category]` でターゲット要素を、 `[data-tracking-category] *` でその子孫要素をそれぞれ取得することができます。

### 変数

次にクリックされた箇所の `data-tracking-category` などの値を格納する変数を作成します。通常、トリガーによって検出されたイベントの発生した要素（ここではクリックされた要素）の情報は「自動イベント変数」を使って取得することができます。ただし今回の場合、子孫要素がクリックされた場合に親の `data-tracking-category` を取得する必要があり、「自動イベント変数」ではこれを実現できません。

この問題の解決方法について [Simo Ahava](https://www.simoahava.com/analytics/capturing-the-correct-element-in-google-tag-manager/) さんのブログ記事を参考にさせてもらいました。まず utility 関数として `Find Closest` という変数を作成します。

```js:Find&#32;Closest
function() {
  return function(target, selector) {
    while (!target.matches(selector) && !target.matches('body')) {
      target = target.parentElement;
    }
    return target.matches(selector) ? target : undefined;
  }
}
```

この変数では引数 `selector` に渡された CSS セレクタに一致する要素が見つかるまで `target` から再帰的に親要素を辿ります。この引数に `[data-tracking-category]` を指定することで、 `data-tracking-category` 属性を持つ親要素が取得できるという寸法です。

実際に `data-tracking-category` の値を取得する変数を `Closest Data Tracking Category` という名前で以下のように作成します。

```js:Closest&#32;Data&#32;Tracking&#32;Category
function() {
  var el = {{Find Closest}}({{Click Element}}, '[data-tracking-category]');
  return typeof el !== 'undefined' ? el.getAttribute('data-tracking-category') : undefined;
}
```

`{{Click Element}}` は GTM の組み込み変数で、名前の通りクリックされた要素が格納されます。
これと同様に `Closest Data Tracking Action` と `Closest Data Tracking Label` も作成しておきます。

### タグ

最後に GA に情報を送るためのタグを作成します。

- タグの設定
    - タグの種類: 「Google アナリティクス: ユニバーサル アナリティクス」
    - トラッキング タイプ: 「イベント」
    - カテゴリ: `{{Closest Data Tracking Category}}`
    - アクション: `{{Closest Data Tracking Action}}`
    - ラベル: `{{Closest Data Tracking Label}}`
- トリガー
    - `Click - Data Tracking`

その他 GA の設定などは環境に合わせて適宜行ってください。

## 動作確認

最後にプレビューモードで動作確認します。トラッキングしたい要素およびその子孫要素をクリックし、イベントが発火すること、変数に目的の値が設定されていることが確認できればオーケーです！

!<img width=500 src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/179455/97829c0d-e84d-9d0e-638b-56f3a44e86a6.png">

!<img width=720 src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/179455/ba1a94ad-50c3-83c1-a013-93b55e2e558c.png">