🗂
vee-validateを使ってinvalidのinput要素にfocusする方法
はじめに
タイトルの通り、vee-validateを使ってinvalidになった(不正な値が入力された)input要素にfocusをしたかったです。
vee-validateを使うと、デフォルトでinvalidになったinput要素を赤枠で囲ってくれるが、それだけだとユーザ体験としてよろしくないform(※)があったからです。
※:formが縦に長く、画面内に全てのinput要素が収まらず、invalidになった要素がどこにあるのかわからないform
まずは公式ドキュメントを読む
まず公式ドキュメントを読みましたが、公式として提供はしていなそう。。
ということで自分で実装しました
ValidationObserver
をラップした形で拡張していきます。
<template>
<ValidationObserver v-bind="$attrs" ref="validationObserver">
<slot v-bind:passes="passes"></slot>
</ValidationObserver>
</template>
<script lang="ts">
import { Component, Vue } from "vue-property-decorator";
// 指定した要素までスクロールさせるUtilメソッドです
import { scrollTo } from "@/utils/scrollUtil";
@Component
export default class CustomValidationObserver extends Vue {
passes = async (action: Function) => {
// eslint-disable-next-line @typescript-eslint/no-explicit-any
const isValid = await (this.$refs.validationObserver as any).validate();
if (!isValid) {
const element = document.querySelector(".is-invalid") as HTMLElement;
if (!element) {
return;
} else if (element.nodeName === "SELECT") {
this.focusSelectElement(element);
} else {
element.focus();
}
return;
}
await action();
};
// 通常のfocusだとプルダウンの選択肢が全面表示されるため、本メソッドで他input要素と同様のfocusの挙動を行う
focusSelectElement = (element: HTMLElement) => {
element.classList.add("focus");
scrollTo(window, element, { offset: -90, duration: 0 });
// デフォルトの「他の要素をクリックした際にフォーカスが外れる挙動」を再現するためにイベントを追加
document.addEventListener("click", () => this.removeFocusClass(element), {
once: true
});
};
removeFocusClass = (element: HTMLElement) => {
// 保存ボタン押下時のclickイベントで意図せずリスナーが実行されるため、イベントリスナー内で再度リスナーを登録
// 保存ボタン押下の次のclickイベント時に、focusクラスをremoveするリスナーを実行させる
document.addEventListener(
"click",
() => {
element.classList.remove("focus");
},
{
once: true,
capture: true
}
);
};
}
</script>
<style lang="scss">
.focus {
border-color: #d23e36;
box-shadow: 0 0 0 3px #f4cccc;
}
</style>
ポイント
- invalid要素にはis-invalidクラスが付与されるので、
const element = document.querySelector(".is-invalid") as HTMLElement;
で対象要素を取得しています。
補足
- iOS, Androidでは、select要素にfocusをした場合、自動でプルダウンが開いてしまうため、手動で赤枠 + 対象の要素までスクロールすることで擬似的にfocusさせるようにしています。
- ↑と同じくselect要素で擬似的なfocus処理を実現させるために、removeFocusClass()で
他要素がクリックされた際に自動でinvalid要素の赤枠が外れる
挙動を実現させています。
Discussion