タイトルだけではどんな現象かわかり難いかもしれませんが、検索でこのページに辿り着いた人は直ぐに理解できると思います。

百聞は一見に如かず。

こんな感じです。

このサンプルのボタンの上辺にマウスオーバーするとボタンがプルプルすると思います。

また、このサンプルでは後に続くテキストの位置もズレるようにしています。

この現象がなぜ起こるのか意外と理解されていないのと、ボタンを動かすサンプルを掲載しているサイトでも同じ現象が起こる状態のまま掲載しているサイトが多いので解説してみようと思いました。

原因は何なのか?

原因が理解できれば修正は容易です。

原因を究明するには、まずはどのような動きになっているのか理解しなければなりません。このボタンの動きは以下のようになっています。

  • ボタンにマウスオーバーすると、translateY(6px)でボタンが下に6px動く
  • ボタンにマウスオーバーすると、border-bottom: 0px none;でボーダーがなくなる

動きとしてはこれだけです。

ここではtranslateYを使用していますが、marginやpositionなどを使用している場合も原理は同じです。

では、ボタンがプルプルする理由ですが次のような流れでプルプルします。

  1. ボタン(a要素)の上辺をマウスオーバーするとtranslateY(6px)が効きa要素が6px縮みます。
  2. するとどうでしょう。今、マウスを置いた位置にはa要素が存在しなくなってしまいます
  3. a要素が6px縮んでしまったことによってマウスを置いた位置にa要素がいなくなったということはa要素にマウスオーバーした状態ではなくなってしまいます。
  4. つまりa:hoverで指定しているCSSが効かなくなり、a要素は元の大きさに戻ろうとします。
  5. すると今度はマウスを置いた位置にa要素が戻ってきます
  6. そうなればまたa:hoverで指定しているCSS(translateY(6px))が効き、a要素が6px縮みます
  7. 以下、無限ループでプルプルするというわけです。

図解するとこんな感じです。

ボタンがプルプルしてしまう原因

この後に解決方法を書きますが、原因を読んだだけでピンと来た人は自分で修正してみると力がつくと思います。

解決方法

言葉で解決方法を説明するのであれば、a要素のhoverに指定している状態をa要素の親要素のhoverに指定することで直ります。

図解すると以下のような感じです。黒い点線が親要素であるp要素の領域だと思ってください。

ボタンがプルプルしてしまう場合の解決案

コードも置いておきます。

問題は残る

気がついた人もいるかもしれませんが、(translateYを用いる)この方法だと少しだけ気になる問題が残ります。

それは、プルプルしなくなったものの、hover後の上端をクリックしてもボタンのアクション(リンクなど)が効かないことです。

これは理屈がわかれば当たり前で、hoverしていてマウスカーソルが置いてあるのはあくまでもp要素であってa要素にマウスが乗っている訳ではないため、クリックしてもリンクが効きません。

これを解決するには、ボタンの動き自体を別の方法で実現するか、a要素のクリックイベントを親要素にも付与するなどの解決方法がありますが、それはまた別のお話になります。

プルプルの原理さえ理解できていれば、それほど難しくはないと思いますので、ケースに合わせた解決策を考えてみてください。