Vue.jsとVueUseでフォーカスの逃げないModalを作るメモ(備忘録)
フォーカスが外に・・・
モーダルを作るときにフォーカスが後ろにいかないよう制御が必要だと思いますが、
VueUseで「focus-trap」をラップした機能が使えるのでそれを使用します。
VueUseインストール
Vue3.2系が使える環境にVueUseをインストール
※focus-trapをラップしている機能なのでfocus-trapもインストール
npm i @vueuse/integrations
npm i focus-trap
モーダルなコンポーネントのSFCで・・・
まずは完成形
※デモページで含まれる不要な要素は割愛
<script setup lang="ts">
import { useFocusTrap } from '@vueuse/integrations/useFocusTrap';
import { ref, watch } from 'vue';
type Props = {
isShow: boolean;
};
const props = defineProps<Props>();
type Emits = {
(e: 'close'): void;
};
const emit = defineEmits<Emits>();
const target = ref();
const { hasFocus, activate, deactivate } = useFocusTrap(target);
</script>
<template>
<div class="card" ref="target">
<div
class="card-header bg-primary text-white d-flex justify-content-between align-items-center"
>
フォーカス制御されたモーダル1
<button
type="button"
class="btn btn-outline-light"
@click="emit('close')"
>
閉じる{{ hasFocus }}
</button>
</div>
<div class="card-body bg-light">
<div class="mb-3">
<label class="form-label">入力</label>
<input type="text" class="form-control" placeholder="" />
</div>
</div>
</div>
</template>
詳しく
useFocusTrapをImport
import { useFocusTrap } from '@vueuse/integrations/useFocusTrap';
focusTrapをon-offするために親要素からモーダルが表示しているかどうかの情報を受取る
type Props = {
isShow: boolean;
};
const props = defineProps<Props>();
focusTrapを設置する要素用のrefオブジェクト
const target = ref();
モーダルの表示非表示を見てfocusTrapを制御
watch(
() => props.isShow,
() => {
if (props.isShow) {
activate();
} else {
deactivate();
}
}
);
テンプレートでフォーカス制御する要素にrefをあてるのを忘れずに。
<div class="card" @click.stop ref="target">
でもこれだと?
「モーダル要素の外をクリックしてモーダルを閉じる」ができなくなる。
※緑の領域のクリックイベントが発火しない
解決策
const { hasFocus, activate, deactivate } = useFocusTrap(target, {
allowOutsideClick: true,
});
useFocusTrapの第2引数に[allowOutsideClick]を設定すればクリックが可能に。
GitHubにサンプル上げてます
ついでにGitHubにデモページも・・・