GTMのプレビューモードは、公開前後のタグの検証を行うためにとても便利な機能です。
しかし、GTMの動作が不安定になりやすい設定や使い方をしていると、ごく稀に「GTMのプレビュー中だけ問題が発生する」あるいは逆に「GTMプレビュー中だけ問題が発生しない」といった不思議な現象に出会うことがあります。
本コラムでは、こうした現象が起こる主な原因と対処法について詳しくご紹介します。
- 原因1. 要件に関係ない要素の影響を受けるトリガー設計になっている
- 原因2. タグやトリガーが使う変数の準備が間に合っていない
- 原因3. タグの実行順序制御が適切でない
- 原因4. カスタムJavaScript変数設定の中でグローバルJavaScript変数の作成・上書きを行っている
- 原因5. タグ内で作成したグローバル変数と、別のグローバル変数の名前が競合している
- 原因6. 組み込み変数「Debug Mode」を利用してタグやトリガーの動作を制御している
- まとめ
原因1. 要件に関係ない要素の影響を受けるトリガー設計になっている
GTMのプレビューモードを実行すると、デフォルトでは最初に開かれたページのURLに gtm_debug=9999999999999 のようなクエリパラメータが自動で付与されます。
その結果、ページURLを条件にしているトリガーや変数の挙動が変わってしまうことがあります。
たとえば、次のようなトリガー設定は「ページURL全体が特定の文字列と完全一致していること」を条件になっているため、 gtm_debug やgclidのような意図しないクエリパラメータがURLに付くと反応しなくなってしまいます。要件に関係ない要素に影響されないトリガー設計を行うことが重要です。

考えられる対応
この例の場合、次のように条件を分解すればURLにクエリパラメータが付いても影響を受けないようになります。

他にも www. が無くても同じページを見ることができる、PC向けページ・スマートフォン向けページでURLが異なるなどの状況があれば、それらも考慮してトリガー条件を設計する必要があります。URL系変数の使いこなしは以下のコラムをご参照ください。
GTMでよく使うURL系変数の使い分け | アユダンテ株式会社
原因2. タグやトリガーが使う変数の準備が間に合っていない
ページのbody内で作られる変数など、サイト側で用意される変数の一部はGTMが動き出すより少し遅れて用意されることがあるため、「初期化」や「ページビュー」のタイミングで動作するタグ設定やトリガー設定からは、その変数の値を正しく取得できないことがあります。
しかしプレビューモード中はGTMの動作タイミングがわずかに遅くなるため、変数の用意が間に合ってしまい、「プレビューモード中のみ値の取得に成功してしまう」という状況が発生することがあります。
考えられる対応
- サイト側で変数を用意するタイミングを早める
- トリガーの発火タイミングを「ページビュー」から「DOM Ready」に変更して、変数が取得できる状態になってからタグやトリガー設定が動くように調整する(ただし、DOM Readyトリガーは、滞在時間が短いページ・描画に時間がかかるページ・通信が遅い環境などではタグの発火漏れが起きやすくなる点に注意が必要です)
- カスタムイベントとdataLayer変数を活用する(非同期で作られる変数をGTMから取得したいときにも有効です)
一番オススメの対応は「サイト側で変数を用意するタイミングを早める」対応です。
GTMのどのトリガーでも使えるように情報を用意したいときは、以下の「GTMに情報を渡すために最も確実な方法」のように実装するのが最も堅実です。このやり方で用意した変数の値は(上書きされない限り)すべてのトリガー設定で利用できます。
<!DOCTYPE html>
<html lang="ja">
<head>
<meta charset="utf-8">
<title>....</title>
<!-- ... -->
<script>
// ■ GTMに情報を渡すために最も確実な方法
// GTMスニペットより上に配置したコード内でdataLayer.push
window.dataLayer = window.dataLayer || [];
dataLayer.push({
// 注意)初期化のタイミングで発火するタグ・トリガーでも使いたいときは
// eventパラメータは持たせない
'page_type': 'product',
'user_id': '12345678',
'member_status': 'gold',
});
</script>
<script>
// ■ GTMに情報を渡すために次に確実な方法
// GTMスニペットより上に配置したコード内で変数を用意
var foo = 'xxxx';
window.bar = 'yyyy';
</script>
<!-- Google Tag Manager -->
<script>
(function(w,d,s,l,i){
w[l]=w[l]||[];
w[l].push({'gtm.start':new Date().getTime(),event:'gtm.js'});
var f=d.getElementsByTagName(s)[0],
j=d.createElement(s),
dl=l!='dataLayer'?'&l='+l:'';
j.async=true;
j.src='https://www.googletagmanager.com/gtm.js?id='+i+dl;f.parentNode.insertBefore(j,f);
})(window,document,'script','dataLayer','GTM-XXXXXX');
</script>
<!-- End Google Tag Manager -->
<script>
// ■ 特定のイベントに関連する情報は1つのdataLayer.pushにまとめる
// event パラメータを持っているdataLayer.pushなので currency, value, itemsの値は
// 「初期化」「同意の初期化」のタイミングでは利用できないことに注意
window.dataLayer = window.dataLayer || [];
dataLayer.push({
'event': 'view_item',
'currency': 'JPY',
'value': '500',
'items': [{ item_id: '001', item_name: 'XXX', price: 500,}],
});
</script>
<!-- ... -->
</head>
<body>
<!-- ... -->
<script>
// body内で作成した変数はDOM Ready以降のタイミングで動くタグ・トリガーからのみ使用する
// 可能であればhead内GTMより上側へコードを移動させることを推奨
var baz = 'zzzz';
</script>
</body>
</html>
原因3. タグの実行順序制御が適切でない
タグAが発火して処理を完了したあとに、タグBが発火しないと正しく動作しないような場合、発火タイミングの制御が不十分だと問題が起こることがあります。
ページを表示する環境(通信速度や端末の処理能力など)によって、タグの発火タイミングがわずかに前後したり、各タグの処理時間が伸び縮みしたりすることで、本来後続で動くはずのタグBが失敗してしまう可能性があります。
また、プレビューモード中はGTM側の処理が増えるため通常時とはタイミングが変わり、「プレビューモードのときだけタグBが正常に動いてしまう」という状況が稀に発生します。

