Fly higher! Sky is the limit!

webの現場で働く人のブログ

[GA][Googleアナリティクス]iPhone7(iOS 10)safariで クリックイベント計測がうまくいかない時の対処方法。

2016年9月13日にiOS 10が配信されました。
さっそくアップデートしましたが今までの仕様に慣れていたので起動時や目覚ましタイマーを止める際に、一度ホームボタンを押さないといけない新しい仕様が若干煩わしいです。
※ ちなみに自分のiPhoneiPhone 6です。

前置きはさておき本題です。
iOS 10にしてから普段通りクリックイベントの計測をしていたところ...
「あれ!?クリックイベントが正しく計測されてない」という事に気づきました。

前に書いた記事iPhoneなんて怖くない!と思っていたのですが再び、調査しなきゃいけないのかと少し青ざめました、、、がなんとか計測できるようになったので覚書きです。

iOS 10からclickの処理の仕様が変わった?

詳しくはわからないですが前に書いた記事の中でクリックイベント計測がうまく取れない理由として“リンク先に飛ぶ処理の方が埋め込んだイベント計測の発火よりも速く処理されてしまうのが原因”ということを述べました。
そこでeventCallback関数を使用してリンクに飛ぶ前に計測用の処理を入れてしまおう!というのが前回のお話でした。

しかし...

明らかにiOS 9以下で計測できていたものが計測できなくなっているんです。

憶測ですがiOS 10のsafariからviewportのプロパティuser-scalable=noを指定しても画面を拡大できるようになったり独自の仕様を積んでいるのでこのリンクを押してからリンク先へ飛ぶ処理についても若干変更されたのかと思います。(多分)

clickがダメならtouchで計測する。

もうこれしかないかなと思いつつ実装してみたところ無事に計測できるようになったので記しておきます。

実装の流れ

iPhoneiOS)をユーザーエージェントで判定
iPhoneiOS)と他の端末との計測方法を変える
iPhoneiOS)のみ、touchイベントを使い計測する
④他の端末は従来通りの計測方法で計測する

ソース

※ 今回もjQueryを使用しております。

/*①iPhoneiOS系)をユーザーエージェントで判定*/
var appleKit = false;
if(navigator.userAgent.indexOf('iPad') > 0 || navigator.userAgent.indexOf('iPhone') > 0 || navigator.userAgent.indexOf('iPod') > 0){
  appleKit = true;
}

//クリックイベント計測用
var eventGA = function(event,category,action,label,link,target){
  dataLayer.push({
    'event': event,
    'category':category,
    'action': action,
    'label': label,
    'eventCallback' : function(){
      /*別窓指定じゃない時だけ実行*/
      if(!target){
        document.location = link;
        return false;
      }
    }
  });
};

/*②iPhoneiOS)と他の端末との計測方法を変える*/
if(appleKit){
/*③iPhoneiOS)のみ、touchイベントを使い計測する*/

  $(".element").on("touchstart touchmove touchend",function(){
    /* touchstartの座標とtouchendの座標が同じ時 = 疑似的なclick */
    if ('touchstart' == event.type){
      $(this).attr('data-touchstarted', '');
      return;
    }
    /*指を動かしたときは判定しない*/
    if ('touchmove' == event.type){
      $(this).removeAttr('data-touchstarted');
      return;
    }
    /*指をつけたところと指を離したところが同じ場合に処理*/
    if ('undefined' != typeof $(this).attr('data-touchstarted')){
      var link = $(this).attr("href");
      var target = ($(this).attr("target")) ? true : false;
      /*イベント送信!*/
      eventGA("event","category","action","label",link,target);
      
      /*フラグ削除*/
      $(this).removeAttr('data-touchstarted');
    }
  });

}else{
/*④他の端末は本来と同じように計測する*/
  $(".element").on("click" , function(){
    var link = $(this).attr("href");
    var target = ($(this).attr("target")) ? true : false;
    /*イベント送信!*/
    eventGA("event","category","action","label",link,target);
  });
}

