始めに
アコーディオンはjQueryだとslideToggle
が優秀でこれを使えばいいですが、Vue.jsとかではどうやったらいいか悩むことがあるかもしれません。
幸いVue.jsにはvue-slide-up-down
というライブラリがあるのでこれを使えばいいかもしれませんが、もし自作をする場合はどうやって実装するかをまとめたいと思います。
(僕も最初は知らなくて自作してました・・・)
実装方法
開く処理
前提として、アコーディオンの高さは0px
にして、overflow: hidden
も設定しておきます。これをしないと中身が見えちゃいますので(汗)。
この状態で以下の手順を踏んで開くアニメーションをします。
- コンテンツの高さ(図ではelContent)を取得して、その値を親にセットする
- CSS transitionで目標の値までアニメーションする
- アニメーション終了後スタイルをリセットする(リセットしないと開いた後にサイズが変更しても調整されなくなってしまいます)
閉じる処理
閉じる場合は以下のような手順を踏みます。ワンテンポ置くというのはVue.jsのtransitionと同じ発想で、activeに初期値をセットし、toで目標の値をセットするやり方と一緒です。
- アコーディオン全体の今の高さを取得し、その値をセットする。その後ワンテンポ置いてから0pxをセットする。
- CSS transitionで0pxに向かってアニメーションされる
- 閉じた場合はスタイルのリセットは行わない(リセットすると中身が見えてしまうため)
まとめ
まとめると、以下のようなコードになります。
コンポーネント化しているので、開閉のタイミングはwatchで監視しています。
const Accordion = Vue.extend({
props: {
isOpen: Boolean,
},
data() {
return {
heightStyle: this.$props.isOpen ? '' : '0px',
isOverflow: !this.$props.isOpen,
};
},
watch: {
// 開閉フラグの監視
'$props.isOpen'(isOpen) {
this.$data.isOverflow = true;
// 現在の高さを設定
this.$data.heightStyle = `${this.$refs.elAccordion.clientHeight}px`;
// ワンテンポおいてから目標の高さを設定するが、$nextTickだとうまくいかないのでsetTimeoutに変更
window.setTimeout(() => {
this.$data.heightStyle = isOpen ? `${this.$refs.elContent.clientHeight}px` : '0px';
}, 10);
},
},
methods: {
// アニメーション終了時
onTransitionEnd() {
// 開いたときは高さとoverflowの設定を解除する
if (this.$props.isOpen) {
this.$data.heightStyle = '';
this.$data.isOverflow = false;
}
},
},
template: `
<div
ref="elAccordion"
class="accordion"
:style="{
height: $data.heightStyle,
overflow: $data.isOverflow ? 'hidden' : '',
}"
@transitionend="onTransitionEnd"
>
<div
ref="elContent"
>
<slot></slot>
</div>
</div>
`,
});
終わりに
以上がアコーディオンを自作する場合の方法でした。
最後にサンプルコードを置きますので、詳しい実装はこちらを参考にしてみてください。