快適!! GoogleTagManager + JSONで複数ページの要素の管理
こんばんは。早いものでもう年の瀬ですね。 またまたblog放置してしまいましたが、 やっとひと段落&ネタが溜まってきたのでぼちぼち書こうと思います。
早速本題に入りましょう。
GoogleTagManager(=以下GTM)を使って、 複数のページでたくさんのDOM要素を必要とするときあなたならどうしますか? いろんな方法があると思います。
- 特定のイベントでルールで引っ掛けるもよし
- マクロを使って一からJavaScript開発するもよし
様々な方法があると思います。 そこで最近閃いたのですが、JSONを使えば上記の方法よりもっともっと効率よく、 JavaScript開発者と運用者の分離が出来るのではないかと。
JSONとは
JSON(ジェイソン、JavaScript Object Notation)は軽量なデータ記述言語の1つである。構文はJavaScriptにおけるオブジェクトの表記法をベースとしているが、JSONはJavaScript専用のデータ形式では決してなく、様々なソフトウェアやプログラミング言語間におけるデータの受け渡しに使えるよう設計されている。 - wikipediaより
そうです、JavaScriptのオブジェクトベースなので親和性が非常に高いのです。
で、一つここで問題が。
JSONファイルをGTMからどうやって読み込めばいいのか
ではここから実際にどうやってJSONをGTMに落とし込んでいくのか画像を交えて見ていきます。
タグの準備
まず下記のような画像のタグを用意します。
HTML
<script> {{run}} </script>
scriptタグ内からrunというマクロを呼び出す記述だけでOK。 そしてルールはgtm.js*1とgtm.dom*2に等しいとします。
マクロの準備
タグの準備が出来たら次はマクロの準備です。
run - カスタムJavaScript
function () { var ev = {{event}}; var pt, json; // IE8未満除外 if ( !JSON || !JSON.parse || !window.addEventListener ) { return; } switch (ev) { case 'gtm.js': // ページの初期設定 pt = {{url path}}.replace('index.html', ''); json = JSON.parse({{LIST}}); if (!json[pt]) { return; } // gtm.jsから提供されるオブジェクトの拡張 window.dataLayer[0].pageData = json[pt]; break; case 'gtm.dom': if (!window.dataLayer[0].pageData) { return; } {{cacheCollection}} break; default: break; } return; }
{{run}}マクロ内で呼ばれているマクロの解説
- {{event}}は今何のイベントでこのscriptが呼ばれているかを返してくれるマクロ
- {{url path}}はページのパスを文字列で返してくれるマクロ
- {{LIST}}マクロはこれから用意するのですが、こちらの行が非常に大切な部分になるので解説は後ほど
次に、肝となる{{LIST}}マクロを準備します。
LIST - カスタムJavaScript
function () { return '{"/":{"event":"custom1","category":"custom1","selector":[".navbar",".jumbotron",".content-body"],"action":["HEADER","MAINVISUAL","CONTENT"]},"/lounge/":{"event":"custom2","category":"custom2","selector":[".navbar","#under-content","#footer"],"action":["HEADER","CONTENT","FOOTER"]}}'; }
こちらのマクロは何をしているかというと、JSONの文法で記述された文字列を単純にリターンしているだけです。
改行とスペースをトリムする前はこちら。
{ "/" : { "event" : "custom1", "category" : "custom1", "selector" : [".navbar", ".jumbotron", ".content-body"], "action" : ["HEADER", "MAINVISUAL", "CONTENT"] }, "/lounge/" : { "event" : "custom2", "category" : "custom2", "selector" : [".navbar", "#under-content", "#footer"], "action" : ["HEADER", "CONTENT", "FOOTER"] } }
特定のバージョン以降のブラウザにはwindow.JSON*3というオブジェクトがあるので、 これを用いて{{LIST}}の文字列をオブジェクトとして取り扱います。
そして最後にDOM要素を取得する{{cacheCollection}}マクロを準備します。
cacheCollection - カスタムJavaScript
function () { var win = window; var doc = document; var selectors = win.dataLayer[0].pageData.selector; var len = selectors.length; var res = []; var el; for (var i = 0; i < len; i++) { el = doc.querySelector(selectors[i]); if (!el) { continue; } res.push(el); } // data形成 win.dataLayer[0].pageData.nodeListArr = res; return res; }
これですべての準備が終わりました。 どうなるのか実際の実行結果を見てみましょう。
トップページ - /
一連の流れの解説
- gtm.jsのタイミングでdataLayer内のgtm.jsと同じオブジェクト内にpageDataというプロパティを作成し
- {{LIST}}内のJSON形式の文字列をオブジェクトに変換して保存
- gtm.domのタイミングで{{cacheElement}}を実行
- 先ほどのpageData内のselectorを参照し、HTML内の対象を取得しnodeListArr配列として保存しています。
で先ほどのJSONの設計
{ "ページのパス" : { "event" : "好きなイベント名", "category" : "好きなカテゴリー名", "selector" : ["要素のclass名またはid名 - 1", "素のclass名またはid名 - 2"], "action" : ["要素のclass名またはid名 - 1のアクション値", "要素のclass名またはid名 - 2のアクション値"] } }
上記のような形になります。
でも、別のページではHTMLの構造が違うんです。という要件に対しても、 もう一つ別の"ページパス"をJSONに記述して別の要素のclassまたはidを用意すれば対応できます。
下層 - /lounge/
ざっとこんな感じですね。
別のページでも動いてしまうのでは?
という懸念もありますが、実際に見てみましょう。
下層 - /studio/
pageDataオブジェクトが取得できない場合は何もしません! という処理を行っているので別のページに何か影響が出るということはありません。
あとはこのnodeListArrに入っている要素を取得して対となるaction値を取得して_gaq.pushしたりと様々な用途に使えると思います。 また、サイトがリニューアルしてしまった場合でもJavaScript側の処理は変えずにJSONだけ変更をすればOKですね。
これで運用も安心ですね!