📮

HTML formのinputでEnterキーを押下するとsubmitボタンのclickイベントが発火する

2022/06/20に公開

問題のコード

突然ですが、みなさんは以下のコードが与えられたとき、<input>要素でEnterキーが押下されたときの挙動を想像できるでしょうか?コードの下にCodeSandboxを貼っているので、答え合わせがてら動かしてみてください。

document.getElementById("app").innerHTML = `
<h1>HTML form</h1>
<h2>inputでEnterキーを押すと何が起きるでしょう?</h2>
<form id='form'>
  <input type='text' name='input'>
  <button id='greeting'>Greeting</button>
  <button id='send' type='submit'>Submit</button>
</form>
`;

const form = document.getElementById("form");
const greetingButton = document.getElementById("greeting");

form.addEventListener("submit", (e) => {
  e.preventDefault();
  console.log("Submitted!");
});

greetingButton.addEventListener("click", (e) => {
  console.log("Hello!");
});

結果

結果はタイトルの通りなのですが、上記のサンプルの<input>においてEnterキーを押下すると、console出力は以下のようになります。

Hello! 
Submitted! 

すなわち、formのsubmitイベントと合わせて、Greetingボタンのclickイベントが発火しています。私はEnterキー押下でフォームのsubmitイベントが発火することは理解していましたが、ボタンのclickイベントの発火については完全に想定外でした。

解説

上記の挙動はHTMLの2つの仕様に起因しています。Living standardの仕様をもとに説明します。

type属性を省略したbuttonはsubmitボタンになる

The type attribute controls the behavior of the button when it is activated.
(中略...)
The missing value default and invalid value default are the Submit Button state.
引用元: 4.10.6 The button element

こちらは知っている方も多いのではないかと思います。私もここについては朧げに記憶していました。つまり今回のサンプルで言えば、type属性の省略されたGreetingボタンはsubmitボタンになっています。ただ、これだけではまだclickイベントが発火する理由がわかりません。それについてはこれから述べるもう一つの仕様が関係しています。

Enterによる送信ではdefaultボタンのclickイベントが発火する

A form element's default button is the first submit button in tree order whose form owner is that form element.
If the user agent supports letting the user submit a form implicitly (for example, on some platforms hitting the "enter" key while a text control is focused implicitly submits the form), then doing so for a form, whose default button has activation behavior and is not disabled, must cause the user agent to fire a click event at that default button.
引用元: 4.10.21.2 Implicit submission

まず、前半で述べられている通り、formにはdefaultボタンという概念があります。<from>要素内でそれをform ownerとして持つsubmitボタンのうち、ツリー上で最初のボタンがdefaultボタンになります。

そして、後半で述べられている箇所が今回の肝です。<input>におけるEnterキー押下等の暗黙的なフォーム送信において、defaultボタンがdisabledでない場合、ユーザーエージェントはdefaultボタンのclickイベントを発火しなければならないとあります。

よって、今回のサンプルではGreetingボタンはこのフォームのdefaultボタンになっているので、暗黙的フォーム送信でユーザーエージェントはこのボタンのclickイベントを発火します。

まとめ

ということで、私にとって想定外だった挙動は完全に仕様どおりでした。今回の挙動はそもそもsubmitを期待していないGreetingボタンにtype='button'を定義されていないことに端を発しています。意図しない挙動を防ぐためにも、<button>のtype属性は省略しないようにしましょう。

Discussion