Modal Dialog
要求
- 素早く簡単に Modal Dialiaog が実装できるよう、コンポーネント化したい。
Code
- ModalContainer.js
import { ref, reactive } from 'https://cdnjs.cloudflare.com/ajax/libs/vue/3.0.2/vue.esm-browser.js'; const ModalContainer = { template: ` <component is="style"> .modal { display: none; } .modal-backdrop { z-index: 0; background: rgba(0, 0, 0, 0.4); } /* vueのtransitionを使わないなら不要 */ .fade-enter-active, .fade-leave-active { transition: opacity .15s; } .fade-enter, .fade-leave-to { opacity: 0; } </component> <transition name="fade"> <div v-if="visible"> <div class="modal" tabindex="-1" :style="display_mode"> <div class="modal-backdrop show" @click="hide"></div> <div class="modal-dialog" :class="size"> <div class="modal-content"> <div class="modal-header"> <slot name="header"> <button type="button" class="close" @click="hide"><span>×</span></button> <h5 class="modal-title">{{ title }}</h5> </slot> </div> <div class="modal-body"> <slot name="body"> <p>Modal body text goes here.</p> </slot> </div> <div class="modal-footer"> <slot name="footer"> <button type="button" class="btn btn-default" @click="hide">Close</button> <button type="button" class="btn btn-primary">Save changes</button> </slot> </div> </div> </div> </div> </div> </transition> `, props: { enableBackdrop: { type: Boolean, default: true }, title: { type: String, default: '' }, size: { type: String, default: 'modal-sm' } }, setup(props, context) { const visible = ref(false) const display_mode = reactive({display: 'none'}) let scrollbarWidth = 0; let orgBodyPaddingRight = 0; // let orgBodyPosition = 'relative'; function show() { scrollbarWidth = window.innerWidth - document.body.clientWidth; orgBodyPaddingRight = document.body.style.paddingRight; // orgBodyPosition = document.body.style.position; // document.body.style.position = 'fixed'; if (scrollbarWidth ) { document.body.style.paddingRight = `${scrollbarWidth }px`; } // document.body.style.position = 'fix'; document.body.style.height = '100vh'; document.body.style.overflowY = 'hidden'; visible.value = true; display_mode.display = 'block'; } function hide(event) { if (event && event.target.classList.contains('modal-backdrop') && !props.enableBackdrop) return; if (scrollbarWidth ) { document.body.style.paddingRight = orgBodyPaddingRight; } // document.body.style.position = orgBodyPosition; document.body.style.height = 'auto'; document.body.style.overflowY = 'auto'; visible.value = false; display_mode.display = 'none'; } return { visible, display_mode, show, hide } } }; export default ModalContainer;
Props
- size: サイズ
- title: タイトル
- enableBackdrop: バックドロップの有効化
Slots
- header スロット
- body スロット
- footer スロット
Events
- None
Demo
Bootstrap バージョンよる記述の違い調査
BS5 の実装
<div class="modal" tabindex="-1"> <div class="modal-dialog"> <div class="modal-content"> <div class="modal-header"> <h5 class="modal-title">Modal title</h5> <button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button> </div> <div class="modal-body"> <p>Modal body text goes here.</p> </div> <div class="modal-footer"> <button type="button" class="btn btn-secondary" data-bs-dismiss="modal">Close</button> <button type="button" class="btn btn-primary">Save changes</button> </div> </div> </div> </div>
BS4 の実装
<div class="modal fade" id="exampleModal" tabindex="-1" role="dialog" aria-labelledby="exampleModalLabel" aria-hidden="true"> <div class="modal-dialog" role="document"> <div class="modal-content"> <div class="modal-header"> <h5 class="modal-title" id="exampleModalLabel">Modal title</h5> <button type="button" class="close" data-dismiss="modal" aria-label="Close"> <span aria-hidden="true">×</span> </button> </div> <div class="modal-body"> <p>Modal body text goes here.</p> </div> <div class="modal-footer"> <button type="button" class="btn btn-secondary" data-dismiss="modal">Close</button> <button type="button" class="btn btn-primary">Save changes</button> </div> </div> </div> </div>
BS3 の実装
<div class="modal fade" id="sampleModal" tabindex="-1"> <div class="modal-backdrop fade in" style="height: 969px;"></div> <div class="modal-dialog"> <div class="modal-content"> <div class="modal-header"> <button type="button" class="close" data-dismiss="modal"><span>×</span></button> <h5 class="modal-title">Modal title</h5> </div> <div class="modal-body"> <p>Modal body text goes here.</p> </div> <div class="modal-footer"> <button type="button" class="btn btn-default" data-dismiss="modal">Close</button> <button type="button" class="btn btn-primary">Save changes</button> </div> </div> </div> </div>
◆ BS3 .modal-open .modal { overflow-x: hidden; overflow-y: auto; } .fade.in { opacity: 1; } .modal-backdrop.in { filter: alpha(opacity=50); opacity: .5; } .modal-backdrop.fade { filter: alpha(opacity=0); opacity: 0; } .modal { position: fixed; top: 0; right: 0; bottom: 0; left: 0; z-index: 1040; display: none; overflow: hidden; -webkit-overflow-scrolling: touch; outline: 0; } .modal-backdrop { position: absolute; top: 0; right: 0; left: 0; background-color: #000; } .fade { opacity: 0; -webkit-transition: opacity .15s linear; -o-transition: opacity .15s linear; transition: opacity .15s linear; }
- div のネストや指定が必要な CSS クラスもほぼ同様
手動でモーダルを表示するには、いろいろとやることがある
- div.modal.fade を div.modal.fade.in に変更
- div.modal-backdrop の z-index を削除
- これでも裏のページがスクロールしてしまうので、以下の対策を。。。