説明はソース上にコメントアウトしておきましたが補足です。

  • touchstartのみだとリンクに触れた瞬間に計測が始まってしまうので、touchstart = touchendのときに計測するようにする。
  • eventCallbackは、別窓でない時のみに使用する。(別窓リンクの場合は計測できるので)

「たった1つの要素を計測するのにこれだけ記述するのは、ちょっと...」って気がしますが本気で計測したい場合に使うと良いかもしれません。
暫定対応ではありますが、このやり方で計測していきたいと思います。

余談 Firefox クリックイベント計測ができない場合...?

たまにあるんですけど、Googleアナリティクスの管理画面を見ながら計測のテストをしているとFirefoxだけ計測がいきなり取れなくなることが...(僕だけでしょうか...)
その場合の自分の対処方法なんですが【新規ウィンドウを立ち上げる】と計測できるようになったりします。
※ 別タブやリロードではダメ

2016/11/2 追記

Firefoxやっぱりおかしいなーということでいろいろ試したのですが...

clickだと取れたり取れなかったり別窓ページ遷移の場合や同ページ内のクリックイベント計測であれば問題ないのですが、 同窓ページ遷移の場合にうまくいかない事が多いですね。

そこで、clickからmousedownに変えたところ計測できるようになりましたが...
そうなると今度はキーボードからのクリックイベント計測はできなくなります。
キーボードもすべて網羅するのであれば、また別の実装方法を考えなくてはいけません。

あくまで“マウスを使ってクリックイベントを計測する”ということであれば、mousedownで計測すればよいかもしれませんが、、、要件定義次第ですね。

もし同じ事象でお困りの方は試してみてください。

それでは、また。

[JavaScript]日本語を数値に変換 / 日本語を数値に変換

クイズの回答との照合やパスワードのチェックなどに使えるメソッド。

charCodeAt() メソッド

与えられたインデックスに位置する文字の Unicode コードポイントを10進数値で返す。
(例)"あいうえお"という文字列の場合、"あ" が charCodeAt(0)にあたる。
⇒ 指定されない場合は、デフォルトでcharCodeAt(0)が返ってくる。

String.fromCharCode() メソッド

Unicode の値の指定のシーケンスを使用することによって生成された文字列を返す。
このメソッドは String オブジェクトではなく文字列を返すので、JavaScript内で数値(文字)を指定しなければならない。
(例)String.fromCharCode(12354,12356,12358,12360,12362) ⇒ "あいうえお"

DEMO

See the Pen charCodeAt / String.fromCharCode by @paradox_tm (@takumaro-web) on CodePen.

jQueryを使用してます。

[JQuery]たった9行でInstagram(インスタグラム)の写真一覧をWebサイトに表示する。

準備するもの

参考サイト

InstagramのAPIを使用して自分の投稿した写真をWEBサイトに出力する

instagram api 事始め access_tokenを取得する - Qiita

Instagram APIを取得してWebサイトと連携し、投稿写真を自動に掲載する方法 | 株式会社LIG

この辺の記事を参考にするとよい。

アクセストークンについて

もし「アプリの登録はできたけど、Instagramのアクセストークンが取得できない!」という場合、以下のページにアクセスし「Generate Access Token」をクリックすると良いかもしれないです。

instagram.pixelunion.net

※ ちなみに私はこちらのサイトを利用しました。

準備ができたところで、本題。

HTML

<ul id="js-instalib"></ul>

JavaScript

$(function(){
  var accessToken = 'xxxxxxxx.xxxxxxx.xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx'; //取得したアクセストークンを貼り付ける
  $.getJSON('https://api.instagram.com/v1/users/self/media/recent/?access_token='+accessToken+'&callback=?',function (insta) {
    $.each(insta.data,function (photos,src) {
      if ( photos === 10 ) { return false; } //上限設定
      $('<li><img src="'+src.images.standard_resolution.url+'"></li>').appendTo('#js-instalib');
    });
  });
});

