TechVue.jsJapanese

[Vue3] Vue 3 プロジェクトで Bootstrap 5 のドロップダウンを使う方法

Tech

以前紹介した  Vue3 で Bootstrap 5 を使う方法ですが、 Bootstrap 5 ではそれまでのバージョンに比べ結構実装方法が異なっているため、ドロップダウンを使うために一手間必要になります。今回は、 Bootstrap 5 でドロップダウンを実装する方法について紹介します。

Bootstrap 5 を有効にしただけではドロップダウンが動かない

まずは以前紹介した方法で Bootstrap 5 を有効にし、公式サイトからサンプルコードを貼付け実行してみます。

main.js

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
<span class="hljs-keyword">import</span> { createApp } <span class="hljs-keyword">from</span> <span class="hljs-string">'vue'</span>
<span class="hljs-keyword">import</span> App <span class="hljs-keyword">from</span> <span class="hljs-string">'./App.vue'</span>
<span class="hljs-keyword">import</span> <span class="hljs-string">"bootstrap/dist/css/bootstrap.min.css"</span>
createApp(App).mount(<span class="hljs-string">'#app'</span>)
<span class="hljs-keyword">import</span> { createApp } <span class="hljs-keyword">from</span> <span class="hljs-string">'vue'</span> <span class="hljs-keyword">import</span> App <span class="hljs-keyword">from</span> <span class="hljs-string">'./App.vue'</span> <span class="hljs-keyword">import</span> <span class="hljs-string">"bootstrap/dist/css/bootstrap.min.css"</span> createApp(App).mount(<span class="hljs-string">'#app'</span>)
import { createApp } from 'vue'
import App from './App.vue'

import "bootstrap/dist/css/bootstrap.min.css"

createApp(App).mount('#app')

App.vue

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
<span class="hljs-tag"><<span class="hljs-name">template</span>></span>
<span class="hljs-tag"><<span class="hljs-name">div</span>></span>
<span class="hljs-tag"><<span class="hljs-name">BSDropdown</span> /></span>
<span class="hljs-tag"></<span class="hljs-name">div</span>></span>
<span class="hljs-tag"></<span class="hljs-name">template</span>></span>
<span class="hljs-tag"><<span class="hljs-name">script</span>></span><span class="javascript">
<span class="hljs-keyword">import</span> BSDropdown <span class="hljs-keyword">from</span> <span class="hljs-string">"./components/BSDropdown.vue"</span>;
<span class="hljs-keyword">export</span> <span class="hljs-keyword">default</span> {
<span class="hljs-attr">name</span>: <span class="hljs-string">"App"</span>,
<span class="hljs-attr">components</span>: {
BSDropdown,
},
};
</span><span class="hljs-tag"></<span class="hljs-name">script</span>></span>
<span class="hljs-tag"><<span class="hljs-name">style</span>></span><span class="css">
<span class="hljs-selector-id">#app</span> {
<span class="hljs-attribute">font-family</span>: Avenir, Helvetica, Arial, sans-serif;
<span class="hljs-attribute">-webkit-font-smoothing</span>: antialiased;
<span class="hljs-attribute">-moz-osx-font-smoothing</span>: grayscale;
<span class="hljs-attribute">text-align</span>: center;
<span class="hljs-attribute">color</span>: <span class="hljs-number">#2c3e50</span>;
<span class="hljs-attribute">margin-top</span>: <span class="hljs-number">60px</span>;
}
</span><span class="hljs-tag"></<span class="hljs-name">style</span>></span>
<span class="hljs-tag"><<span class="hljs-name">template</span>></span> <span class="hljs-tag"><<span class="hljs-name">div</span>></span> <span class="hljs-tag"><<span class="hljs-name">BSDropdown</span> /></span> <span class="hljs-tag"></<span class="hljs-name">div</span>></span> <span class="hljs-tag"></<span class="hljs-name">template</span>></span> <span class="hljs-tag"><<span class="hljs-name">script</span>></span><span class="javascript"> <span class="hljs-keyword">import</span> BSDropdown <span class="hljs-keyword">from</span> <span class="hljs-string">"./components/BSDropdown.vue"</span>; <span class="hljs-keyword">export</span> <span class="hljs-keyword">default</span> { <span class="hljs-attr">name</span>: <span class="hljs-string">"App"</span>, <span class="hljs-attr">components</span>: { BSDropdown, }, }; </span><span class="hljs-tag"></<span class="hljs-name">script</span>></span> <span class="hljs-tag"><<span class="hljs-name">style</span>></span><span class="css"> <span class="hljs-selector-id">#app</span> { <span class="hljs-attribute">font-family</span>: Avenir, Helvetica, Arial, sans-serif; <span class="hljs-attribute">-webkit-font-smoothing</span>: antialiased; <span class="hljs-attribute">-moz-osx-font-smoothing</span>: grayscale; <span class="hljs-attribute">text-align</span>: center; <span class="hljs-attribute">color</span>: <span class="hljs-number">#2c3e50</span>; <span class="hljs-attribute">margin-top</span>: <span class="hljs-number">60px</span>; } </span><span class="hljs-tag"></<span class="hljs-name">style</span>></span>
<template>
  <div>
    <BSDropdown />
  </div>