考えられる対応
必要に応じて以下のような対応を行います。
- タグの発火タイミングを分ける(例 : タグAを初期化トリガー、タグBをページビュートリガー等に設定する)
- タグ配信の優先度設定を利用する
- タグの順序付け設定を利用する(タグAの中に非同期処理が入っている場合に有効。但しタグBの実行タイミングがかなり遅くなる)
- カスタムイベントを利用する
詳しくは以下のコラムをご参照ください。
いまさら聞けない!GTMでタグの配信順序を設定するには? | アユダンテ株式会社
原因4. カスタムJavaScript変数設定の中でグローバル変数の作成・上書きを行っている
通常(プレビューモードを使っていないとき)のGTMでは、タグやトリガーから参照された変数設定だけが評価されて動作しますが、プレビューモード中はページビューやDOM Readyなどのイベントが発生するたびに、すべての変数設定が呼び出されます。
そのため、コード内でグローバル変数を作成・上書きするカスタムJavaScript変数設定が、どのタグやトリガーからも使われていない状態でGTM内に存在していると、プレビューモード中だけタグやサイトの挙動がおかしくなる現象が発生する場合があります。この現象は全てのタグ設定・トリガー設定を削除しても発生し続けます。
グローバル変数(グローバルJavaScript変数)とは
ページ内で動作する、ほぼ全てのスクリプトから参照できる形で作成されたJavaScript変数のことです。グローバル変数として作成された変数の値は、GTMの変数タイプ「JavaScript 変数」を使って値を取得することができるほか、「カスタムHTMLタグ」「カスタムJavaScript変数」のスクリプト内で利用することもできます。
ページ内で用意されたグローバル変数の例

GTM内のグローバル変数を取得する変数設定の例

GTMからグローバル変数の値を取得できている様子

考えられる対応
GTMの変数設定は、GTM内のタグやトリガーを動作させるために必要な情報を用意するための機能です。そのため、ページ側の状態を変化させるような処理を、変数設定の中に書くべきではありません。
(どうしてもグローバル変数をGTMから操作する必要がある場合は、カスタムJavaScript変数ではなく、カスタムHTMLタグの中で処理を実行する必要があります)
カスタムJavaScript変数の中では、次のようなコードの使用は避けましょう。
window.xxx = "something";のようにグローバル変数を作成するコード- 既存のグローバル変数の上書きを行うコード
- グローバル変数がシャローコピー(参照渡し)された変数の値を上書きするコード
最後のケースはエンジニアでもうっかりしてしまうことがあるパターンです。たとえば、ページ側で次のようにグローバル変数が用意されている状況を考えます。

<script>
// 商品詳細情報をitemsとして定義。priceは税抜き商品単価
var items = [
{ item_id: "A001", item_name: "商品A", price: 1000, tax: 100 , quantity: 2},
{ item_id: "B002", item_name: "商品B", price: 2000, tax: 200 , quantity: 1},
];
</script>
このitemsのpriceの値にtaxの値を足し合わせて税込み金額とした情報を、あるタグ設定に渡したいとします。その際、次のようなカスタムJavaScript変数を使うと元のitemsの値が上書きされてしまい、itemsの情報を利用しているすべてのタグ設定に影響が及ぶ危険性があります。
問題のあるカスタムJavaScript変数設定

実行結果: カスタムJavaScript変数設定「cjs – items for sample ads」内の処理によってグローバル変数itemsの値が上書きされたため、itemsの値をそのまま参照しているJavaScript変数設定「js – items」にも影響が及んだ(税込金額への変換・taxプロパティの削除)

