Vue + bootstrapのツールチップが動かないときの対処法

さてさて、前回はVue + DataTableにチェックボックスで複数削除の機能をつけるというVuejQueryを連携させる方法をご紹介しました。

Vueは、JavaScriptの開発をより効率的にしてくれますが、特定の機能だけを考えるとまだまだjQueryの機能を使うほうが高速に実装することができたりします。

そのため、jQueryをベースにしているBootstrapを使った場合でも同じで、例えば以下のようなモーダルはそんな便利機能のうちのひとつです。

そして、個人的ですが地味に便利だと思うBootstrapの機能があります。

それは、

ツールチップ(マウスを合わせるとテキストをポップアップしてくれる機能)

です。

これのことですね ↓↓↓

なぜかというと、全ての項目に説明文をつけてしまうとページ内容がごちゃごちゃしてしまうので、例えば「?」マークのアイコンにツールチップをつけて「ここの意味はこうですよ」と教えてあげるとユーザビリティ的にもグッド👍だからです。

ただ、Vue + Bootstrapを使っている場合にうまく動かない例が出てくることがあるので、その対処法をこの記事でシェアしておきたいと思います。

ぜひ皆さんのお役に立てると嬉しいです😊✨

開発環境: Vue 2.6

上手く動く例

まずはVueとツールチップを使った上手く動く例を紹介します。

ただし、この書き方はVueとしての動きがほぼないので、ウェブ開発としては使えませんが、順を追って見ていきたいのであえて載せておきます。

<html>
<head>
    <link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.3.1/css/bootstrap.min.css">
</head>
<body>
    <div id="app">
        <div class="p-2" v-for="color in colors">
            <button
                type="button"
                class="btn btn-dark"
                data-toggle="tooltip"
                :title="color +'のツールチップ'">{{ color }}</button>
        </div>
        <hr>
        <a class="m-2" href="#" @click.prevent="changeColors">色を変更</a>
    </div>
    <script src="https://code.jquery.com/jquery-3.4.1.min.js"></script>
    <script src="https://stackpath.bootstrapcdn.com/bootstrap/4.3.1/js/bootstrap.bundle.min.js"></script>
    <script src="https://cdn.jsdelivr.net/npm/vue@2.6.0"></script>
    <script>

        new Vue({
            el: '#app',
            data: {
                colors: ['赤', '青', '黄色', '白', '黒']
            },
            mounted() {

                $('[data-toggle="tooltip"]').tooltip();

            }
        });

    </script>
</body>
</html>

内容としてはとてもシンプルで、Vuecolorsという色の名前が入った変数を用意し、HTML側でv-forを使ってループさせます。

そして、そのループで作成したボタンにBootstraptooltip()を実行してツールチップを起動させるようにしています。

これを実行した結果はこうなります。

上手く動いていますね。

さっきのコードを拡張させる(動かない例)

では続いて、さっきのコードをより動的に動くものに変更したくなったとします。

例えば、色のデータをAjaxで取得する場合やパラメータによって色データを切り替えたい場合ですね。

では、そんな場合のコードです。

// 【注意】この例は上手くいません

new Vue({
    el: '#app',
    data: {
        colors: [] // 初期値は色データなし
    },
    mounted() {

        // ここで色データをセットする
        this.colors = ['赤', '青', '黄色', '白', '黒'];

        // ツールチップ
        $('[data-toggle="tooltip"]').tooltip();

    }
});

一見するとこのコードを見ると上手くツールチップが起動しそうにみえます。なぜなら、colorsをセットしてからtooltip()を実行しているからです。

しかし、実際にはツールチップは起動できません。

これはなぜか???

それは、この場合だと tooltip() が呼ばれた時にターゲットになる <button>ボタン</button> はまだ存在してないからです。

最初の上手く行ったバージョンの例を思い出してみてください。あのときは初期値としてdatacolorsという色データが入っていました。

そして、Vuemounted()はページの準備が完了した時、つまりボタンも含めたHTMLが用意されてから呼ばれるメソッドなので、すでにツールチップを表示するボタンも存在していたことになります。

しかし、拡張版の場合は色データを変更しただけでHTMLデータはまだ変更されていないということになります。

では、どう対応すればいいのか??

そこで登場するのがVueが用意してくれているnextTick()というメソッドです。

nextTick() は、HTMLが作成された後に実行されるメソッドで、一種のコールバック関数になります。

では、実際にコードを見てみましょう。

new Vue({
    el: '#app',
    data: {
        colors: []
    },
    mounted() {

        this.colors = ['赤', '青', '黄色', '白', '黒'];

        Vue.nextTick(() => {

            // ここはHTMLの作成後に実行される
            $('[data-toggle="tooltip"]').tooltip();

        });

    }
});

内容としては、先ほどのコードにnextTick()を追加しただけですが、実行するとこのコードはうまく動くようになります。

データの中身がよく変更になる場合は??

先ほどの例では、色データはmounted()の中で1回だけ変更されるようになっていました。

ただ、開発内容によってはv-forで表示するデータが頻繁に変更になる場合も多いと思います。そんな場合はどうすればいいのでしょうか。

その答えは、watchです。

watchはある特定のデータが変更になったら必ず呼ばれるメソッドを作ることができるので、以下のようにすることでcolorsの中身が変更になるごとにtooltip()を実行することができるようになります。

