v-form (Vuetify) の submit イベントによってページがリロードされないようにする

今回は Vuetify でシンプルな入力画面を実装するといった時に遭遇するかもしれない挙動の一つとその対応について紹介します。
v-form の中に一つの v-text-field を配置し、フォーカスを当てた状態で Enter キーを押すとページがリロードされることがあります。
投稿されて1年以上経過しているものですが、以下 Vuetify の GitHub Issue にも Bug Report がありました。


リロードされる原因

この問題、実は Vuetify に限った話ではなく、前からある Implicit submission (暗黙の submit)という HTML の仕様によるもので、バグということではありません。

具体的にはデフォルトボタンによる submit が適用される場合で、入力中に Enter キーによって submit イベントが発火します。HTML の仕様書を読んでみると多少ややこしい記述にはなっていますが、少し抜粋して要約・補足すると下記のようになります。
🔗https://html.spec.whatwg.org/multipage/form-control-infrastructure.html#implicit-submission

If the form has no submit button, then the implicit submission mechanism must do nothing if the form has more than one field that blocks implicit submission, and must submit the form element from the form element itself otherwise.

form内に submit ボタンがない時、

  • 暗黙の submit をブロックする項目が2つ以上ある場合:暗黙の submit は動作しない
  • 暗黙の submit をブロックする項目が1つある場合:Form document の URI へ submit する

と定義されています。

For the purpose of the previous paragraph, an element is a field that blocks implicit submission of a form element if it is an input element whose form owner is that form element and whose type attribute is in one of the following states: Text, Search, URL, Telephone, E-mail, Password, Date, Month, Week, Time, Local Date and Time, Number

暗黙の submit をブロックする項目とは、form の中にある input 要素で、type 属性が Text, Search, URL, Telephone, E-mail, Password, Date, Month, Week, Time, Local Date and Time, Number というものです。

つまり、HTMLとしては「一つのテキストフィールドがある」及び「submit ボタン (type="submit") がない」状態では、 Enter キーを押すことで sutmit するのが正しい仕様ということですね。

v-form コンポーネントを使用する場合、ボタンに v-btn を利用するとtype="button"となるため、ブラウザによって暗黙の submit として扱われるようになることが原因となります。

CodePen で再現してみると以下のようになります。
※ Vue.js 2.6, Vuetify 2.0 を使っています。

See the Pen(Vuetify) v-form reload issue sampleby lopburny (ロップバーニー) (@lopburny) onCodePen.

解決方法

以下のように submit イベントを抑えてしまいます。

1
2
<v-form ref="form" @submit.prevent>
<v-text-field ...

CodePen のサンプルはこちらです。
※ Vue.js の省略記法を使っています。

See the Pen(Vuetify) v-form reload issue sample - resolvedby lopburny (ロップバーニー) (@lopburny) onCodePen.

Vue.js 公式ドキュメントにも、ピンポイントで対応方法が記載されています。

1
2
<!-- submit イベントによってページがリロードされません -->
<form v-on:submit.prevent="onSubmit"></form>

ユーザーに対して明示的に submit ボタンを押してもらうアクションを期待している場合は、これで解決します。
form 要素の submit イベントを抑制し、ボタンを押したときのクリックイベントで対応する形です。

しかし、あえて Enter キーを押した場合に submit イベントを発火したいということもあります。
Vuetify の場合、v-form コンポーネントと各入力要素の rule 属性によるバリデーションを実装することが多いので、このケースはあまりないかと思いますが、一応上記に加え下記のように対応することで実現できます。

1
<v-text-field @keyup.enter="customMethod()"></v-text-field>

いかがだったでしょうか。

今回紹介した挙動については、古くからある HTML の仕様なので Vuetify に限らず多くの記事が出ておりますが、
Vuetify 特有のバグや癖だと思ってハマることもあるかと思い、記事にしてみました。
最後まで読んでいただきありがとうございました。