最近の仕事で、ウェブサイトに Google Analytics のユーザーイベント追跡コードを組み込んでいました。HTML のイベントを使ってコードを input にバインドし、ユーザーが特定の欄やボタンをクリックするたびに Google Analytics へ記録を送信するようにしました。この方法自体には問題ありません。ところが、ユーザーが input 欄をクリックしたあとに別のページ(Chrome のタブ)へ切り替え、再び自分のページに戻ってきた(Page onFocus)とき、input の onFocus イベントが自動的に発火することに気づきました!
DevTools をクリックしてからページに戻ると、input の onFocus も発火します。しかしクリックしたのは input 欄ではなくページです。ほかのページやほかのアプリに切り替えても、同じ現象が起きます。
実のところ、ページが onFocus になったときに input も自動的に onFocus になる理由は理解できるもので、バグとは言えません。ただ、ここではユーザーの行動を追跡しているので、その観点から見ると、ページの読み込み時にユーザーは input をクリックしていないのに、フォーカスされてしまうのは困ります。そのため、これを防ぐ方法を考える必要がありました。
私の解決策は、Page onFocus のイベントとフラグ変数を組み合わせることです。コードは次のとおりです。
var flag = false;
$("input:text").focus(function(e){
if(flag == true) {
flag = false;
return false;
}
$("body").append("input focus!<br>");
});
window.onfocus = function() {
$("body").append("page focus!<br>");
flag = true;
setTimeout(function(){
flag = false;
}, 300);
}ページが onFocus になったら、フラグを true にします。Page onFocus の後に現在の input の onFocus が自動的に発火することはわかっています。input の onFocus でフラグが true かどうかを確認し、true なら直前のイベントが Page onFocus だったことを意味するので、イベントの処理は実行せず、フラグを false に戻します。こうすることで、その後のユーザーの通常のクリックは、きちんとイベントを発火させられます。
Chrome では Page onFocus が input の onFocus より先に発火するかもしれませんが、ほかのブラウザでは実装や順序が異なる可能性があります。このような事態は JavaScript のイベントでは珍しくないので、保険として setTimeout も追加しています。