※ ちなみに色データを手動で変更できるようにchangeColors()というメソッドもつけています。

<html>
<head>
    <link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.3.1/css/bootstrap.min.css">
</head>
<body>
    <div id="app">
        <div class="p-2" v-for="color in colors">
            <button
                type="button"
                class="btn btn-dark"
                data-toggle="tooltip"
                :title="color +'のツールチップ'">{{ color }}</button>
        </div>
        <hr>
        <a class="m-2" href="#" @click.prevent="changeColors">色を変更</a>
    </div>
    <script src="https://code.jquery.com/jquery-3.4.1.min.js"></script>
    <script src="https://stackpath.bootstrapcdn.com/bootstrap/4.3.1/js/bootstrap.bundle.min.js"></script>
    <script src="https://cdn.jsdelivr.net/npm/vue@2.6.0"></script>
    <script>

        new Vue({
            el: '#app',
            data: {
                colors: []
            },
            methods: {
                changeColors() {

                    this.colors = ['金', '銀', '銅'];

                }
            },
            watch: {
                colors() {

                    Vue.nextTick(() => {

                        $('[data-toggle="tooltip"]').tooltip();

                    })

                }
            },
            mounted() {

                this.colors = ['赤', '青', '黄色', '白', '黒'];

            }
        });

    </script>
</body>
</html>

でも、これだと色を変更しても前のデータでポップアップされてしまう・・・

watchを使うと色データが変わってもツールチップは起動するようになりますが、実はこのままでは1つ問題があります。

もし動的にツールチップの内容を変更するような(今回のような)場合だと、前のツールチップの内容が残ってしまうのです。

こんなカンジです
↓↓↓

「金」に変更になった後も最初の「赤のツールチップ」と表示されていますね。

これは、tooltip()はその内容をdata-original-titleというプロパティに格納するのですが、data-*はキャッシュされる仕様になっているためで、これに対応するには直接このプロパティを変更する必要があります。

<button
    type="button"
    class="btn btn-dark"
    data-toggle="tooltip"
    :data-original-title="color +'のツールチップ'">{{ color }}</button>

これですべてOKです。

お疲れ様でした😊✨

テストしてみる

全ての変更を実施したコードを実行してみましょう。

はい!上手く動きました。

今回のソースコード

ちなみに今回作成した最終コードは以下のとおりです。
ご自身の開発で自由に応用して使ってください。

<html>
<head>
    <link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.3.1/css/bootstrap.min.css">
</head>
<body>
    <div id="app">
        <h3 class="p-2">Vue + Bootstrapでツールチップを実装</h3>
        <div class="p-2" v-for="color in colors">
            <button
                type="button"
                class="btn btn-dark"
                data-toggle="tooltip"
                :data-original-title="color +'のツールチップ'">{{ color }}</button>
        </div>
        <hr>
        <a class="m-2" href="#" @click.prevent="changeColors">色を変更</a>
    </div>
    <script src="https://code.jquery.com/jquery-3.4.1.min.js"></script>
    <script src="https://stackpath.bootstrapcdn.com/bootstrap/4.3.1/js/bootstrap.bundle.min.js"></script>
    <script src="https://cdn.jsdelivr.net/npm/vue@2.6.0"></script>
    <script>

        new Vue({
            el: '#app',
            data: {
                colors: []
            },
            methods: {
                changeColors() {

                    this.colors = ['金', '銀', '銅'];

                }
            },
            watch: {
                colors() {

                    Vue.nextTick(() => {

                        $('[data-toggle="tooltip"]').tooltip({
                            title() {

                                return $(this).attr('title');

                            }
                        });

                    })

                }
            },
            mounted() {

                this.colors = ['赤', '青', '黄色', '白', '黒'];

            }
        });

    </script>
</body>
</html>

ちなみに

通常のBootstrapを使っているとコンソールに以下のようなエラーが表示されることがあります。

TypeError: Bootstrap's tooltips require Popper.js

これは、「Bootstrapでツールチップを使うにはPopper.jsが必要です」という意味になります。

そのため、今回のようにCDNを使ってBootstrapを読み込むためには以下のようにBundleバージョンを読み込むといいでしょう。

<script src="https://stackpath.bootstrapcdn.com/bootstrap/4.3.1/js/bootstrap.bundle.min.js"></script>

もしくは、Popper.jsを追加で読み込んでください。

おわりに

ということで、今回はVue + Bootstrapでツールチップが動かない場合の対処法をお届けしました。

今回のツールチップに限らずですが、Vue+jQueryを使っていて「あれ、上手くいかないぞ」ということがあればnextTickを使うことを試してみるといいと思います。

また、今回とは関係ない話ですがBootstrap 4にはp-*(*は数字)と書くだけでパディングを設定できるんですね。m-*はマージンです。

もう一つの人気CSSフレームワークのtailwind CSSには同様のものがあるので、「これBootstrapにもあったらな〜」なんて思ってたら普通にありました😂(なんだったらBootstrapが本家だったりするのかな???)

ということで、また1つ賢くなれたVue記事でした。

ぜひ皆さんもVueでツールチップを試してみてくださいね。

ではでは〜!

この記事が役立ちましたらシェアお願いします😊✨