スマホ向けにサイトを作っていると、ブラウザの挙動が遅くてUXを低下させてしまうことがあります。例えば、画面をタッチしたときの:hoverの挙動。あと、jQueryのanimate()の挙動。とか。
スマホブラウザはJavaScriptの実行速度が遅いので、animate()の挙動とかはCSSで書くのがベストです。これはよく知られているので、実装されているサイトも多いのではないでしょうか。(あ、具体的な実装はまた次回ご紹介したいと思います。)
今回はですね、あまり話題にならない:hoverのことです。
:hover がおかしい?
マウスカーソルを乗せたときのスタイルを設定するのが:hoverです。スマホだとタッチした瞬間に適用されると思いますよね。でも例えばiPhoneで:hoverを使うと、指を離したときにスタイルが適用されます。ですので、「もう押してないのに…」という場所が「押されたスタイル」のままになってしまいます。また、タッチした瞬間にはなにも反応がないため、タイムラグがあるようにも見えます。「画面の動き」が「実際の指の動き」と違うのは、操作している実感を鈍らせて、使う人を不安にさせます。『ちゃんと押せてるのかな?』という不安がストレスにつながるので、UX的によろしくないですね。
解決するにはtouchstart/touchend
もともと:hoverはマウスカーソル(=画面内のどこかが常にポインティングされている)を前提にしたものなので、タッチ操作のスマホでは使い勝手が悪いようです。そこで、JavaScriptのtouchstartとtouchendというイベントハンドラを使います。
名前のままですが、タッチした瞬間と、指を離した瞬間にイベントが発生します。これを使って、タッチした瞬間DOMにクラスを追加して、指を離したらクラスを削除してあげれば擬似的に:hoverを表現できます。JavaScriptなので、CSSにはできない「気遣い」も、これだと表現できます。例えばjQueryを使うと、以下のようなコードでネイティブアプリと同じくらいスムーズに反応してくれます。
/* まず関数を定義 */ var linkTouchStart = function(){ thisAnchor = $(this); touchPos = thisAnchor.offset().top; //タッチした瞬間のa要素の、上からの位置を取得。 moveCheck = function(){ nowPos = thisAnchor.offset().top; if(touchPos == nowPos){ thisAnchor.addClass("hover"); //タッチした瞬間と0.1秒後のa要素の位置が変わっていなければ hover クラスを追加。 //リストなどでa要素が並んでいるときに、スクロールのためにタッチした部分にまでhover効果がついてしまうのを防止している。 } } setTimeout(moveCheck,100); //0.1秒後にmoveCheck()を実行。 } var linkTouchEnd = function(){ thisAnchor = $(this); hoverRemove = function(){ thisAnchor.removeClass("hover"); } setTimeout(hoverRemove,500); //0.5秒後にhoverRemove()を実行。 //指を離した瞬間にhover効果を切るよりも、要素が変化した手応えを表現できる。 } /* タッチイベントで上記の関数を呼び出す */ $(document).on('touchstart mousedown','a',linkTouchStart); //a要素をタッチ、もしくはマウスクリックしたらlinkTouchStart()を実行。 $(document).on('touchend mouseup','a',linkTouchEnd); //a要素から指を離す、もしくはクリックを終えたらlinkTouchEnd()を実行。
↑なんかコメントが多くて見づらいのでコメント無しバージョンも下に置きます(笑)
var linkTouchStart = function(){ thisAnchor = $(this); touchPos = thisAnchor.offset().top; moveCheck = function(){ nowPos = thisAnchor.offset().top; if(touchPos == nowPos){ thisAnchor.addClass("hover"); } } setTimeout(moveCheck,100); } var linkTouchEnd = function(){ thisAnchor = $(this); hoverRemove = function(){ thisAnchor.removeClass("hover"); } setTimeout(hoverRemove,500); } $(document).on('touchstart mousedown','a',linkTouchStart); $(document).on('touchend mouseup','a',linkTouchEnd);
あとは、CSSに.hoverのスタイルを追加すればOKですね。
いかがでしょう。参考になれば幸いです!