アコーディオン [jQuery] [js]

2016/09に公開

jQueryのslideToggleを使ったアコーディオンと関連した小技をご紹介します。

電子部品とか多少でも扱ったことのある方は「トグルスイッチ」など「トグル」という言葉を聞いたことがあると思います。「トグル」とは一般的に同じ操作によって二つの状態を交互に切り替えられるような仕組みのことをいいます。プログラミングの世界でも聞くことがある言葉です。


sample

見出し1
テキスト□テキスト■テキスト□テキスト■テキスト□テキスト■テキスト□テキスト■テキスト□テキスト■
見出し2
テキスト□テキスト■テキスト□テキスト■テキスト□テキスト■
見出し3
テキスト□テキスト■テキスト□テキスト■テキスト□テキスト■テキスト□テキスト■
テキスト□テキスト■テキスト□テキスト■テキスト□テキスト■テキスト□テキスト■テキスト□テキスト■テキスト□テキスト■テキスト□テキスト■テキスト□テキスト■テキスト□テキスト■

各見出し部分をクリックすると開閉します。
IE7/8でも動作します(ただし、webフォントを使っているので、IE7では表示されない部分があります)。


html

<dl>
<dt>見出し1</dt>
<dd>内容1(テキストや画像など)</dd>
<dt>見出し2</dt>
<dd>内容2(テキストや画像など)</dd>
<dt>見出し3</dt>
<dd>内容3(テキストや画像など)</dd>
</dl>

※黒い部分をダブルクリックするとソースを選択できるので、コピーして使ってください。

webページを読み込んだときに表示しておく見出し(dt)と非表示にしておくその内容(dd)をdlタグを利用して組んでおきます。

  • ここではdl(dt/dd)タグを使用していますが、他のタグでも良いです。
  • ここでは全部閉じていますが、最初に開いておきたい場合は、該当タグに開いたときに追加するclassがあればあらかじめ設定しておきます。
  • 同一ページにアコーディオンにするdlとしないdlが混在する場合は、アコーディオンにするものを識別できるように、dlタグにセレクタとして任意のclass名やid名をつけてください。

css

dt{
    cursor: pointer; /* PCの場合クリックできる部分と分かりやすいように、マウスオーバーでポインターになるように設定。*/
    /*何もしていないときの見出しの設定*/
}
dt.open{
    /*アコーディオンを開いたときの見出しの設定*/
}
dd{
    display: none; /*内容部分は最初非表示にしておく*/
    /*アコーディオンを開いたときの内容部分の設定*/
}

※黒い部分をダブルクリックするとソースを選択できるので、コピーして使ってください。

アコーディオンを開閉したときの状態をそれぞれcssで設定しておきます。
サンプルの場合は、背景色を変えるようにしました。

最初に読み込んだときに開いておきたい場合は、(このサンプルなら)該当するddタグをdisplay: block;にしておきます。


js

アコーディオンはこのようなシンプルなコードで実現できます。

$(function(){
    $('dt').on('click', function() {
        $(this).next().slideToggle();
        $(this).toggleClass('open');
    });
});

※黒い部分をダブルクリックするとソースを選択できるので、コピーして使ってください。

dtタグをクリックするたびに、クリックしたdtタグ(this)のすぐ次に来るタグ(next() = dd)を上下にスライドさせています。
その際、クリックしたdtタグ(this)にclass[open](開いたときの見出しの設定)を追加したり削除したりさせます。

slideToggleの()内では、開閉のスピードを設定したり、コールバック関数を追加できたりします。
アコーディオンが開いた(閉じた)直後に何らかの処理を加えたい場合などにはコールバック関数で処理を追加してみてください。


sample(アコーディオンの状態によってテキストを変える)

クリックして開く
テキスト□テキスト■テキスト□テキスト■テキスト□テキスト■テキスト□テキスト■テキスト□テキスト■
クリックして開く
テキスト□テキスト■テキスト□テキスト■テキスト□テキスト■
クリックして開く
テキスト□テキスト■テキスト□テキスト■テキスト□テキスト■テキスト□テキスト■
テキスト□テキスト■テキスト□テキスト■テキスト□テキスト■テキスト□テキスト■テキスト□テキスト■テキスト□テキスト■テキスト□テキスト■テキスト□テキスト■テキスト□テキスト■

