先日、iOS の position:fixed バグについて書いた。
最初は面白がって iOS バグの回避とか、zepto の小技とか書いていたのだけど、実は書いたのは氷山の一角。
iOS はバグだらけで、回避技ばかり作っていたら面白くもなくなり、特に書こうとも思わなくなった。
しかし、iOS の position:fixed の話は参照してくださる方もいたようだ。
じゃぁ、もう少しなんか書いとくか…と思ったけど、いろいろやったはずなのに思い出せない(笑)
回避しちゃうと、バグってのは忘れちゃうもんだから、その場で書いとかないとだめだね。
で思い出したので、hover バグについて。
css で a:hover とかすると、普通はマウスカーソルが乗ったことを感知して絵を変えたりできる。
でも、iOS にはマウスがない。マウスがないけど、タップすると一応 hover は動いてくれる。
しかしこれが、いろいろと厄介な代物なのだ。
まず、お手伝いしているゲームが、「ボタンを押した」ことをユーザーがわかるように、
hover でボタンを光らせようとしたことが発端なのだ。
まぁ、いろいろ CSS を駆使すること前提で、a:hover になると光るようになっていた。
でも、iOS では思ったように動かない。動かない理由はいくつかある。
まず、iOS のバグ。
おそらく Retina ディスプレイと通常のディスプレイのドット数の違いを吸収するプログラムにバグがある。
a:hover が反応するのは、a がタップされたときではない。
a の座標の、ちょうど X Y とも半分の位置をタップされたときにも反応するのだ。
タップした位置を元にすれば、画面左上からの距離が2倍の点に a があれば、hover が反応してしまう。
ただし、反応するのは hover だけで、a がクリックされるわけではない。
また、本来の a の位置にクリックが発生する(iOS の内部処理の関係で 0.3秒後になる)と、そちらにも hover が発生する。
hover で光るボタンがいっぱい並べてあると、タップしたところと違うところが光り、その一瞬後に正しいボタンが光る感じだ。
こうして発生した hover は、指を離しても消えない。
次にどこかに触れ、別の個所に hover が発生すると消える。hover が付いているところは常に一つだけど、その一つは指が離れても付きっぱなしなのだ。
これは、バグではなくて仕様だ。
実は、iOS の hover 動作は、マウスを載せるとメニューが出る、というような、ありがちな操作法のページでも閲覧できるようにするための物なのだ。
だから、1回目のタップでメニューが開き、そのまま開きっぱなしになって欲しい。
2回目のタップでメニューを選ぶと、hover が消えてメニューも消えてよい。
しかし、指を押したときだけ光って欲しい、と言う用途では使えない。
元々 a:hover でボタンを光らせようとした人は、指を押している間だけ光ると考えていたため、先の「押してない個所が光る」問題と併せて、「動きがおかしい。どうにかならないか」と僕に相談してきた。
実は、他にも問題がある。
a タグのように、タップして画面遷移が生じる要素に「表示が変わる」ような仕掛けを付けた場合、タップと同時に hover が発生して、画像が切り替わったまま次の画面に行ってしまうのだ。
他の画面に行くのだから、どんな風になっていようといいじゃない、なんて思ってはいけない。
戻るボタンで戻ってくると、先ほどの「光った」画面のままになっているのだ。
しかも、この際 hover は外れている。
画面遷移している間に hover 状態は変わっているのに、「hover を失った」瞬間に画像を切り替えるタイミングを失ったために、ボタンが光ったままになっている。
これは、画面上に hover は1つだけ、というルールとも関係ない表示上のバグなので、他の個所をタップしても元に戻らない。
光っているボタンを押したうえで、さらに別の個所を押すと消える。
と、いろいろ厄介な問題が hover には多い。
これを回避するのは難しくなく :hover を使わなければよいだけのことだ。
代わりに、hover 相当の動作を javascript で作り出して、class に hover を与える。
すると、:hover の代わりに .hover と書ける。
すでに :hover で書いてしまったところも一括置換で幸せになれる。
具体的には、こういうことだ。
・body や window など、上位の要素に click イベントリスナーを付ける。
たとえ window にリスナーを付けていても、event.target で click が発生した要素がわかる。
・click した要素から parentNode で親をたどり、target.tagName で a タグを見つけたら hover クラスをつけてやる。
僕の例のように a タグではない、別の要素に必要なら、必要な要素につける。
・hover クラスを付ける際には setTimeout で、 hover クラスを一定時間後に消すようにする。
・window の pageshow イベント発生時に、hover class のついているタグを探し出し、すべて hover を消すようにする。(戻るボタンで戻ってきた時の対策)
これでいい。
ついでに、PC で確認できるように、mouseover で同じように hover を与え、mouseout で hover を奪うようなプログラムも作っておくと便利。
class の追加/削除は、jQuery の addClass、removeClass が便利。
target.tagName は大文字なので、 a ではなく A を確認するように。
サンプルコードを見せろ、と言われそうだけど、残念ながらない。
なぜなら、hover の問題と同時に、iOS の click 遅延問題も一緒に解消するようにした巨大なバグ回避ライブラリを作ってしまったから。
大きすぎて簡単に見せられないのだ。
でも、真のプログラマなら上の説明で回避プログラムを組めるはず。
言っていることの意味が全く分からないなら、プログラムだけ示されても適切な改造ができないと思うので、あきらめたほうがよい。
(上の例は a タグにだけ hover が付けばよい、と言う前提で、環境によって改造が必要)
同じテーマの日記(最近の一覧)
関連ページ
別年同日の日記
申し訳ありませんが、現在意見投稿をできない状態にしています。 |