css

お好みで調整

DEMO

See the Pen insta lib by @paradox_tm (@takumaro-web) on CodePen.

編集後記

//api.instagram.com/v1/users/self/media/recent/?access_token=[アクセストークン]にアクセスすると いいね数やコメント、時間、リンク先など様々な情報が載っているので必要に応じてカスタマイズするのもありですね。

参考オプション

//インスタグラム詳細URL
src.link
//いいね数
src.likes.count
//コメント数
src.comments.count
//自分のコメント
src.caption.text

TweenMaxを使ってオープニングアニメーションをつくる。

TweenMaxを使ってオープニングアニメーションをつくる。

日本でもTweenMax(トゥイーンライブラリ)を使用しているリッチサイトが大分増えてきた気がますねー。 大体、動きがかっくいいサイトのソースを覗くと「TweenMax」の文字が見受けられます!

本記事は、TimelineMaxの基本的なアニメーション操作と記述方法のメモ書き。

オープニングアニメーションっぽい適当なDEMO| TimelineMax

See the Pen TweenMax sample by @paradox_tm (@takumaro-web) on CodePen.

以下、ソース抜粋

//TweenMaxを使用するための宣言
var timeLine = new TimelineMax();

//セレクターを指定
var elm1 =  document.getElementById("js-anim-01");
var elm2 =  document.getElementById("js-anim-02");
var elm3 =  document.getElementById("js-anim-03");
var elm4 =  document.getElementById("js-anim-04");
var elm5 =  document.getElementById("js-anim-05");
var elm6 =  document.getElementById("js-anim-06");

//第1引数:セレクター指定.
//第2引数:秒数指定(1000msではなく1秒単位).
//第3引数:アニメーション指定.
//第4引数:オプション指定.

//.toでタイムラインをつくっていく。
//コールバックは、onComplete: function(){}を指定.

//詳しくは以下を参照
//https://greensock.com/get-started-js
//https://ihatetomatoes.net/wp-content/uploads/2016/07/GreenSock-Cheatsheet-4.pdf

timeLine.to(elm1, 1, {opacity: '1', x:0, rotation:360}, "blueGreenSpin")
        .to(elm1, 1, {opacity: '0'})
        .to(elm2, 1, {top: '50%',opacity: '1'})
        .to(elm2, 1, {opacity: '0'})
        .to(elm3, 1, {right: '40%',opacity: '1'})
        .to(elm3, 1, {opacity: '0'})
        .to(elm4, 1, {opacity: '1', width:"50%", top:"100px", ease:Power2.easeInOut})
        .to(elm4, 1, {opacity: '0'})
        .to(elm5, 1, {opacity: '1', x:0, y:0, rotation:1440})
        .to(elm5, 2, {opacity: '0',top:'60%', ease:Power2.easeOut, onComplete:
                      function(){
                        //アニメーションが終わった後の処理
                     }})
        .to(elm6, 1, {opacity: '1',scale:"2"});

CDNの利用

2016年7月16日に最新版の1.19.0をリリースされた模様。 greensock.com

Cheat Sheet

https://ihatetomatoes.net/wp-content/uploads/2016/07/GreenSock-Cheatsheet-4.pdf

触ってみての感想

直感的でわかりやすい!!
他にも色々なオプションがたくさんある!!
Canvas/WebGLなどの制御も可能!!
jQueryも使える!!

胸がワキワキしちゃいますね!

最近見つけたiOSのバグ(?) -JQuery アニメーション-

最近、ほとんどjQueryを使用したアニメーションの実装をしなくなってきておりますが、 たまたまiOSで納品物をチェックしている際に見つけてしまった挙動について記したいと思います。

サイドからスライドするメニューの動きが不自然。

メニューボタンを押すと横からスライドするパターンのメニューですが、 メニューボタンを押して開く時はスムーズなのですが、戻るときの挙動が[最初に瞬間移動をして真ん中あたりにいき、そこからアニメーションが始まるような感じ]になっておりました。

