Unformed Building

IE 8でjQueryのslideDown()とfadeIn()が効かないことがある件

公開:
更新:

パーマリンク

jQueryでスライドダウンを作っているときにIE 8だけで動いていないのを確認したので、そのメモです。検索しても原因が分からなかったので自分で調べてみました。

作っていたのは以下のようなコードでした。

<div id="slide">
  <h2>スライドダウン</h2>
  <p>テキスト。テキスト。テキスト。...</p>
</div>
jQuery(function($) {
  $('#slide > p').hide();
  $('#slide > h2').click(function() {
    $(this).next().slideDown();
  });
});

これがIE 8の場合だけ動かなかったんですね。
ちょっと検索してみたら、IE 8にはslideDown()が動かない場合があるから、

<meta http-equiv="X-UA-Compatible" content="IE=7">

で、IE 7互換モードにしてしまうか、

$(selector).slideDown().show();

という感じで、とりあえず表示だけはさせとけ、って感じの記事がありました。

最初はもうこれでいいかなって思ったんですけど、ちょっと再現してみようと思ってデモを作ってみたら原因が分かりました。
ブログ用に書き直したデモページは以下。
IE 8だとスライドダウンするものとしないものがあります。

IE 8でjQueryのslideDown()とfadeIn()が効かないことがある件 DEMO

デモを見ると分かるとおり、IE 8でslideDown()fadeIn()が動かない条件は、トリガーにdisplay: inlineもしくはdisplay: inline-blockが指定されている場合になります。
加えて、その要素の隣接する弟要素をスライドダウンやフェードインしようとしていること、ですね。

デモにdivspanの両方を書いているのは、ブロック要素にdisplayプロパティを指定したときだけなのか、インライン要素やインラインブロック要素の隣接する弟要素も駄目なのか確認するためです。
結果、本来ブロックレベルの要素にdisplay: inlineまたはdisplay: inline-blockが指定されているときに発生することが分かりました。

この現象の回避策ですが、大人しくHTMLを変更するか、CSSを変更することですね。
もしくは上記の「互換モードにしてしまう」「とりあえず見れるように」の対策をするか。

また、display:inlineが指定されている場合のみですが、

selector:after {
  display: block;
  content: '';
}

で、動くようになります。
display:inline-blockが指定されている場合には通用しませんのでご注意を。

HTMLもCSS も変更できない上に、どうしても同じ動きを実現しなければならない場合は、JavaScriptを以下のように書き換えます。
これは@GeckoTangさんが作ってくれました。

$(this).next().slideDown();

/* この部分を以下のように変更 */

var div = $('<div/>'),
    nxt = $(this).next();

$(this).after(div);
nxt.slideDown();
div.remove();

ご覧の通り、トリガーとなる要素の直後にブロック要素を作り、スライドダウンさせる要素との隣接関係をなくし、スライドダウンしたら追加したdivを削除、という変更です。

ここまでやらなきゃいけないことはあんまりないと思いますが……やっぱり現実的にはHTMLかCSSを変更する方がよさそうです。