簡易にGoogleAnalyticsを使用する

本記事の対象者

  • 少しでもサイトを高速化したい人
  • 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標準の読込み速度で十分と判断したため

コード:定義部

定義部<script>
(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');
</script>

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

コード:処理部

処理部(function(doc, win) {
  // 子要素判定
  const isChildElement = function(parent, child) {
    if (parent) {
      for (; child; child=child.parentElement) {
        if (parent == child) {
          return true;
        }
      }
    }
    return false;
  };
  // 要素のパス作成
  const createTagPath = function(element) {
    let array = [];
    for (; element; element=element.parentElement) {
      let text = element.tagName || '';
      if (element.id) {
        text = '#'+element.id;
      } else {
        if (element.className) {
          text += '.'+element.className.replace(' ', '.');
        }
        if (element.name) {
          text += '[name="'+element.name+'"]';
        }
      }
      array.push(text);
      if (element.id) { break; }
    }
    return array.reverse().join(' ');
  };
  // イベント送信
  const sendEvent = function(category, action, label, value) {
    ga('send', 'event', category, action, label, value, {nonInteraction:true});
  };

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

  // クリックイベントを設定
  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);
      }
      ga('send', 'event', category, action, label, value);
    }
    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() {
      const y = win.pageYOffset;
      const ch = doc.documentElement.clientHeight;
      const dh = doc.documentElement.scrollHeight;
      const w = dh - ch;
      const arr = y / w;
      if (arrival < arr) {
        arrival = arr;
      }
    }

    let timer = null;
    win.addEventListener('scroll', function() {
      // 高頻度呼び出し対策
      clearTimeout(timer);
      timer = setTimeout(onScroll, 300);
    });
    win.addEventListener('beforeunload', function() {
      onScroll();
      const arr = Math.floor(arrival * 100);
      const label = (Math.ceil(arr / 20) * 20)+'%';
      sendEvent('scroll', 'page', label, arr);
    });
  }

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

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

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

参考