htmlの構造・cssは変更していません。


js

上のコードに処理を追加して、アコーディオンの開閉の状態によって表示を変えます(ここではdtタグのテキストを変えます)。

$(function(){
    $('dt').on('click', function() {
        $(this).next().slideToggle();
        $(this).toggleClass('open');

        var check = $(this).attr('class');
        if(check.indexOf('open') != -1) $(this).text('クリックして閉じる');
        else $(this).text('クリックして開く');
    });
});

※黒い部分をダブルクリックするとソースを選択できるので、コピーして使ってください。

クリックしたdtタグ(this)のclass属性に[open]が含まれるかどうかで開閉の状態を検出し、テキストを差し替えます。


sample(連動して開閉する)

見出し1
テキスト□テキスト■テキスト□テキスト■テキスト□テキスト■テキスト□テキスト■テキスト□テキスト■
見出し2
テキスト□テキスト■テキスト□テキスト■テキスト□テキスト■
見出し3
テキスト□テキスト■テキスト□テキスト■テキスト□テキスト■テキスト□テキスト■
テキスト□テキスト■テキスト□テキスト■テキスト□テキスト■テキスト□テキスト■テキスト□テキスト■テキスト□テキスト■テキスト□テキスト■テキスト□テキスト■テキスト□テキスト■

htmlの構造・cssは変更していません。


js

最初のコードにsiblingsを使った処理を追加して、見出しのどれかをクリックしてアコーディオンをクリックすると、他は閉じるようにします。

$(function(){
    $('dt').on('click', function() {
        $(this).next().slideToggle();
        $(this).toggleClass('open');

        $(this).next().siblings('dd').slideUp();
        $(this).siblings('dt').removeClass('open');
    });
});

※黒い部分をダブルクリックするとソースを選択できるので、コピーして使ってください。

クリックしたdtタグ(this)の次のタグ(ddタグ)の兄弟要素で指定したタグ(thisの次のddタグ以外のddタグ)に対してslideUp(開いてある内容部分を閉じる)処理を追加し、さらにクリックしたdtタグ(this)以外のdtタグからclass[open]を削除する処理を追加しています。


sample(内容以外の部分をタップすると閉じる)

スマートフォンなどでメニューを常に表示しておくと専有面積が大きすぎて邪魔なため、あらかじめ非表示にしておいてタップで開閉してもらったりすることがあると思います。
その際、メニューを開いたときのトリガー以外の部分をタップしてもメニューを閉じられるとタップ面積が広い分、操作しやすくなるかもしれません。

htmlの構造はdt・ddタグをそれぞれ一つずつにしただけです。cssは変更していません。


js

closestを用いて、アコーディオン部分以外をタップ(クリック)しても閉じるようにします。

$(function(){
    $('dt').on('click', function() {
        $(this).next().slideToggle();
        $(this).toggleClass('open');
    });

    $(document).on('click', function(evt){
        if( !$(evt.target).closest('dt, dd').length ){
            $('dd').slideUp();
            $('dt').removeClass('open');
        }
    });
});

※黒い部分をダブルクリックするとソースを選択できるので、コピーして使ってください。

htmlページのどこかをクリックしたら、アコーディオンを閉じる処理を追加しています。その際、closestをつかってアコーディオン部分を除くようにしています。


sample(一行表示)

例えばスマートフォンで画像のキャプション(説明文)が長すぎるとスクロールが長くなるため、一行だけ表示して続きを読みたい人だけタップしてみてもらえればいいかなぁという場合に、slideToggleでは難しいので、他の方法を探ります。

  • dummy
    画像の説明。
    テキスト□テキスト■テキスト□テキスト■テキスト□テキスト■テキスト□テキスト■
    テキスト□テキスト■テキスト□テキスト■テキスト□テキスト■テキスト□テキスト■テキスト□テキスト■テキスト□テキスト■テキスト□テキスト■テキスト□テキスト■テキスト□テキスト▲
  • dummy
    画像の説明。
    テキスト□テキスト■テキスト□テキスト■テキスト□テキスト■テキスト□テキスト■
    テキスト□テキスト■テキスト□テキスト■テキスト□テキスト■
    テキスト□テキスト■テキスト□テキスト■テキスト□テキスト■
    テキスト□テキスト■テキスト□テキスト■テキスト□テキスト▲