以下、jQueryソースコード(抜粋)

ボタンを押してメニューを開く処理

$('#hoge').animate({
  right:'80%'
});

ボタンを押してメニューを閉じる処理

$('#hoge').animate({
  right:'0'
});

一見、どこにでもありそうな処理ですがこの記述がどうやらiOSでは、 不自然な動きを起こしてしまうようです。

解決方法

ボタンを押してメニューを閉じる処理の数値を0と指定するのではなく、 0%と指定してあげることでスムーズに動くようになりました。

実際に以下のページをiPhone等で確認するわかります。

CodePen - iOS - bug??

もし自社サイトなどで似たような動きをしていた場合、試してみる価値はあるかと思います。

検証環境

iOS 9.1/ 9.2.1 / 9.3.1

深まる謎(余談)

ただこれが逆の場合、この現象が起きないという...。 (誰か教えてください。)

$('#hoge').animate({
  left:'80%'
});

$('#hoge').animate({
  left:'0'
});

最後に

この度の熊本地震により被災されたみなさまとそのご家族の方々に心からお見舞い申し上げます。 また一日も早い復旧がなされますことをお祈りいたします。

[jQuery]自作ブックマークレットで効率化!!~リードタイムの短縮~

世の中には先人が作った素晴らしいアドオンやブックマークレットがたくさんあります。私も様々なアドオンやブックマークレットをブラウザに入れ普段の業務に役立てております。

しかし、すべての作業についてそれらのアドオンやブックマークレットで賄えるわけでもなく

「この繰り返しの作業、どうにかならないか...」
「あんな機能ほしい!」
「あの機能とこの機能が一緒であれば...」

ということがあったりします。

ということで制作者であれば自分たちの現場に合った素敵なブックマークレットを作ってしまおう!というのが今回のお話です。 もちろん生のJSで記述することもできますが、今回は親しみやすいjQueryを使ったブックマークレットの作り方(導入部分)を紹介いたします。

jQueryブックマークレットの記述

javascript:(function(){
!function(d,f,s){s=d.createElement("script");s.src="//j.mp/1bPoAXq";s.onload=function(){f(jQuery.noConflict(1))};d.body.appendChild(s)}(document,function($){
/**************************************************
* BOOKMARKLET by jQuery
***************************************************/


//処理はここまで
});})();

こちらのソースでjQueryを使ったブックマークレットが作製可能になります。

ブックマークレットを作製するときの手順

実際に自分がブックマークレットを作製するときの手順が以下になります。

①:エディターにて編集
②:自分が書いたJSコードをブラウザで実行
③:JSコードを圧縮
④:ブックマーク(お気に入り)に追加

②のところで個人的にオススメの確認方法は、Firefoxのアドオンの「Firebug」を使用する方法です。

ブックマークレットを作製する場合、接頭辞に「javascript:」という記述をいれないといけないのですが 通常のブラウザでデバックしようするとエラーになってしまいます。

しかし、Firebugの場合まるまる自分が記述したJSコードをコピペするだけでデバックができます。

試しに以下のソースをコピーし、Firebugで実行してみます。

javascript:(function(){
!function(d,f,s){s=d.createElement("script");s.src="//j.mp/1bPoAXq";s.onload=function(){f(jQuery.noConflict(1))};d.body.appendChild(s)}(document,function($){
/**************************************************
* BOOKMARKLET by jQuery
***************************************************/

var pageTitle = $('h1').text();
alert('このページのh1は「'+pageTitle+'」です');


//処理はここまで
});})();

手順

1.Firefox上でF12を押しFirebugを起動する

2.コンソールタブを選択し、矢印ボタンを押しコンソール画面を表示
f:id:paradox_tm:20160307172654p:plain

3.ソースを貼り付ける f:id:paradox_tm:20160307172705p:plain

実行できると閲覧しているページのh1をアラートで知らせてくれるかと思います。 f:id:paradox_tm:20160307172717p:plain

