2018-08-12T12:28:58Z

簡易にGoogle Analyticsを使用する

タグマネージャー覚えたくなかったのでanalytics.jsを勉強した結果です。

対象者

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

gtag.jsを使用しない理由

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

ページを解析する

前提

  • ページのHTMLファイル内にga関数を埋め込みたくない
    • JavaScriptコードのみでaddEventListener等でga関数を埋め込む
  • ある程度解析したい
    • クリックイベントを収集する
      • 内部リンク
      • 外部リンク
      • ページ内リンク
      • JavaScript実行
      • ただし、動的に追加したaタグは対象外
    • 読了率を収集する
      • ページ全体を画面内に表示した範囲(0-100)
    • サブミットイベントを収集する
      • サイト内検索
    • 特定のページの表示イベントを収集する
      • 404ページの表示
      • 外部サイト(別ホスト)での表示
        • Google翻訳とか?
    • ページの読み込み速度
      • analytics.js読み込み時
      • DOMContentLoaded
      • load
      • 読了時間(ユーザの閲覧時間)
      • unload

コード:定義部

<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', {
  'page':  location.pathname,
  '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});
}
//]]>

<head>内部に追加する
UA-XXXXXXXXX-X(トラッキングID)要修正

コード:処理部

<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 forms = document.querySelectorAll('form');
  for (let i=0; i<forms.length; i++) {
    forms[i].addEventListener('submit', function(event) {
      let action = 'submit';
      let label = '';
      if (forms[i].className == 'search-form') {
        // サイト内検索(TODO:要formの特定)
        action = 'search';
        label = forms[i].q.value;
      } else {
        label = location.href+'@'+createTagPath(forms[i]);
      }
      ga('send', 'event', {
        'eventCategory':'action', 
        'eventAction': action, 
        'eventLabel': label,
        'eventValue': 0,
        '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:箇所を要修正

参考

 コメントを書く