ボタンには常にtype="button"をつけよう

2023/08/08に公開

結論

ボタンを実装する際には、フォームのサブミットボタンでなければtype="button"をつけた方が良いです。

なぜか

HTMLのボタン要素は3つのtype属性を持つことができます。

  • submit
    type="submit"のボタンはフォームのデータをサーバーへ送信します。

  • button
    type="button"のボタンは規定の動作がなく、イベントハンドラーを渡して使うのが一般的となります。

  • reset
    type="reset"のボタンは

    すべてのコントロールを初期値の初期化します。<input type="reset"> と同様です。

そしてデフォルトのtype属性の値はsubmitです。
つまりtype属性を指定せずにボタンを置くとtype="submit"として動作します。

起こった問題

予想外の挙動をするパターン1

以下は郵便番号と住所を入力するシンプルなフォームです。
郵便番号に基づいて住所を検索できるボタンも設置しました。よくみるUIのパターンですね。

このとき、郵便番号を入力したあとにsearchボタンを押した場合、どのような挙動になるでしょうか?

form1.tsx
const Form = () => {
  return (
    <main>
      <form
        onSubmit={() => alert('submit!')}
      >
        <input
          type="text"
          placeholder="postcode"
        />
        <button
          onClick={() => alert('search!')}
        >
          search
        </button>
        <input
          type="text"
          placeholder="address"
        />
        <button type="submit">
          submit
        </button>
      </form>
    </main>
  );
};

export default Form;

上のソースコードの場合、以下のように動作します。
予想外の挙動をするパターン1
なんとsearch!と出たあとにsubmit!も表示されています。
つまりサブミットボタンが押され、フォームが送信されているということです。

予想外の挙動をするパターン2

先ほどと同じフォームですべての項目を入力し、今度はEnterキーを押してみます。
一般的なフォームの場合、Enterキーを押すことでフォームが送信されることを想定します。

しかし上記のソースコードの場合、以下のように動作します。
予想外の挙動をするパターン2

こちらも先ほどと同様にsearch!が出力され、そのあとにsubmit!が表示されます。

安全なフォーム

はじめに出てきたソースコードに少し変更を加えます。
結論で紹介したようにサブミットボタン以外のボタンにtype="button"をつけます。

以下のようになります。見やすいようにスタイルを除いています。

form2.tsx
 const Form = () => {
   return (
     <main>
       <form
         onSubmit={() => alert('submit!')}
       >
         <input
           type="text"
           placeholder="postcode"
          />
         <button
+          type="button"
           onClick={() => alert('search!')}
         >
           search
         </button>
         <input
           type="text"
           placeholder="address"
         />
         <button type="submit">
           submit
         </button>
       </form>
     </main>
   );
 };

 export default Form;

上記のソースコードの場合、以下のように動作します。

searchボタンを押したとき

searchボタンを押したとき

Enterキーを押したとき

Enterキーを押したとき

どちらも期待した通りに動作します。

まとめ

フォームを作成する際には、サブミットボタン以外はボタンにtype="button"を設定する習慣をつけることは非常に大切だと思います。思いもよらぬ挙動やバグを未然に防ぐことができます。

またJavaScriptで特別な動作を実装する場合は必ずtype="button"を使うのが良いと思います。
UIフレームワークをラップして使う場合はデフォルトを設定しなおして使うのも良いですね。

参考

https://developer.mozilla.org/ja/docs/Web/HTML/Element/button

Discussion