あとはJSコードを圧縮すれば自作のブックマークレットの完成です!

Firebug
https://addons.mozilla.org/ja/firefox/addon/firebug/

圧縮する時の注意点

圧縮する時の注意点として、「javascript:」という記述が消えないようにすることです。 オンラインで圧縮する場合、以下のサイトが良いかと思います。

JS Minifier (JavaScriptの圧縮)
https://syncer.jp/js-minifier

自作したブックマークレットの機能紹介

かなり現場に依存してたブックマークレットですが、機能だけ紹介致します。
(少しでもヒントになればと思いリストだけ載せておきます。)

  • 日付を入力するとURLにパラメーターを付与するもの
  • ページの生成時に画像のパス(ディレクトリ)が最新のものになっているかチェックするもの
  • DOCTYPE宣言の確認
  • ページタイトルの確認
  • descriptionの確認
  • パンくずの確認
  • UAイベントの検出の確認
  • 外部ドメインの有無の確認
  • PDFの有無の確認
  • YOUTUBE(動画IDなど)の確認
  • 特定のページへリンクの確認
  • OGP情報の確認(og:type、og:site_name、og:url、og:image、og:title、og:description)
  • Test Server Switcher etc

こちらのブックマークレットは現場からの声を元に実装し提供しました。 確認作業が少しでも効率よく済めば、リードタイムの短縮につながりますね!

おまけ

ブックマークレットを少しでもオシャレ(?)に表現するためにモーダル風なものを用意しました。 よろしければお使いください..。 ※ sampleではページのh1を取得しております。

javascript:(function(){
!function(d,f,s){s=d.createElement("script");s.src="//j.mp/1bPoAXq";s.onload=function(){f(jQuery.noConflict(1))};d.body.appendChild(s)}(document,function($){
/**************************************************
* BOOKMARKLET by jQuery
***************************************************/
$("body").append('<div id="js-GlayLayer"></div><style>#js-GlayLayer{background: #000 none repeat scroll 0 0;height: 100%;left: 0;opacity: 0.6;position: fixed;top: 0;width: 100%;z-index: 9999;}</style>');
var $layer = $('#js-GlayLayer');
$("body").append('<div id="js-INFO" class="fadeInUp"></div><style>#js-INFO {border-radius:8px;border:2px solid #333;position:absolute;left:50%;margin-left: -480px;top:50px;padding: 20px;width:920px;background: #fff;z-index:9999;}.titlestyle03{padding:0 15px 10px 0;}@-webkit-keyframes fadeInUp {0% {opacity: 0;transform: translate3d(0, 100%, 0);}100% {opacity: 1;transform: none;}}@keyframes fadeInUp {0% {opacity: 0;transform: translate3d(0, 100%, 0);}100% {opacity: 1;transform: none;}}.fadeInUp {animation-name: fadeInUp;animation-duration: 0.3s;animation-fill-mode: both;}</style>');
var $info = $('#js-INFO');


/*ここから記述*/
$info.append('<h1 style="margin-bottom:30px;font-size:24px">ページタイトル取得</h1><p style="margin-bottom:30px">'+$('h1').text()+'<p>');


/*レイヤー削除*/
$(document).on('click',$layer,function(){
  $info.remove();
  $layer.remove();
});
//処理はここまで
});})();

[GA]iPhone、iPad等(iOS:Safari)でGoogleアナリティクスでクリックイベント計測がうまくいかない時の対処方法。

※ こちらも合わせてご覧ください。2016/11/1更新 paradox-tm.hateblo.jp

iOSSafariのみブラウザの仕様上、Googleアナリティクス(以下、GA)を使用したイベント計測がうまくいかない事が多々あります。

特に計測がうまくいかない時のパターンが以下です。

  • a要素でリンクを指定していた場合のclickイベント計測
  • a要素でリンクとtarget="_blank"を指定していた場合のclickイベント計測

なぜ計測ができないのか?

