Web Developer's Struggle Memories

日々の業務から思ったこと、学んだことを書き連ねていきます。

input 要素で日本語入力が完了したのか submit のエンターなのかを判定する

軽くググるとすでに数多くの記事が書かれており、たくさんの先人の方々が苦しんだことが分かりますが、古かったり超厳密に実装しようとして少々複雑度が増しているものだったりしますので、完璧ではないですが妥協できるくらいのレベルでの実装で良い、という人のために書きます。(おそらく未来の自分になるかもw)

実装方針

基本的には KeyboradEvent オブジェクトの key プロパティや keyCode プロパティを見て判定する形です。詳しくは、MDN の記事が参考になりますのでご参照ください。

developer.mozilla.org

※余談ですが、MDN によると keyCode プロパティは Deprecatedcode プロパティを使えとのことだそうですが、様々なサービスやブラウザではまだ使えない*1ので、本記事では keyCode プロパティを利用します。

完成品のデモ

まずは完成品のデモのリンクを載せておきます。

codesandbox.io

アクセスされましたら、画面右ペイン下の console をクリックして表示してください。その後は、日本語でパタパタ入力して完了したり、アルファベット入力のままでエンターキーを押下してみたりしてください。

実際のコード

今回は styled-components という便利なライブラリ*2を利用して、入力欄(input 要素)を作っていますのでそのコードを先に記載します。

import React from "react";
import styled from "styled-components";

const Input = styled.input.attrs({ type: "text" })`
  border-color: ${props => (props.error ? "#ff0000" : "skyblue")};
  width: 200px;
  border-radius: 8px;
  padding: 5px;
  line-height: 1.5;
  font-size: 1rem;
`;

const TextField = ({
  value,
  placeholder,
  onChange,
  onKeyUp,
  onKeyDown
}) => (
  <Input
    value={value}
    placeholder={placeholder}
    onChange={e => onChange && onChange(e.target.value)}
    onKeyUp={e => onKeyUp && onKeyUp(e)}
    onKeyDown={e => onKeyDown && onKeyDown(e)}
  />
);

export default TextField;

特に説明することもないですw

では、今回コアとなるコンポーネントのコードを紹介します。

import React, { useState } from "react";
import TextField from "./TextField";

const handleKeyUp = e => {
  // ※ここと
  if (e.key === "Enter" && e.keyCode === 13) {
    console.log("KeyUp: Japanese input!!");
  }
};

const handleKeyDown = e => {
  // ※ここがキーポイント
  if (e.key === "Enter" && e.keyCode === 13) {
    console.log("KeyDown: Japanese input!!");
  }
};

const SearchField = ({ text, placeholder }) => {
  const [inputText, setInputText] = useState(text);

  const checkAndSetText = newText => {
    if (newText.length > 10) return;

    setInputText(newText);
    localStorage.setItem("text", newText);
  };

  return (
    <>
      <TextField
        value={inputText}
        placeholder={placeholder}
        onChange={checkAndSetText}
        onKeyUp={handleKeyUp}
        onKeyDown={handleKeyDown}
      />
      <p>variable text: {inputText}</p>
    </>
  );
};

export default SearchField;

解説

上記コードにもコメントしていますが、今回の肝は if (e.key === "Enter" && e.keyCode === 13) の判定条件です。更に言うと、keyDown イベントを使う事 です。当初は keyUp イベントを使っていたのですが、日本語入力には使えませんでした。

以下、日本語完了と submit の Enterキーを押下したときの比較 をしていきます。

keyUp 日本語 submit
key 'Enter' 'Enter'
keyCode 13 13
keyDown 日本語 submit
key 'Enter' 'Enter'
keyCode 229 13

上記のように、keyUp イベントでは Enter キーを押下した時、それが日本語入力の完了なのか submit するときのものなのか判定ができませんでした。よって、日本語の一単語を入力し終わって次の単語を入力しようとした時、すでに submit が走ってしまい入力ができない状況になっていました。

したがって、keyDown イベントを用いることにしました。ちなみに日本語入力の場合は、常に keyCode229 になっているので、そこは注意が必要です。

ではでは(=゚ω゚)ノ

*1:執筆現時点での chrome でもだめでした

*2:筆者は CSS in JS が好きなので、このライブラリはかなり気にいっていますw