3
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 3 years have passed since last update.

Javascriptメモ:BootstrapのCollapseのトランジションを、async/awaitで待つ

Last updated at Posted at 2021-07-21

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関数が何をしているのか、コメント不要レベルの明白さになった。
将来の自分のために、こちらを採用したい。

参考

3
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
3
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?