振動するリンク

現象

CSS の :hover 疑似セレクタを使ってリンクの上にマウスポインタを持っていくとリンクのスタイルが変わるという効果を使用しているサイトはよくあるが、このスタイルの変化が意図せず振動してしまうものをたまに見かける。これはかなりウザイ。

この現象は Firefox ではマウスポインタを特定の場所に置くだけで発生する。IE ではマウスを動かしている間しか発生しないのでほとんど問題にならない。

よくあるのはここで紹介されているような、以下のようなスタイル。

a:hover {
  position: relative;
  top: 2px;
  left: 2px;
}

この場合リンクの領域の上辺付近にマウスポインタを置くと hover に入ったり出たりして振動してしまう。

サンプルでうまく振動できない場合は以下の動画を参照。


http://www.youtube.com/watch?v=tPQclYv7WKI

IE だと上辺をなぞるようにマウスポインタを動かすと振動するが、マウスポインタを置いただけでは振動しない。

原因

まずマウスポインタをリンクの上にのせると、:hover の適用でレイアウトが変化してリンクの領域が移動する。その結果、マウスポインタがリンクの上に位置しなくなり :hover の適用が外れる。するとレイアウトがもとに戻り、リンクがマウスポインタの下に戻ってくるので再び :hover が適用されて… ということが無限に繰り返されることで自動的に振動し続ける。

回避方法

Firefox では以下のようにすると回避できる(全く同じレイアウトにはならないことに注意)。

a:hover {
  position: relative;
  top: 2px;
  padding-left: 2px;
  padding-top: 2px;
}

padding-top を増やしてボックスが下にずれる分を領域を上に広げてキャンセルしている。インライン要素では top と bottom のマージン、パディング等は行の高さに影響を与えない(CSS2 10.8.1)ので、レイアウトに影響を与えない。

一方 left, right のマージン、パディング等はレイアウトに影響するので left:2px をやめて padding-left:2px でずらしている。ただし、こうすると行全体がずれて再レイアウトされるので元々の「リンク部分だけずれる」という仕様とは違うレイアウトになってしまう。マウスポインタを置くと振動が発生するエリアの面積は通常上辺付近に比べて左辺付近のほうがずっと狭いので、左辺付近では振動してもいいと割り切ってもよいかも。

また、パディングを増やすのでリンクに背景色を付けてる場合は矩形が大きくなってしまい「矩形がずれる」という効果に見えなくなってしまう。だからといってパディングのかわりにマージンを増やしても、マージン領域上にポインタを置いても hover は適用されない(Firefox では)。

ボーダー上だと :hover が適用されるのだが、ボーダーの色に親要素の背景色を明示的に設定しないといけないようだ。例えばこんなふうに。

body {
  background: #A1FE9F;
}

a:hover {
  position: relative;
  top: 2px;
  border-top: 2px solid #A1FE9F;
  border-left: 2px solid #A1FE9F;
}

ちなみにボーダの色に transparent を指定すると意図したように表示されない。firefox だとその要素の背景色が、IE だとその要素の文字色が使用されてしまう。

なお IE で上辺をなぞるようにマウスポインタを動かすと振動する現象は上の方法では回避できない。IE では内容領域の上にポインタがある場合のみ :hover が適用されるようだ。このあたりは CSS の仕様上は曖昧だと思う(CSS2 5.11.3)。