簡易にGoogle Analyticsを使用する

本記事の対象者

  • 少しでもサイトを高速化したい人
  • Google Tag Manager(gtag.js)を使いたくない人
  • Google Analytics(analytics.js)のサンプルコードが欲しい人

タグマネージャーを使用しない理由

タグマネージャー(gtag.js)を使用しているページを解析するとanalytics.jsを読み込んでいなくても、analytics.jsが読み込まれている。このことから、gtag.jsは、analytics.jsの機能をラップしているだけだと考えられる。そのため、最小環境でGoogle Analyticsを使用するのであれば、analytics.jsだけを使用したほうがページの読み込みファイル数を削減でき、サイトが高速化できる。そのため、タグマネージャーを使用しない。

解析内容

  • クリックイベントを収集する
    • 内部リンク
    • 外部リンク
    • ページ内リンク
    • JavaScript実行
    • ただし、動的に追加したaタグは対象外
  • 読了率を収集する
    • ページ全体を画面内に表示した範囲(0-100)
  • 特定のページの表示イベントを収集する
    • 404ページの表示
    • 外部サイト(別ホスト)での表示
      • Google翻訳とか?
  • ページの読み込み速度
    • analytics.js読み込み時
    • DOMContentLoaded
    • load
    • 読了時間(ユーザの閲覧時間)
    • unload

※ページのHTMLファイル内のタグ毎にga関数を埋め込みたくはないため、JavaScriptコードのみでaddEventListenerを使用してga関数を埋め込む

コード:定義部

<script>
//<![CDATA[
(function(i,s,o,g,r,a,m){i['GoogleAnalyticsObject']=r;i[r]=i[r]||function(){
(i[r].q=i[r].q||[]).push(arguments)},i[r].l=1*new Date();a=s.createElement(o),
m=s.getElementsByTagName(o)[0];a.async=1;a.src=g;m.parentNode.insertBefore(a,m)
})(window,document,'script','https://www.google-analytics.com/analytics.js','ga');

ga('create', 'UA-XXXXXXXXX-X', 'auto');
ga('send', 'pageview', {
  'title': document.title,
  'location': location.href
});
if (window.performance) {
  ga('send', 'timing', {
    'timingCategory':'time', 
    'timingVar': 'create', 
    'timingValue': Math.round(window.performance.now()),
    'transport':'beacon', 'nonInteraction':true});
}
//]]>
</script>

<head>内部に追加する
UA-XXXXXXXXX-X(トラッキングID)要修正
ga('send', 'pageview')?以降のURLが必要な場合、location: location.hrefを指定する
 サイト内検索等を解析したい場合、上記設定が必須となる
ga('send', 'pageview')?以降のURLが不要な場合、page: location.pathnameを指定する

コード:処理部

<script>
//<![CDATA[
(function() {
  // 要素のパス作成
  function createTagPath(element) {
    let array = [];
    for (; element; element=element.parentNode) {
      let text = element.tagName.toLowerCase();
      if (element.id) {
        text += '#'+element.id;
      }
      if (element.className) {
        text += '.'+element.className.replace(' ', '.');
      }
      array.unshift(text);
    }
    return array.join(' ');
  }

  // クリックイベントを設定
  let ankers = document.querySelectorAll('a');
  for (let i=0; i<ankers.length; i++) {
    let a = ankers[i];
    a.addEventListener('click', function() {
      let category = 'etc';
      let label = a.href;
      let m = a.href.match(/^https?:\/\/([^\/]+)/);
      if (m && m[1] == location.host) {
        category = 'inbound';
      } else if (m) {
        category = 'outbound';
      } else if (a.href.substr(0, 1) == '#'){
        category = 'target';
      } else if (a.href.substr(0, 18) == 'javascript:void(0)'){
        category = 'js';
        label = location.href+'@'+createTagPath(a);
      } else {
        category = 'etc';
        label = location.href+'@'+createTagPath(a);
      }
      ga('send', 'event', {
        'eventCategory': category, 
        'eventAction': 'click', 
        'eventLabel': label,
        'eventValue': 0,
        'transport':'beacon', 'nonInteraction':true});
    });
  }

  // 読了率
  let arrival = 0;
  function onArrival() {
    let top = document.documentElement.scrollTop || document.body.scrollTop;
    let wh  = document.documentElement.clientHeight || document.body.clientHeight || document.body.scrollHeight;
    let dh  = document.documentElement.scrollHeight || document.body.scrollHeight;
    let arr = 1 - ((dh - (top + wh)) / dh);
    if (arrival < arr) {
      arrival = arr;
    }
  }
  onArrival();
  window.addEventListener('scroll', onArrival, false);
  window.addEventListener('beforeunload', function() {
    let arr = Math.floor(arrival * 100);
    let label = '0-20%';
    if (arr > 80) {
      label = '80-100%';
    } else if (arr > 60) {
      label = '60-80%';
    } else if (arr > 40) {
      label = '40-60%';
    } else if (arr > 20) {
      label = '20-40%';
    }
    ga('send', 'event', {
      'eventCategory': 'page', 
      'eventAction': 'scroll', 
      'eventLabel': label,
      'eventValue': arr,
      'transport':'beacon', 'nonInteraction':true});
  });

  // 特定のページ表示イベント
  let viewObj = {
    'eventCategory': 'view', 
    'eventAction': '', 
    'eventLabel': location.href,
    'eventValue': 0,
    'transport':'beacon', 'nonInteraction':true
  };
  if (document.querySelector('.post-404')) {
    // 404ページ(TODO:要404ページの特定)
    viewObj.eventAction = '404';
    ga('send', 'event', viewObj);
  }
  if (location.host != 'www.bugbugnow.net') {
    // 外部ページ(TODO:要ドメインの変更)
    viewObj.eventAction = 'unknown site';
    ga('send', 'event', viewObj);
  }

  // ページの読み込み速度
  if (window.performance) {
    let timeObj = {
      'timingCategory': 'time', 
      'timingVar': '', 
      'timingValue': '',
      'transport':'beacon', 'nonInteraction':true
    };
    window.addEventListener('DOMContentLoaded', function() {
      timeObj.timingVar = 'DOMContentLoaded';
      timeObj.timingValue = Math.round(window.performance.now());
      ga('send', 'timing', timeObj);
    }, false);
    window.addEventListener('load', function() {
      timeObj.timingVar = 'load';
      timeObj.timingValue = Math.round(window.performance.now());
      ga('send', 'timing', timeObj);
    }, false);

    // 読了時間
    let time = 0;
    let date = null;
    let isExited = false;
    window.addEventListener('pagehide', function() {
      date = Date.now();
    }, false);
    window.addEventListener('pageshow', function() {
      if (date) {
        time += Date.now() - date;
        date = null;
      }
    }, false);
    window.addEventListener('beforeunload', function() {
      if (isExited) { return; }
      timeObj.timingVar = 'read';
      timeObj.timingValue = Math.round(window.performance.now() - time - (date? Date.now() - date: 0));
      ga('send', 'timing', timeObj);
      isExited = true;
    }, false);
    setTimeout(function() {
      isExited = true;
    }, 10*60*1000);  // 10分以上は、放置とみなして集計しない

    // ページ離脱(放置を考慮すると不要?)
    window.addEventListener('unload', function() {
      timeObj.timingVar = 'unload';
      timeObj.timingValue = Math.round(window.performance.now());
      ga('send', 'timing', timeObj);
    }, false);
  }
})();
//]]>
</script>

</body>の直前に配置する
TODO:箇所を要修正

参考

 コメントを書く