iOSSafariのみ指定していたリンク先に飛ぶ処理の方が、埋め込んだイベント計測の発火よりも速く処理されてしまうのが原因のようです。

つまり通常のフローでは、

・click → イベント計測 → リンク先へ移動

ですが、iOSSafariの場合

・click → リンク先へ移動 (イベント計測はスルー)

と処理されてしまうということです。

解決策

二種類の方法でほぼ確実に計測ができるようになりました。

1つ目:eventCallback関数を使用する。

通常のdataLayer.pushの記述

dataLayer.push({
  'event': 'hoge',
  'category':'fuga',
  'action':'piyo',
  'label':'hogehoge'
});

eventCallback関数を使用したdataLayer.pushの記述

dataLayer.push({
  'event': 'hoge',
  'category':'fuga',
  'action':'piyo',
  'label':'hogehoge',
  'eventCallback' : function(){
    //event
  }
});

このeventCallback関数中に飛ぶリンク先を指定してあげることで、 確実にイベントを発火させます。

sampleソース(jQuery使用)

$('#target').each(function(){
  var link = $(this).attr('href');
  $(this).on( 'click' , function(){
    dataLayer.push({
      'event': 'hoge',
      'category':'fuga',
      'action':'piyo',
      'label':'hogehoge',
      'eventCallback' : function(){
        //event
        document.location = link;
      }
    });
    return false; 
  });
});

target="_blank"が使用されている場合は別窓表示に変更するなどの対応が必要。
※ document.locationの場合、同ページ内に表示されてしまいます。

公式ドキュメントにはないですが、Google Tag Manager for Web Trackingdのサンプルソースでも実装されている方法になります。

以下、引用ソース

/**
 * Call this function when a user clicks on a product link. This function uses the event
 * callback datalayer variable to handle navigation after the ecommerce data has been sent
 * to Google Analytics.
 * @param {Object} productObj An object representing a product.
 */
function(productObj) {
  dataLayer.push({
    'event': 'productClick',
    'ecommerce': {
      'click': {
        'actionField': {'list': 'Search Results'},      // Optional list property.
        'products': [{
          'name': productObj.name,                      // Name or ID is required.
          'id': productObj.id,
          'price': productObj.price,
          'brand': productObj.brand,
          'category': productObj.cat,
          'variant': productObj.variant,
          'position': productObj.position
         }]
       }
     },
     'eventCallback': function() {
       document.location = productObj.url
     }
  });
}

Enhanced Ecommerce (UA) Developer Guide  |  Google Tag Manager for Web Tracking  |  Google Developers

2つ目:iPhoneiPad等(iOSSafari)のみ処理を遅延させる。

手順1. ユーザーエージェントでiOSのデバイスかどうかを判定する。

var appleKit = false;
  if(navigator.userAgent.indexOf('iPad') > 0 || navigator.userAgent.indexOf('iPhone') > 0 || navigator.userAgent.indexOf('iPod') > 0){
  appleKit = true;
}

手順2. appleKit = trueの場合のみ遅延させる処理を追加。

if( appleKit === true ) {
/*event1*/ 
  $('#target').each(function(){
      var link = $(this).attr('href');
      $(this).on( 'click' , function(){
        dataLayer.push({
          'event': 'hoge',
          'category':'fuga',
          'action':'piyo',
          'label':'hogehoge'
        });
        var linkFunc = function(){
          document.location = link;
        };
        window.setTimeout( linkFunc, 500 );
      });
      return false;
  });
}else{
/*event1*/
//通常の記述
}

target="_blank"が使用されている場合は別窓表示に変更するなどの対応が必要。

ユーザーエージェントとの出し分けと通常の記述、iOS専用の記述と少し長めの記述になります。

考察

touchstartの座標 = touchendの座標のとき(疑似的なclick)を使用して計測した場合、二重にカウントされてしまうこともありうまくいかなかったり、onmousedownでの発火の場合、clickしていなくてもイベントが発火してしまうという欠点があったり...色々調査した結果、この2つのやり方がベストかと思いました。