uga.dev - A Front-end Engineer's shed

現在のテーマは「ライトモード」です。

IME操作中のEnterキーでフォームが誤送信される問題を検出するESLintプラグインを作った話

公開日:

  • #JavaScript
  • #ESLint
  • #IME
  • #アクセシビリティ

日本語などのIME(Input Method Editor)を利用するユーザが、たびたび遭遇する不具合があります。まだ入力中なのに送信されてしまうというアレです。

この件について大変わかりやすいスライドで問題提起をしている、かみくずさんのツイートを拝見しまして、ESLintのプラグイン eslint-plugin-ime-safe-form を用意しました。

問題となる実装の具体例

keydownイベントでEnterキーが押された時にフォームを送信するような実装です[1]

IME変換中もフォームが送信されてしまう例
inputElement.addEventListener('keydown', (event) => {
  if (event.key === 'Enter') {
    submit();
  }
});

IMEユーザは変換を確定するためにもEnterキーを押します。「Enterキーが押された時」だけでハンドリングしてしまうと、変換を確定しようとしただけなのにフォームが送信されてしまうわけです。

この問題は日本語利用者だけでなく、中国語・韓国語・ベトナム語・タイ語などIMEを使う数十億人のユーザに影響する問題です。

正しい実装

筆者個人としては、form要素のsubmitイベントを使う方法を推奨しています。こうしておけばそもそもIMEのサポートを考えなくてよくなります。

submitイベントをハンドリングする例
formElement.addEventListener('submit', (event) => {
  event.preventDefault();
  submit();
});

一方で、送信する以外の目的でkeydownイベントによって何かを実行する場合には、KeyboardEvent.isComposingを確認する必要があります。

IME変換中をサポートした例
inputElement.addEventListener('keydown', (event) => {
  if (event.isComposing || event.keyCode === 229) {
    return;
  }

  if (event.key === 'Enter') {
    submit();
  }
});

なお、event.keyCode === 229はSafariのバグを回避するためのものです[2]

ESLintプラグイン

普段IMEを利用しているなら別ですが、英語圏などの非IMEユーザがこの問題に気づくのは難しいでしょう。というわけでESLintプラグインを用意しました。

SHのサンプルコード
npm install --save-dev eslint-plugin-ime-safe-form
eslint.config.js
import imeSafeForm from "eslint-plugin-ime-safe-form";

export default [
  {
    plugins: {
      "ime-safe-form": imeSafeForm,
    },
    rules: {
      "ime-safe-form/no-enter-keydown-without-composition-check": "error",
    },
  },
];

設定すると、vanilla/JSXともにisComposing未使用を検出できます。

検出される例
inputElement.addEventListener('keydown', (event) => {
  if (event.key === 'Enter') submit();
});

<input onKeyDown={(event) => {
  if (event.key === 'Enter') submit();
}} />
適合例
inputElement.addEventListener('keydown', (event) => {
  if (event.isComposing || event.keyCode === 229) return;
  if (event.key === 'Enter') submit();
});

<input onKeyDown={(event) => {
  if (event.isComposing || event.keyCode === 229) return;
  if (event.key === 'Enter') submit();
}} />

終わりに

eslint-plugin-ime-safe-formを導入することで、isComposingのチェック漏れを検出できるようになります。

すでにIssueを立てていただいておりますが、今後はTab・Escapeへの対応なども追加していけたらと思います。

まだ改善の余地はありますが、IMEユーザの体験改善につながる議論が進んでいけば嬉しいです。

また、Biomeにも同様のルールが提案されており、そこでこのプラグインを根拠に添えていただきました。

無事に組み込まれることで、IMEユーザの困った!が少しでも減ることに期待したいです。

脚注

  • [^1]:keydownのみならず、keypress(非推奨)やkeyupなども同様の問題が発生する。
  • [^2]:古いSafariではcompositionendイベントがkeydownより先に発火してしまう。IME変換中はkeyCodeが常に229になるという仕様を利用することで、Safariでも正しく判定できる。参考:Element: keydown event - MDN