1
2

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

全角数字をいい感じに半角に変換する入力フォーム

Posted at

全角数字を入力したら半角数字に変換したい。
onBlurはフォーカスを外す一手間が必要になるので使いたくない。
全角文字は入力確定してから半角に変換したい。
数字以外の文字は入力されても消さない。

そんな要件に応えるフォームを試行錯誤しながら作ってみました。

macのchrome, firefox, safariで検証済みです。

↓完成したコードがこちら

// IME使用中のinput type
const IME_TYPES: string[] = [
  'insertCompositionText',
  'deleteCompositionText',
  'insertFromComposition',
  'deleteByComposition',
] as const

const handleInput: ChangeEventHandler<HTMLInputElement> = (e) => {
    // IME使用中は後続処理を行わない
    if (
      e.nativeEvent instanceof InputEvent &&
      IME_TYPES.includes(e.nativeEvent.inputType)
    )
      return

    // ここで半角変換(コピペされた時用)
    const halfWidthNum = toHalfWidthNum(e.target.value)

    e.target.value = halfWidthNum
    props.onChange?.(halfWidthNum) // 親に入力値を渡してバリデーション等を行う
}

// IMEが確定するとこれが呼ばれる(Enterキーはもちろん、マウスで確定しても呼ばれる)
const handleCompositionEnd: CompositionEventHandler<HTMLInputElement> = (
    e,
  ) => {
    const halfWidthNum = toHalfWidthNum(e.currentTarget.value) // ここで半角変換

    // ここでセットすることで入力した文字が半角になる
    e.currentTarget.value = halfWidthNum 
    props.onChange?.(halfWidthNum) // 親に入力値を渡してバリデーション等を行う
  }

<input 
    type="text" // numberは使わない
    inputMode="numeric"
    defaultValue={value}
    onInput={handleInput}
    onCompositionEnd={handleCompositionEnd}
    // value, onChangeは使わない
/>

ポイントはvalue, onChangeは使わずにdefaultValue, onInput, onCompositionEndで処理するところです。
inputにpropsをそのまま渡す場合はvalue, onChangeを渡さないように注意が必要です。


valueを入力以外で変更することがある場合はこちら

// IME使用中のinput type
const IME_TYPES: string[] = [
  'insertCompositionText',
  'deleteCompositionText',
  'insertFromComposition',
  'deleteByComposition',
] as const

const [isComposing, setIsComposing] = useState(false)

const handleInput: ChangeEventHandler<HTMLInputElement> = (e) => {
    // IME使用中は後続処理を行わない
    if (
      e.nativeEvent instanceof InputEvent &&
      IME_TYPES.includes(e.nativeEvent.inputType)
    )
      return

    // ここで半角変換(コピペされた時用)
    const halfWidthNum = toHalfWidthNum(e.target.value)

    e.target.value = halfWidthNum
    props.onChange?.(halfWidthNum) // 親に入力値を渡してバリデーション等を行う
}

// IMEが確定するとこれが呼ばれる(Enterキーはもちろん、マウスで確定しても呼ばれる)
const handleCompositionEnd: CompositionEventHandler<HTMLInputElement> = (
    e,
  ) => {
    const halfWidthNum = toHalfWidthNum(e.currentTarget.value) // ここで半角変換

    // ここでセットすることで入力した文字が半角になる
    e.currentTarget.value = halfWidthNum 
    props.onChange?.(halfWidthNum) // 親に入力値を渡してバリデーション等を行う
    setIsComposing(false)
  }

<input 
    type="text" // numberは使わない
    inputMode="numeric"
    value={isComposing ? undefined : value}
    onInput={handleInput}
    onCompositionStart={() => setIsComposing(true)}
    onCompositionEnd={handleCompositionEnd}
    // onChangeは使わない
/>

IME使用中はinput内部のvalueを使うようにすることで、確定するまで待ってから変換処理を行うことができます。
コンポーネントが制御になったり非制御になるのであまりいい方法では無いかもですが…^^;

参考:
https://qiita.com/alt_yamamoto/items/8663d047a3794dd5605e

1
2
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
1
2

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?