キャプション部分をクリックすると開閉します。
IE7/8でも動作します(ただし、webフォントを使っているので、IE7では表示されない部分があります)。

このサンプルで使用している画像は、ダミー画像を生成するPlacehold.jpを利用しております。ご利用の際は提供元の利用規約をよく読んでご利用ください。


html

<figure>
    <img src="画像のパス" alt="">
    <figcaption>画像の説明</figcaption>
</figure>

※黒い部分をダブルクリックするとソースを選択できるので、コピーして使ってください。

想定として画像のキャプションということにしていますが、状況はこれに限りません。従って、使用するタグもfigcaptionに限りません。

同一ページにアコーディオンにするfigcaptionとしないfigcaptionが混在する場合は、アコーディオンにするものを識別できるように、figcaptionタグやその親タグなどにセレクタとして任意のclass名やid名をつけてください。


css

figcaption{
    overflow: hidden;
    cursor: pointer; /* PCの場合クリックできる部分と分かりやすいように、マウスオーバーでポインターになるように設定。*/

    /*その他必要な装飾*/
}

figcaption.open{
    /*必要であればキャプションを開いたときの装飾*/
}

※黒い部分をダブルクリックするとソースを選択できるので、コピーして使ってください。

一行だけ表示した際に余計なものが表示されないよう、figcaptionにoverflow: hiddenを設定しておきます。

ここで初期値としてのキャプションの高さをcssで指定してしまわないことがコツです。


js

$(function(){
    var dh = '1.5em'; //デフォルトの高さ
    var figcap = $('figcaption');
    var h= new Array(); //figcaptionの高さを格納する配列

    figcap.each(function(index) { //ページ内に複数のfigcaptionタグがある場合はすべての高さを控えておく
        h.push($(this).innerHeight() + "px");
    });

    figcap.css('height', dh); //最初に一度すべてのfigcaptionの高さを一行分の高さ(デフォルトの高さ)にしておく

    figcap.on('click', function() {
        var index = figcap.index(this);
        var thisClass = $(this).attr('class');
        if(thisClass.indexOf('open') != -1){
            $(this).stop().animate({height:h[index]},'fast');
            $(this).addClass('open');
        } else {
            $(this).stop().animate({height:dh},'fast');
            $(this).removeClass('open');
        }
    });

    //おまけ(リサイズしたらリロードする)
    var timer = false;
    $(window).on('resize', function(){
        if (timer !== false) { //リサイズ中に何度も処理が行われるのを防ぐ
            clearTimeout(timer);
        }

        timer = setTimeout(function() {
            location.reload();
        }, 20);
    });
});

