◆ form タグにまかせないでそのまま input などを並べて js で処理するほうがやっぱりいいと思った
◆ IE なんてゴミ箱に捨てれば良いんだ

あまり サーバサイドにデータ送るようなもの作らないし 作っても送る前に加工することが多いので 普段から form タグをつかうことはほとんどないのですが使ってみると以外に困るのがエンターでサブミットされる機能です

input ひとつとボタンひとつの作りなら良い機能なのですけど複数入力のときに途中でサブミットされたりすると困ります

サブミットする前に入力チェック (checkValidity) をするべきで してるなら未入力だとサブミットされないから大丈夫
という考えかたもありますが 未入力おっけいだったり エンター押しただけで「ここエラーです」がいっぱいでてくるのも邪魔です

よくある問題だしそろそろ form 自体に preventEnterSubmit とかプロパティができてもいいと思うのですがないようです

単純に form が onkeydown をチェックして Enter なら preventDefault と すると textarea で改行できなくなったり他のところに影響でます

タグの種類と type 属性をみて エンターでサブミットするかを調べて さらにそのイベント元が submit のボタンじゃないときは preventDefault というのはちょっと大変です

案1

onsubmit で event.target が submit タイプ以外なら preventDefault ならシンプルかも

と思ったのですがやってみると onsubmit イベントは絶対 target が form でクリックしたボタンやエンターキーを押したなどの情報はとれないようでした

案2

試してると input のエンターによるサブミットは内部的に type="submit" のボタンがクリックされたことになるようで onclick イベントが発火していました
クリックしていないのに click イベントが起きるということはプロパティから本当のクリックかどうかを判断できるはず
と言う考えでやってみたのがこれ
var form = document.querySelector("form")

form.onclick = eve => {
if(eve.target.type === "submit"
&& eve.x === 0
&& eve.y === 0
&& document.activeElement !== eve.target){
eve.preventDefault()
}
}

関係ないのは弾くように type === "submit" は必須
クリックしてないときのクリックイベントは eve.x と eve.y が 0 になってるので これでエンターキーでのサブミットかの判断をします
最後に <input type="submit"> にフォーカスしてエンターしたときはサブミットしたいので アクティブな要素かもチェックします

アクティブな要素(フォーカス中)とクリック対象が違っていて 実際にはクリックしていないクリックイベントで イベントの対象が submit と言う状態をエンターキーのサブミットとみなしてイベントをキャンセルしています


これで簡単にエンターサブミットを防止できます

といいたいのですが IE11 にも対応する必要がある場合は使えません
なんでこんなぽんこつが2025年までサポートなんでしょうね

IE では

IE でも submit ボタンが click されたことになります
ですが 実際にクリックしていないクリックイベントで event.x と event.y が 0 になりません
-574 など負の数です

数値もウィンドウサイズでかわるようで固定ではありません
条件を === 0 から <= 0 にすればとりあえず解決できそう ではあります
本当に大丈夫か心配がありますけど


他に実際のクリックとの判断できそうなものに event.detail があります
これはクリックした数が入るので click イベントだと 1 ダブルクリックだと 2 クリックしてないと 0 になります

なのに IE11 は event.detail プロパティは一応存在するのにクリックしてもずっと 0 でした


あまりすっきりしないけど event.x と event.y が 0 以下なら……と諦めたところで screenX/Y が 0 になってるのを発見しました
var form = document.querySelector("form")

form.onclick = function(eve) {
if(eve.target.type === "submit"
&& eve.screenX === 0
&& eve.screenY === 0
&& document.activeElement !== eve.target){
eve.preventDefault()
}
}

これで完成


と思ったらまだありました

input をエンターしたときの click イベントで activeElement が input でなく button になっていました
css セレクタで :focus にしても同じで button になっています

これを対応するのは簡単には行かなそうなので IE 対応が必要なら最後の条件なしにして 代わりに type="submit" にフォーカスしてエンターすると submit メソッドを呼びます
var form = document.querySelector("form")

form.onclick = function(eve) {
if(eve.target.type === "submit"
&& eve.screenX === 0
&& eve.screenY === 0){
eve.preventDefault()
}
}

form.onkeydown = function(eve) {
if(eve.keyCode === 13 && eve.target.type === "submit"){
eve.preventDefault()
this.submit()
}
}

これで今度こそ大丈夫なはずといいですが IE11 にはやっぱリ問題があって input をエンターで確定すると見た目はどこにもフォーカスしていないように見えるのに 内部的に button がフォーカスされています
onclick の時点で activeElement が button になっていましたが一時的にではなくてそのままフォーカスが移ってるようです

この状態でエンターすればやっぱりサブミットされるので 2 回エンターすればサブミットされることになります


サブミットボタンのエンターはなしでボタンクリックのみでいいかなー
とりあえず追加した onkeydown を消して これで完成としたかったのですが 別の問題に気付いてしまいました

ボタンにフォーカスあててスペースキーを押したときに発生する click イベントでは screenX/Y が 0 でなく 400 などの正の数字でした




もう IE なんて知らない