CSSのgrid-template-rowsを使って、jQueryのslideDown()のようなアニメーションを実装する方法を見かけたので、試してみます。
サンプルコード
例として、アコーディオンの実装を試してみます。
<div class="acc"> <button class="acc_btn">リゼ・ヘルエスタ</button> <div class="acc_contents"> <div class="acc_contents-inner"> <p>ヘルエスタ王国の第二皇女。<br> 文武両道学園主席、真面目で誰にでも優しくかなりの人望がある。<br> 王位継承の資格者として日々鍛錬や人とのコミュニケーションを大事にしている。</p> <p>ファンアート:<a href="https://twitter.com/hashtag/%E3%83%98%E3%83%AB%E7%B5%B5%E3%82%B9%E3%82%BF">#ヘル絵スタ</a></p> </div> </div> </div>
アコーディオンのコンテンツ部分(.acc_contents)を入れ子(.acc_contents-inner)にしている点がポイントです。
.acc_contentsに対してgrid-template-rows で0frを設定して、開く際は1frになるようにします。
.acc_contents { display: grid; grid-template-rows: 0fr; transition: grid-template-rows 1s; } .acc_contents.is-open { grid-template-rows: 1fr; } .acc_contents-inner { overflow: hidden; }
grid-template-rowsの0frから1frへの変更時は、transitionを使ったアニメーションの設定が可能になります。
ただそれだけだとコンテンツの中身が非表示にならないため、インナー要素(.acc_contents-inner)に対してoverflow: hidden を設定して非表示になるようにしています。
最後にJavaScriptで開閉時のclassの付け替え処理を行います。
document.querySelector('.acc_btn').addEventListener('click', function() { document.querySelector('.acc_contents').classList.toggle('is-open'); });
これでスライドで開閉するアコーディオンの実装ができました。
スライドで開閉するアコーディオンのデモページ
detailsタグとsummaryタグを使ったアコーディオンの場合も同じように試してみます。
まずはうまくいかなかった例です。
<details class="acc"> <summary class="acc_btn">リゼ・ヘルエスタ</summary> <div class="acc_contents"> <div class="acc_contents-inner"> <p>ヘルエスタ王国の第二皇女。<br> 文武両道学園主席、真面目で誰にでも優しくかなりの人望がある。<br> 王位継承の資格者として日々鍛錬や人とのコミュニケーションを大事にしている。</p> <p>ファンアート:<a href="https://twitter.com/hashtag/%E3%83%98%E3%83%AB%E7%B5%B5%E3%82%B9%E3%82%BF">#ヘル絵スタ</a></p> </div> </div> </details>
detailsタグの場合はopen属性が自動で付与されるため、open属性を使って開閉アニメーションの設定を行います。
.acc_contents { display: grid; grid-template-rows: 0fr; transition: grid-template-rows 1s; } .acc[open] .acc_contents { grid-template-rows: 1fr; } .acc_contents-inner { overflow: hidden; }
detailsタグとでうまくいかなかった場合デモページ
detailsタグはopen属性が付与された時点で表示、削除された時点で非表示となるため、この実装方法だと閉じる際にアニメーション前に非表示になってしまいます。
また、開く際にも挙動が不安定で、スライドのアニメーションが発生する場合としない場合があるようでした。
開閉のアニメーションをopen属性ではなく、別途data属性を付与して管理するようにしてみます。
const details = document.querySelector('.acc'); const summary = document.querySelector('.acc_btn'); const contents = document.querySelector('.acc_contents'); summary.addEventListener('click', function(e) { // デフォルトの開閉処理を停止 e.preventDefault(); // アコーディオンが開いている場合 if(details.open) { details.dataset.acc = 'close'; // アコーディオンが閉じている場合 } else { details.open = true; // open属性の付与と同じタイミングでのアニメーション開始だとうまくいかないことがあるので少しずらす details.dataset.acc = 'wait'; setTimeout(function() { details.dataset.acc = 'open'; }, 10); } }); // アコーディオンの開閉アニメーション終了時 contents.addEventListener('transitionend', function(e) { // アコーディオンの閉じるアニメーション終了時 if(details.dataset.acc === 'close') { details.open = false; details.dataset.acc = ''; } });
open属性のデフォルトの付け替えを停止した上で、任意のタイミングで付与・削除するようにしています。
スライドのアニメーションはdata-acc属性を使った管理で、ボタンクリック後に開くアニメーションが開始されるまではwait、開くアニメーション実行時はopen、閉じるアニメーション実行時はclose、閉じるアニメーション終了後は値なしになるようにしています。
アニメーションの設定をopen属性からdata-acc属性を使った形に変更します。
.acc_contents { display: grid; grid-template-rows: 0fr; transition: grid-template-rows 1s; } :where(.acc:not([data-acc="wait"]):not([data-acc="close"])[open]) .acc_contents { grid-template-rows: 1fr; transition: none; } .acc[data-acc="open"] .acc_contents { grid-template-rows: 1fr; transition: grid-template-rows 1s; } .acc_contents-inner { overflow: hidden; }
.acc:not([data-acc=”wait”]):not([data-acc=”close”])[open] はJavaScriptがオフの場合の開閉用と、ページ内検索時の開閉用に設定しています。
これで意図したアコーディオンの挙動にすることができました。
detailsタグでうまくいった場合のデモページ
コメントが承認されるまで時間がかかります。