Vue.jsで日本語入力中に未確定状態でinput boxにmaxlengthを超えた部分が表示されないようにするには

# インプットボックスの文字数制限

最近、インプットボックスの文字数制限を実装する必要があり、日本語入力中の挙動で結構はまりました。とある方のアドバイスで解決したので、備忘録として残しておこうと思います。アドバイスしてくださった方、ありがとうございました。なお、今回はVue.jsでの実装で直面した問題でしたが、Vue.js以外でも(JavaScriptなら)同様のことが起こりうると思います。(より良い実装の情報があれば、ぜひ教えてください。)

## シンプルに`maxlength`を指定した実装

以下ソースコードで問題ないと思いきや...

### ソースコード

```html:html
<div id=app>
  <input type="text" v-model="test" maxlength="5">
  {{test}}
</div>
```

```js:Vue.js
const app = new Vue ({
  el: '#app',
  data: {
    test:''
  }
})
```

もし、挙動を試してみたい方は、以下codepenからお試しください。

<p class="codepen" data-height="191" data-theme-id="light" data-default-tab="html,result" data-user="YasunoriMATSUOKA" data-slug-hash="LYVxxXV" style="height: 191px; box-sizing: border-box; display: flex; align-items: center; justify-content: center; border: 2px solid; margin: 1em 0; padding: 1em;" data-pen-title="This form can't restrict max length on Japanese language input">
  <span>See the Pen <a href="https://codepen.io/YasunoriMATSUOKA/pen/LYVxxXV">
  This form can't restrict max length on Japanese language input</a> by Yasunori MATSUOKA (<a href="https://codepen.io/YasunoriMATSUOKA">@YasunoriMATSUOKA</a>)
  on <a href="https://codepen.io">CodePen</a>.</span>
</p>
<script async src="https://static.codepen.io/assets/embed/ei.js"></script>

### 微妙な状況に

しかし、この実装では日本語入力中に未確定状態では最大文字数の`maxlength`を超えた部分についてもインプットボックスに表示されてしまうという状況に悩まされていました。(`Enter`押す等して確定すれば、`maxlength`以降の文字は消えるのですが、その前の未変換の状態や変換中は`maxlength`以上の文字が入力できているような印象をユーザーに与えてしまうという点で微妙な状況でした。)

## 解決策

### inputイベント時に`event.target.value`の長さをチェックし、`maxlength`を超えている場合はinput boxからフォーカスを外す(`event.target.blur()`する)

### 解決策のソースコード

```html:html
<div id=app>
  <input type="text" v-model="test" maxlength="5" @input="onInput">
  {{test}}
</div>
```

```js:Vue.js
const app = new Vue ({
  el: '#app',
  data: {
    test:''
  },
  methods: {
    onInput (event) {
      console.log('onInput', event)
      if (event.target.value.length > event.target.getAttribute("maxlength")) {
        event.target.blur()
      }
    }
  }
})
```

無理矢理感は否めませんが、これで、`maxlength`を超えた際は、自動的に対象のinput boxからフォーカスが外れ、日本語入力が強制的に確定され、`maxlength`以降の仮入力中の文字がinput boxに表示されることが無くなりました。

試してみたい方は、以下のcodepenでお試しください。

<p class="codepen" data-height="205" data-theme-id="light" data-default-tab="html,result" data-user="YasunoriMATSUOKA" data-slug-hash="qBdRqwL" style="height: 205px; box-sizing: border-box; display: flex; align-items: center; justify-content: center; border: 2px solid; margin: 1em 0; padding: 1em;" data-pen-title="Restrict max length on Japanese language input">
  <span>See the Pen <a href="https://codepen.io/YasunoriMATSUOKA/pen/qBdRqwL">
  Restrict max length on Japanese language input</a> by Yasunori MATSUOKA (<a href="https://codepen.io/YasunoriMATSUOKA">@YasunoriMATSUOKA</a>)
  on <a href="https://codepen.io">CodePen</a>.</span>
</p>
<script async src="https://static.codepen.io/assets/embed/ei.js"></script>

## 試したがうまくいかなかったもの

### `keydown`イベント時に`event.target.value`の長さをチェックし、`maxlength`を超えている場合はイベントを無効化する

### うまくいかなかった実装のソースコード

```html:html
<div id=app>
  <input type="text" v-model="test" maxlength="5" @keydown="onKeyDown">
  {{test}}
</div>
```

```js:VUe.js
const app = new Vue ({
  el: '#app',
  data: {
    test:''
  },
  methods: {
    onKeyDown (event) {
      console.log('onKeyDownEvent', event)
      if (event.target.value.length >= 5) {
        if (event.code !== "Backspace" && event.code !== "Delete" && event.code !== "ArrowRight" && event.code !== "ArrowLeft" && event.code !== "ArrowUp" && event.code !== "ArrowDown" && event.code !== "PageUp" && event.code !== "PageDown" && event.code !== "Enter" && event.code !== "Space" && event.code !== "Tab") {
          event.preventDefault()
          console.log('preventDefault', event)
        }
      }
    }
  }
})
```

IME ONの状態での入力中に、入力をJavaScriptで制御するのは難しいようで、半角英数ならば意図した通に動くものの、日本語入力中には意図した通り動作させることができませんでした。
試してみたい方は、以下codepen上でお試しください。

<p class="codepen" data-height="557" data-theme-id="light" data-default-tab="js,result" data-user="YasunoriMATSUOKA" data-slug-hash="NWqdPJZ" style="height: 557px; box-sizing: border-box; display: flex; align-items: center; justify-content: center; border: 2px solid; margin: 1em 0; padding: 1em;" data-pen-title="Try to apply max length restriction on input Japanese language (don't work well)">
  <span>See the Pen <a href="https://codepen.io/YasunoriMATSUOKA/pen/NWqdPJZ">
  Try to apply max length restriction on input Japanese language (don't work well)</a> by Yasunori MATSUOKA (<a href="https://codepen.io/YasunoriMATSUOKA">@YasunoriMATSUOKA</a>)
  on <a href="https://codepen.io">CodePen</a>.</span>
</p>
<script async src="https://static.codepen.io/assets/embed/ei.js"></script>

## まとめ

### 半角英数で完結する言語うらやましい...日本語入力等のIME ONにする必要がある言語はこういうとき大変...