0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

はじめに

実務で対応したバグの内容について、調べるとすぐに修正方法はわかったのですが、「簡単そうで、意外となんでそうなるのかよくわからない」内容でした。

調べた実装方法と検証結果を記録に残しておきます。

先に結論を書きます。

<input>要素のonKeyDownイベントを使用して、Enterキーが押されたときにフォームのSubmitを防ごう!

const handleKeyDown = (event: KeyboardEvent<HTMLInputElement>):void => {
    if (event.key === 'Enter') {
      event.preventDefault();
    }
};

const handleSubmit = (event: FormEvent<HTMLFormElement>) => {
    event.preventDefault();
  };

<form onSubmit={handleSubmit}>
    <input
        type="text"
        value={query}
        onChange={(e) => setQuery(e.target.value)}
        onKeyDown={handleKeyDown}
    />
    <button type="submit">Submit</button>
</form>
  

要件

  • <form>要素の中に、複数の<input>要素がある場合を考えます
  • このうち1つは、表示する要素を絞り込むための検索に使用する<input>要素です
  • このとき現在の実装では、検索で使用する<input>要素に文字を入力しEnterキーを押した時、<form>要素のSubmitが実行されてしまいます
  • この<input>要素に文字を入力している状態でEnterキーを押したときは、<form>要素のSubmitを実行させないようにしたいです
  • 他の<input>要素に文字を入力している状態でEnterキーを押したときは、<form>要素のSubmitは実行して構いません

現在の実装

現在の実装を簡略化して示します。
<form>要素のhandleSubmitが実行されるとき、Form submitted!!とconsoleに出力されるようにしました。
ユーザー検索用の<input>要素に文字を入力しEnterキーを押すとForm submitted!!が出力されてしまいます😢これが出力されないようにしたいです。

実装

調べてみると、<form>要素の中の<input>要素で、Enterキーが押されたときにフォームのSubmitを防ぐためにはonKeyDownイベントを使うと簡単に実装できることがわかりました。

<input>要素のonKeyDownイベントを使用して、Enterキーが押されたときにフォームのSubmitを防ごう!

const handleKeyDown = (event: KeyboardEvent<HTMLInputElement>):void => {
    if (event.key === 'Enter') {
      event.preventDefault();
    }
};

const handleSubmit = (event: FormEvent<HTMLFormElement>) => {
    event.preventDefault();
  };

<form onSubmit={handleSubmit}>
    <input
        type="text"
        value={query}
        onChange={(e) => setQuery(e.target.value)}
        onKeyDown={handleKeyDown}
    />
    <button type="submit">Submit</button>
</form>
  

解説

ここからは検証しながら、なぜこのような実装になるのかを考えていきます。
2つのポイントがあります。

イベントのバブリング

JavaScriptのイベントには「バブリング」という特性があります。イベントが発生した要素(今回は <input>要素)から親要素(今回は<form>要素)に伝播していきます。
この特性により、<input>要素内で発生したonKeyDownイベントは、<form>要素にも到達します。

preventDefaultでデフォルト動作をキャンセル

handleKeyDownhandleSubmitevent.preventDefault()が出てきました。
これにより、そのイベントのデフォルト動作がキャンセルされます。

それぞれ、キャンセルされているデフォルト動作が違うらしいです🤔

  • handleKeyDown
    <input>要素のEnterキーが押されると、通常は<form>要素のsubmitがトリガーされます。しかし、preventDefaultを呼び出すと、このデフォルトのsubmit動作がキャンセルされます。

  • handleSubmit
    通常、フォームのsubmitボタンをクリックするか、フォーム内でEnterキーを押すと、ブラウザは以下の動作を行います。

    1. フォームデータの送信:フォーム内のすべての入力フィールドのデータをサーバーに送信します。送信先は、<form>要素のaction属性で指定されたURL
    2. ページのリロード:デフォルトでは、フォームが送信されると現在のページがリロードされる

handleSubmitのpreventDefaultについては、消してみると確かにページがリロードされてしまったので、調べた内容は正しそうでした。

handleKeyDownのpreventDefaultについては、ちょっとよくわからないので検証してみました。

検証①: handleKeyDownのevent.preventDefault()を残す

①handleKeyDownのevent.preventDefault()を残す
const handleKeyDown = (event: KeyboardEvent<HTMLInputElement>) => {
    console.log("onKeyDown event triggered");
    if (event.key === "Enter") {
      console.log("Enter key pressed");
      event.preventDefault();
    }
};

const handleSubmit = (event: FormEvent<HTMLFormElement>) => {
    event.preventDefault();
    console.log("Form submitted!!");
  };

// 1. input要素にカーソルを当てて任意のキーを押す
    // => onKeyDown event triggered

// 2. input要素にカーソルを当ててEnterキーを押す
    // => onKeyDown event triggered
    // => Enter key pressed

検証②: handleKeyDownのevent.preventDefault()を消す

②handleKeyDownのevent.preventDefault()を消す
const handleKeyDown = (event: KeyboardEvent<HTMLInputElement>) => {
    console.log("onKeyDown event triggered");
    if (event.key === "Enter") {
      console.log("Enter key pressed");
    }
};

const handleSubmit = (event: FormEvent<HTMLFormElement>) => {
    event.preventDefault();
    console.log("Form submitted!!");
  };

// 1. input要素にカーソルを当てて任意のキーを押す
    // => onKeyDown event triggered

// 2. input要素にカーソルを当ててEnterキーを押す
    // => onKeyDown event triggered
    // => Enter key pressed
    // => Form submitted!!

出力結果から、確かにhandleKeyDownのpreventDefaultによって<form>要素のsubmitがトリガーされるのが防がれていることが確認できました。

handleKeyDownpreventDefaultがない場合、イベントバブリングによって<input>要素のhandleKeyDownイベントが親要素の<form>に伝播するので、handleSubmitがトリガーされます。しかし、handleSubmitpreventDefaultがデフォルト動作を止めているので、フォームデータの送信等は実行されません。

名前を入力してEnterキーを押したときは、submitできているので要件も満たすことができました。

終わりに

「あるある」な実装のようで、修正方法について書かれている記事はたくさんありました。
自分で動かして検証したことで、納得できたのでよかったです!

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?