</template>

<script>
import BSDropdown from "./components/BSDropdown.vue";

export default {
  name: "App",
  components: {
    BSDropdown,
  },
};
</script>

<style>
#app {
  font-family: Avenir, Helvetica, Arial, sans-serif;
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
  text-align: center;
  color: #2c3e50;
  margin-top: 60px;
}
</style>

components / BSDropdown.vue

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
<span class="hljs-tag"><<span class="hljs-name">template</span>></span>
<span class="hljs-tag"><<span class="hljs-name">div</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"dropdown"</span>></span>
<span class="hljs-tag"><<span class="hljs-name">button</span>
<span class="hljs-attr">class</span>=<span class="hljs-string">"btn btn-secondary dropdown-toggle"</span>
<span class="hljs-attr">type</span>=<span class="hljs-string">"button"</span>
<span class="hljs-attr">id</span>=<span class="hljs-string">"dropdownMenuButton"</span>
<span class="hljs-attr">data-bs-toggle</span>=<span class="hljs-string">"dropdown"</span>
<span class="hljs-attr">aria-expanded</span>=<span class="hljs-string">"false"</span>
></span>
Dropdown button
<span class="hljs-tag"></<span class="hljs-name">button</span>></span>
<span class="hljs-tag"><<span class="hljs-name">ul</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"dropdown-menu"</span> <span class="hljs-attr">aria-labelledby</span>=<span class="hljs-string">"dropdownMenuButton"</span>></span>
<span class="hljs-tag"><<span class="hljs-name">li</span>></span><span class="hljs-tag"><<span class="hljs-name">a</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"dropdown-item"</span> <span class="hljs-attr">href</span>=<span class="hljs-string">"#"</span>></span>Action<span class="hljs-tag"></<span class="hljs-name">a</span>></span><span class="hljs-tag"></<span class="hljs-name">li</span>></span>
<span class="hljs-tag"><<span class="hljs-name">li</span>></span><span class="hljs-tag"><<span class="hljs-name">a</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"dropdown-item"</span> <span class="hljs-attr">href</span>=<span class="hljs-string">"#"</span>></span>Another action<span class="hljs-tag"></<span class="hljs-name">a</span>></span><span class="hljs-tag"></<span class="hljs-name">li</span>></span>
<span class="hljs-tag"><<span class="hljs-name">li</span>></span><span class="hljs-tag"><<span class="hljs-name">a</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"dropdown-item"</span> <span class="hljs-attr">href</span>=<span class="hljs-string">"#"</span>></span>Something else here<span class="hljs-tag"></<span class="hljs-name">a</span>></span><span class="hljs-tag"></<span class="hljs-name">li</span>></span>
<span class="hljs-tag"></<span class="hljs-name">ul</span>></span>
<span class="hljs-tag"></<span class="hljs-name">div</span>></span>
<span class="hljs-tag"></<span class="hljs-name">template</span>></span>
<span class="hljs-tag"><<span class="hljs-name">template</span>></span> <span class="hljs-tag"><<span class="hljs-name">div</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"dropdown"</span>></span> <span class="hljs-tag"><<span class="hljs-name">button</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"btn btn-secondary dropdown-toggle"</span> <span class="hljs-attr">type</span>=<span class="hljs-string">"button"</span> <span class="hljs-attr">id</span>=<span class="hljs-string">"dropdownMenuButton"</span> <span class="hljs-attr">data-bs-toggle</span>=<span class="hljs-string">"dropdown"</span> <span class="hljs-attr">aria-expanded</span>=<span class="hljs-string">"false"</span> ></span> Dropdown button <span class="hljs-tag"></<span class="hljs-name">button</span>></span> <span class="hljs-tag"><<span class="hljs-name">ul</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"dropdown-menu"</span> <span class="hljs-attr">aria-labelledby</span>=<span class="hljs-string">"dropdownMenuButton"</span>></span> <span class="hljs-tag"><<span class="hljs-name">li</span>></span><span class="hljs-tag"><<span class="hljs-name">a</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"dropdown-item"</span> <span class="hljs-attr">href</span>=<span class="hljs-string">"#"</span>></span>Action<span class="hljs-tag"></<span class="hljs-name">a</span>></span><span class="hljs-tag"></<span class="hljs-name">li</span>></span> <span class="hljs-tag"><<span class="hljs-name">li</span>></span><span class="hljs-tag"><<span class="hljs-name">a</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"dropdown-item"</span> <span class="hljs-attr">href</span>=<span class="hljs-string">"#"</span>></span>Another action<span class="hljs-tag"></<span class="hljs-name">a</span>></span><span class="hljs-tag"></<span class="hljs-name">li</span>></span> <span class="hljs-tag"><<span class="hljs-name">li</span>></span><span class="hljs-tag"><<span class="hljs-name">a</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"dropdown-item"</span> <span class="hljs-attr">href</span>=<span class="hljs-string">"#"</span>></span>Something else here<span class="hljs-tag"></<span class="hljs-name">a</span>></span><span class="hljs-tag"></<span class="hljs-name">li</span>></span> <span class="hljs-tag"></<span class="hljs-name">ul</span>></span> <span class="hljs-tag"></<span class="hljs-name">div</span>></span> <span class="hljs-tag"></<span class="hljs-name">template</span>></span>
<template>
  <div class="dropdown">
    <button
      class="btn btn-secondary dropdown-toggle"
      type="button"
      id="dropdownMenuButton"
      data-bs-toggle="dropdown"
      aria-expanded="false"
    >
      Dropdown button
    </button>
    <ul class="dropdown-menu" aria-labelledby="dropdownMenuButton">
      <li><a class="dropdown-item" href="#">Action</a></li>
      <li><a class="dropdown-item" href="#">Another action</a></li>
      <li><a class="dropdown-item" href="#">Something else here</a></li>
    </ul>
  </div>