問題のコードの中では var gtmItems = items; の処理でグローバル変数itemsの値を別の変数gtmItemsにコピーしてから扱っているため、一見すると安全そうに見えます。しかし今回の例では、この処理にはほとんど意味がありません。
var gtmItems = items; の処理をたとえるなら「滝沢さん(items)」と呼んでいた同僚をプライベートで「タッキー(gtmItems)」と呼べるようにあだ名をつけただけの状態です。滝沢さんもタッキーも同一人物なので、どちらかに起きた変化はもう一方にも影響します。
これは、いわゆる「シャローコピー」や「参照渡し」と呼ばれるJavaScriptの挙動に関係する問題で、コピー元の値が配列・オブジェクトであった場合に発生します。コピー元の値が文字列、数値、真偽値、null、undefinedといった「プリミティブ値」と呼ばれるものであれば、この問題は発生しません。
この例では、カスタムJavaScriptタグのコードを以下のように変えることで対策できます。
問題が起きないように対策したコード例
元の配列に影響を与えず新しい配列を生成する Array.prototype.map() を使うことで問題を回避

function() {
return window.items.map(function(item) {
return {
item_id: item.item_id,
item_name: item.item_name,
price: item.price + (item.tax || 0),
};
});
}
実行結果: カスタムJavaScript変数設定「cjs – items for sample ads」の処理がJavaScript変数設定「js – items」が取得しているグローバル変数itemsに影響を与えなくなった

原因5. タグ内で作成したグローバル変数と、別のグローバル変数の名前が競合している
GTMのタグ設定(主にカスタムHTMLタグ)の中で作成したグローバル変数と、サイト側で定義されているグローバル変数、あるいは他のタグ設定内で作成されたグローバル変数の「名前」が同じになってしまうと、先に作られた変数の値が、後から動作する処理によって上書きされて消えてしまうことがあります。
スクリプトの実行タイミングの微妙な前後でページ読み込みごとに上書きされる側が変わる場合がありますが、特にプレビューモード実行中は、GTMの実行タイミングが通常よりわずかに遅くなるため、サイト側で用意したグローバル変数を、GTM側の処理が上書きしてしまう可能性が高くなります。
イメージとしては、会社のホワイトボードに「社員の予定」を書く欄があり、田中太郎さんが自分の欄「田中」に「PM出社予定」と書いたとします。ところが、別の人が田中二郎さんからの連絡を受けて、その欄を上書きして消し、「有給」と書いてしまいました。
同僚は上書き後のホワイトボードだけを見て、田中太郎さんと一緒に参加するはずだった重要な予定をキャンセルしてしまいます。
このように、「同じ名前を使ってしまったせいで、本来残したかった情報が上書きされてしまう」現象を「名前衝突」と呼びます。

考えられる対応
GTM内のカスタムHTMLでは、できる限りグローバル変数を作成しないことが理想です。
どうしても必要な場合は、他の処理と名前が重なるリスクが低い、十分にユニークな変数名を付けるようにしましょう。
改善前の設定例 : このタグ設定の中だけで使い捨てている deviceType 変数がカスタムHTMLタグ内でグローバル変数として作成されており、サイト内の他スクリプトと名前衝突する危険性がある

改善例1 : GTMの変数設定を活用して、グローバル変数をタグ設定内で作成することを避ける


改善例2 : 変数名を他のスクリプトと衝突する危険性が低い名前に変更する

また、カスタムHTMLタグではなくテンプレートタグを使うようにすることで問題を回避することもできます。
テンプレートタグ vs カスタムHTMLタグ:GTMでマーケターが気をつけるべきこと | アユダンテ株式会社
原因6. 組み込み変数「Debug Mode」を利用してタグやトリガーの動作を制御している
組み込み変数「Debug Mode」はGTMプレビューモード実行中のみ true を返す変数です。この変数設定をタグのトリガー設定に利用している場合、プレビューモード実行中のみタグが発火する・発火しないような設定が意図的に行われています。


変数「Debug Mode」は、GTMプレビュー中のみ console.log() を実行してデバッグしやすくする等の用途で活用できる便利な組み込み変数ですが、乱用するとGTMプレビュー中とそれ以外でGTMの挙動を大きく変えてしまい、プレビューモードの信頼性を落とします。
まとめ
本記事では、GTMプレビューモードという観点から、トラブルを招きやすいGTMの設定例を紹介しました。
これらの問題はプレビューモードが悪いのではなく、わずかな動作タイミングのずれなど、些細な条件の変化によって動作が不安定になりやすい設定を行ってしまっていることが原因です。
こうした問題を避けるためには、次のような点に注意するとよいでしょう。
- GTMのプロダクトとしての設計に沿った使い方を行うこと
- カスタムHTMLタグやカスタムJavaScript変数を扱う際は、JavaScriptの仕様に留意して慎重に実装すること
- 不要になったタグ設定・変数設定は速やかに削除すること
- GTM公開前のプレビューモードによる動作テストに加え、可能であればGTM公開後にもプレビューモードを使わない動作テストを行うこと
これらを意識することで、より安定した計測運用がしやすくなります。







