簡易に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を使用することになります。そのほうが、ページの読み込みファイル数を削減でき、サイトが高速化できます。

解析内容

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

※ページの読み込み速度は、コメントアウトで記載
 Analytics標準の読込み速度で十分と判断したため

コード:定義部

<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)要修正

コード:処理部

(function(doc, win) {
  // 子要素判定
  function isChildElement(parent, child) {
    if (parent) {
      for (; child; child=child.parentNode) {
        if (parent == child) {
          return true;
        }
      }
    }
    return false;
  }
  // 要素のパス作成
  function createTagPath(element) {
    let array = [];
    let loop = true;
    for (; element&&loop; element=element.parentNode) {
      let text = element.tagName && element.tagName.toLowerCase() || '';
      if (element.id) {
        text += '#'+element.id;
        loop = false;
      }
      if (element.className) {
        text += '.'+element.className.replace(' ', '.');
      }
      array.unshift(text);
    }
    return array.join(' ');
  }
  // イベント送信
  function sendEvent(category, action, label, value) {
    let obj = {
      'eventCategory': category, 
      'eventAction': action, 
      'eventLabel': label,
      'eventValue': value,
      'transport':'beacon', 'nonInteraction':true};
    ga('send', 'event', obj);
  }
  // タイミングイベント送信
  function sendTiming(action, value) {
    let timeObj = {
      'timingCategory': 'time', 
      'timingVar': '', 
      'timingValue': '',
      'transport':'beacon', 'nonInteraction':true
    };
    timeObj.timingVar = action;
    timeObj.timingValue = value? value: Math.round(win.performance.now());
    ga('send', 'timing', timeObj);
  }

  // ページ表示を設定
  function initView() {
    // 特定のページ表示イベント
    // TODO: 404ページ判定変更
    if (doc.getElementById('post-404')) {
      sendEvent('view', '404', location.href, 0);
    }
    // TODO: ドメイン変更
    if (location.host != 'www.bugbugnow.net') {
      sendEvent('view', 'unknown', location.href, 0);
    }
  }

  // クリックイベントを設定
  function initClick() {
    function onClick(e) {
      let action = 'etc';
      let label = e.target.href;
      let m = e.target.href.match(/^https?:\/\/([^\/]+)/);
      if (m && m[1] == location.host) {
        action = 'inbound';
      } else if (m) {
        action = 'outbound';
      } else if (e.target.href.substr(0, 1) == '#'){
        action = 'target';
      } else if (e.target.href.substr(0, 18) == 'javascript:void(0)'){
        action = 'js';
        label = location.href+'@'+createTagPath(a);
      } else {
        action = 'etc';
        label = location.href+'@'+createTagPath(a);
      }
      sendEvent('click', action, label, 0);
    }
    let ankers = doc.getElementsByTagName('a');
    for (let i=0; i<ankers.length; i++) {
      ankers[i].addEventListener('click', onClick);
    }
  }

  // 読了率
  function initScroll() {
    let arrival = 0;
    function onScroll() {
      let top = doc.documentElement.scrollTop || doc.body.scrollTop;
      let wh  = doc.documentElement.clientHeight;
      let dh  = doc.body.clientHeight;
      let arr = 1 - ((dh - (top + wh)) / dh);
      if (arrival < arr) {
        arrival = arr;
      }
    }

    let timer = null;
    win.addEventListener('scroll', function() {
      // 高頻度呼び出し対策
      clearTimeout(timer);
      timer = setTimeout(onScroll, 300);
    });
    win.addEventListener('beforeunload', function() {
      onScroll();
      let arr = Math.floor(arrival * 100);
      let label = '00-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%';
      }
      sendEvent('scroll', 'page', label, arr);
    });
  }

  // ページの読み込み速度
  function initSpeed() {
    if (win.performance) {
      let types = ['DOMContentLoaded', 'load', 'unload'];
      for (let i=0; i<types.length; i++) {
        win.addEventListener(types[i], function() {
          sendTiming(types[i]);
        });
      }

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

  function main() {
    initView();
    initClick();
    initScroll();
  }

//  initSpeed();
  //main();
  win.addEventListener('DOMContentLoaded', main);
})(document, window);

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

参考

コメントを書く