</template>

上記コードの内容で実行すると、以下のように Bootstrap のドロップダウンがエラーなく表示されるのですが、クリックしても何も動作することはありません。

Bootstrap 5 におけるドロップダウンでは Popper が必要

公式ドキュメントをきちんと読むと、以下の説明を見つけることができます。

ドロップダウンはサードパーティのライブラリであるPopper.jsを使って構築されており、動的なポジショニングとビューポート検出を提供しています。必ずBootstrapのJavaScriptの前にpopper.min.jsをインクルードするか、Popper.jsを含むbootstrap.bundle.min.js / bootstrap.bundle.jsを使用してください。

https://getbootstrap.jp/docs/5.0/components/dropdowns/

どうやら、 Popper を活用する必要があるようです。

Popper について

 Popper は、簡潔に言うと「ツールチップとポップオーバーに関連するもの」です。

Given an element, such as a button, and a tooltip element describing it, Popper will automatically put the tooltip in the right place near the button.

https://www.npmjs.com/package/@popperjs/core

「ボタン等のエレメントと、記載されたツールチップがあると、 Popper は自動的にそのボタンの近くの適切な場所にツールチップを配置します」というような紹介文が掲載されています。百聞は一見にしかず、ということで公式サイトにデモが掲載されていますので、そちらを見ていただくと良いと思います。

現状は Popper 2 がメインストリームのようなので、まずはこちらをインストールすることことからはじめます。

Popper 2 のインストール

公式サイトの手順に従ってすすめていきます。

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
% <span class="hljs-built_in">npm</span> i @popperjs/core
% <span class="hljs-built_in">npm</span> i @popperjs/core
% npm i @popperjs/core

package.json の内容をみていきます。今回は、 以下のようなバージョンでインストールすることができました。

main.js で Bootstrap bundle をインポート

次に、 公式ドキュメントで言及されている通り、 popper を利用可能にしていきます。今回は、 main.js に bootstrap.bundle.min.js をインポートする方法で進めていきます。

main.js

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
<span class="hljs-keyword">import</span> { createApp } from 'vue'
<span class="hljs-keyword">import</span> App from './App.vue'
<span class="hljs-keyword">import</span> <span class="hljs-string">"bootstrap/dist/css/bootstrap.min.css"</span>
<span class="hljs-keyword">import</span> <span class="hljs-string">"bootstrap/dist/js/bootstrap.bundle.min.js"</span>
createApp(App).mount(<span class="hljs-string">'#app'</span>)
<span class="hljs-keyword">import</span> { createApp } from 'vue' <span class="hljs-keyword">import</span> App from './App.vue' <span class="hljs-keyword">import</span> <span class="hljs-string">"bootstrap/dist/css/bootstrap.min.css"</span> <span class="hljs-keyword">import</span> <span class="hljs-string">"bootstrap/dist/js/bootstrap.bundle.min.js"</span> createApp(App).mount(<span class="hljs-string">'#app'</span>)
import { createApp } from 'vue'
import App from './App.vue'

import "bootstrap/dist/css/bootstrap.min.css"
import "bootstrap/dist/js/bootstrap.bundle.min.js"

createApp(App).mount('#app')

Bootstrap 5 でのドロップダウン実装

popper をインストールし、 main.js 内にインポート行を追記したら、改めて実行してみます。今度は、ただ表示されるだけではなく、クリックに反応するようになりました。

まとめ

Vue2 + bootstrap-vue とは少しお作法が異なる Vue3 + Bootstrap 5 ですが、基本的には”より簡易に実装できる方向”に進化していると思います。まだまだ、枯れていないところですので、色々試行錯誤しながらわかった点はブログに残していきたいと思います。

  • Bootstrap 5 でドロップダウン使うには Popper 2 が必要
  • main.js で、Popper を含む bootstrap.bundle.min.js をインポートする必要あり
  • 選択した値を取得するには Vue3 Multiselect を用いる必要あり (関連記事

関連記事

Ads