Bootstrapのcollapseの.show()
や.hide()
メソッドは、トランジションのアニメーションを待たない。
collapse要素が表示され(消え)終わった後に何かしたい時はどうすればいいか。
Bootstrapドキュメント:Collapse#Eventによれば、トランジションが終了した時にイベントが発生するから、それを捕まえればいいよ、とのこと。
イベントリスナを書いた場合
- 画面にcollapse要素が2つあって、
- 片方を閉じるアニメーションが終わった後に、もう一方を開くアニメーションを始めたい
とする。
// bootstrapは読み込み済みの前提。
// collapse要素が画面に2個あるとして
const fig1 = document.getElementById('collapse-fig1');
const fig2 = document.getElementById('collapse-fig2');
// それぞれのcollapseオブジェクト取得して
let myCollapse = {};
myCollapse['fig1'] = new bootstrap.Collapse(fig1, {toggle:false});
myCollapse['fig2'] = new bootstrap.Collapse(fig2, {toggle:false});
// これだと閉じる&開くアニメーションが同時進行してしまう
// myCollapse['fig1'].hide()
// myCollapse['fig2'].show()
// fig1->fig2に切り替えたい
function switchFig() {
myCollapse['fig1'].hide(); // (1) fig1を閉じる
}
// fig1の閉じるトランジションが終わったら
fig1.addEventListener('hidden.bs.collapse', () => {
myCollapse['fig2'].show(); // (2) fig2を開く
});
確かに動作する。
上記ぐらいシンプルなら、これでもいい。
でも、こういうスクリプトが必要ということは、実際には条件分岐やら何かもっとガチャガチャしたコードになってるのだ。
switchFig関数がやりたいことは画面の切り替えであり、本来、(1)閉じる処理と(2)開く処理はセット。
なのに、関数とイベントリスナのコールバックに分断されてしまっている。
後日何か変更するときに、イベントリスナもセットだと思い出せるだろうか。
思い出せたとして、コードのどこかのイベントリスナを探すのめんどいなーってならないだろうか。
私は思い出せないし、めんどい。
async/awaitを使った場合
ここは、コールバック地獄の蜘蛛の糸、Promiseにすがって、async/awaitを使いたい。
(IEには対応できないけど、もういいよね?)
まずは、「おまけつきcollapse」を作れるようにする。
// おまけつきcollapseを作る関数
const makeMyCollapse = (id) => {
// ターゲットとなるcollapse要素。存在しないIDを指定されない前提。
const target = document.getElementById(id);
// bootstrap の collapse を取得。Collapseじゃない要素を指定されない前提。
let collapse = new bootstrap.Collapse(target, {toggle:false});
let fire; // 関数が入る予定
let registed; // イベント登録済みフラグ
// おまけ。collapse閉じて待つ(promiseを返す)メソッド。
collapse.waitHide = () => {
// イベントリスナ未登録なら登録
if (!registed) {
// targetにhidden.bs.collapseイベントが発生したらfire!
target.addEventListener('hidden.bs.collapse', () => {
if (fire) fire();
});
registed = true;
}
// 閉じて
collapse.hide();
// プロミスを返す
return new Promise(resolve => {
// fireに、このプロミスを解決する関数を設定
// つまり、fire()が実行されたときに、このプロミスは解決する。
fire = () => resolve();
});
}
// 「おまけつきcollapse」を返す
return collapse;
}
この準備をしておけば、処理のコードは以下のようになる。
// 要素のIDを渡して、おまけつきcollapseを取得
let myCollapse = {};
myCollapse['fig1'] = makeMyCollapse('collapse-fig1');
myCollapse['fig2'] = makeMyCollapse('collapse-fig2');
// fig1->fig2に切り替えたい
async function switchFig() {
await myCollapse['fig1'].waitHide(); // (1) 閉じるのを待って
myCollapse['fig2'].show(); // (2) その後、開く
}
switchFig関数が何をしているのか、コメント不要レベルの明白さになった。
将来の自分のために、こちらを採用したい。
参考