※黒い部分をダブルクリックするとソースを選択できるので、コピーして使ってください。

  1. 2行目:キャプション部分のデフォルトの高さ(ここではテキスト1行分)を変数dhに格納
  2. 3行目:figcaptionタグをセレクタとして頻繁に使用するので、扱いやすいように変数figcapに格納
  3. 4行目:キャプション部分が開いたときの高さを格納するための配列変数hを用意。
  4. 6〜8行目:キャプション部分が開いているときの高さを配列変数hに格納(同一ページに複数figcaptionがある場合に備えてeachをつかって配列に格納する)
    上記のcssでキャプション部分のデフォルトの高さを指定しなかったのは、あらかじめ指定しておいてしまうと、開いたときの高さではなく指定したデフォルトの高さを格納してしまうから。
  5. 10行目:キャプション部分の高さをデフォルトの高さ(ここではテキスト1行分)に設定する。
    ここまでの処理は通常瞬間的に終わるので問題ないはずだが、ページ全体の処理の関係で一瞬キャプションが開いている状態がユーザに見えてしまうこともある。それが気になる場合は時間差で処理させるなど何らかの工夫を追加する。
  6. 12〜22行目:キャプション部分(figcaptionタグ)をクリックしたときの処理
  7. 13行目:ページ内のどのfigcaptionをクリックしたかindex関数を使って把握する。
  8. 14行目:クリックしたキャプション部分(figcaptionタグ)のclassを変数thisClassに格納
  9. 15〜21行目:クリックしたキャプション部分(figcaptionタグ)のclassに[open]が含まれているか否かで、キャプションの状態を把握し、それぞれの処理をする。
  10. 16〜17行目:クリックしたキャプション部分(figcaptionタグ)のclassに[open]が含まれていなければ、キャプションは閉じているので、配列に格納しておいた開いたときの高さ(13行目で変数indexに格納した番号で配列を参照)まで、キャプション部分をanimateし、キャプション部分(figcaptionタグ)にclass[open]を追加。
  11. 19〜20行目:クリックしたキャプション部分(figcaptionタグ)のclassに[open]が含まれていれば、キャプションは開いているので、デフォルトの高さ(ここではテキスト1行分)までキャプション部分をanimateし、キャプション部分(figcaptionタグ)のclass[open]を削除。
  12. 25〜34行目:ここまでの処理ではスマートデバイスの場合、縦横回転した際に回転前に把握したキャプション部分が開いたときの高さのままに開閉されるので、開いたときにやたらと空間があったり逆にキャプション部分が全て表示されなかったりするので、回転したら(正確にはリサイズしたら)リロードするようにする。

最後のはおまけです。ページ全体をリロードするのは美しくないと思うので、他に良い方法があったらそちらの処理を行ってください。
また、この処理をすることで古いブラウザでは不具合があるかもしれないので、古いブラウザではこの処理をしないようにするなど、工夫が必要かもしれません。


アコーディオンが「解」なのか?

ここまで、さんざんアコーディオンを実現するテクニックをご紹介してきましたが、コーディングのテクニックよりも何よりも、デザインするよりもっと以前に、考えることがあります。

そのページでアコーディオンを使うことが、本当に良いことなのか?

なぜ、そこでアコーディオンなのか?

もしかしたら、1ページにまとめたいコンテンツの量が多すぎて、スクロールが長すぎるからちょっとでも短くしたくて、アコーディオンを導入するのかもしれません。
もしそうなら、本当にそれがユーザーのためになるのか?そのページで一番大事なことは伝えられるのか?
デザイン・設計に入る前に、仕様書段階で考えてみてください。

ファーストビューでアコーディオンが全部(または一部)閉じていれば、隠れている情報はユーザーにはわかりません。それは気付かれなくても良いような情報ですか?

アコーディオンの仕組みをとっさに理解できるユーザーばかりではありません。クリック(タップ)したくなるようなデザインで補完できますか?

デバイスの違いは考慮しましたか?アコーディオンをPCで操作するのとスマートデバイスで操作するのでは、ユーザーのストレスは違ってくるはずです。

そもそも、今時の(そのwebページのターゲット)ユーザーはスクロールをそんなにめんどくさく感じていないかもしれません。むしろアコーディオンをいちいちクリック(タップ)する方が面倒かもしれませんよ?

いちいち開いてみたけれど、そのユーザーにとっては大したことのない情報だったとき、がっかり感・徒労感でユーザーの心証を悪くしませんか?

デザインや技術を駆使して、ある程度の問題は解決できるかもしれません。でも、本当にそのページでアコーディオンを使うことがベストの方法か、もっと違う方法があるのではないか、導入前に考えてみてください。
そして、導入後も考え続けてください。最新の技術(トレンド)、デザイナーやコーダーやシステム屋さんなどそのページに携わる人たちの意見、ユーザーの反応なども鑑みて、もっと良い方法を探り続けてください。

ご紹介した記事は、恒久的な示唆も含まれていれば、記事が書かれた時点でのトレンド的な内容のものもあります。あくまでも参考にとどめて、今現在の業界のトレンドはどうなのか、制作しているwebページにあてはまることなのか、何よりユーザーにとって見やすいか、使いやすいか、大事なことが伝わる(目的の行動に誘導できる)のかといったことを優先的に考えてください。


戻る

Top