Vue.js: TodoMVCの例で日本語の項目が正しく入力できるようにする

本稿は、[Vue.js](https://jp.vuejs.org/)公式サイトの「[例](https://jp.vuejs.org/v2/examples/)」のページにおける「TodoMVC の例」の、日本語入力の不具合とその修正の仕方についてご説明します。

[追記: 2018/12/14]この問題は`vuejs/vue`の開発ブランチで[修正されました](https://github.com/vuejs/vue/commit/80fb6b8da144bd9c2313702e23a35d72fa620135)。つぎのリリースでは解消するはずです(なお、「[[TodoMVC Example] [enter] key event to determin transform of Japanese characters makes the item added to list](https://github.com/vuejs/jp.vuejs.org/issues/1338)」参照)。


# TodoMVC の例
Vue.jsサイトの「[TodoMVC の例](https://jp.vuejs.org/v2/examples/todomvc.html)」は、洗練されたインタフェースのTodoリストです(図001)。機能は、項目の追加と削除、未処理と処理済みの表示切り替えなど、基本的といえます。

#### 図001■TodoMVCの画面表示
![FN1707004_002.png](https://qiita-image-store.s3.amazonaws.com/0/128949/54242261-84bf-434d-2b93-af10a3ccdac2.png)

けれど、ありがちのTodoリストのようなボタンだらけの画面ではありません(図002)。削除ボタンは項目にロールオーバーすると表れ、項目の追加は[enter]キーで確定します。

#### 図002■ありがちのTodoリスト
![FN1702010_001.png](https://qiita-image-store.s3.amazonaws.com/0/128949/621952a4-ff28-1aa2-562e-dcc4ac9af81d.png)
[\>> 「Vue.js入門 + ES6」01-11](http://fumiononaka.com/Business/index.html#Vue)より


# 日本語変換確定の[enter]キーで項目が追加されてしまう
ところが、この洗練されたインタフェースが、日本語入力ではアダになります。日本語変換確定の[enter]キーで項目が追加されてしまうのです(図003)。

#### 図003■日本語変換確定の[enter]キーで追加された項目
![1812001_001.png](https://qiita-image-store.s3.amazonaws.com/0/128949/ba3b5eeb-5924-5534-71d9-2845a9cbc467.png)

はじめはChromeで、イベント`@keyup.enter`と`@keydown.enter`とでは、取得する入力値に時間差があることを見つけました。そこで、ふたつの値を比べることにより、変換の確定と[enter]キーの単独入力を切り分けたのです。けれど、Safariではふたつの値は同じでした。つまり、この案は却下ということです。


# keypressイベントを捉える
調べたところ、`@keypress`イベントは変換確定の[enter]キーは拾わないことがわかりました。つまり、イベント`@keypress.enter`で、変換の確定ではなく、単独で入力された[enter]キーが捉えられるのです。

そこで、TodoMVCの修正です。`<header>`に置かれた`<input>`要素のキーイベントを、つぎのように`@keyup.enter`から`@keypress.enter`に書き替えます。コールバックメソッド`addTodo()`は、そのままで構いません。

```html
<section class="todoapp">
  <header class="header">
    <h1>todos</h1>
    <input class="new-todo"

      @keypress.enter="addTodo">
      <!-- @keyup.enter="addTodo"> -->
  </header>

</section>
```

リスト項目を編集する処理についても、同じように書き替えます。`<input>`要素のキーイベントは、`@keyup.enter`でなく`@keypress.enter`です。こちらも、キーイベントから呼び出すメソッド`doneEdit()`は手を触れません。

```html
<section class="main" v-show="todos.length" v-cloak>

  <ul class="todo-list">
    <li v-for="todo in filteredTodos"

      >

      <input class="edit" type="text"

        @keypress.enter="doneEdit(todo)"
        @keyup.esc="cancelEdit(todo)">
        <!-- @keyup.enter="doneEdit(todo)" -->
    </li>
  </ul>
</section>
```

これらの修正が加えられたTodoMVCのサンプルを「[Vue.js: TodoMVC revised](https://jsfiddle.net/FumioNonaka/38uvnawh/)」としてFiddleに掲げます。また、Qiitaに埋め込めるよう、同じコードをCodePenにコピーしてつぎのサンプル001に掲げました。

#### サンプル001■ Vue.js: TodoMVC revised
<p data-height="265" data-theme-id="0" data-slug-hash="QzLowZ" data-default-tab="js,result" data-user="FumioNonaka" data-pen-title="Vue.js: TodoMVC revised" class="codepen">See the Pen <a href="https://codepen.io/FumioNonaka/pen/QzLowZ/">Vue.js: TodoMVC revised</a> by Fumio Nonaka (<a href="https://codepen.io/FumioNonaka">@FumioNonaka</a>) on <a href="https://codepen.io">CodePen</a>.</p>
<script async src="https://static.codepen.io/assets/embed/ei.js"></script>


# 標準JavaScriptのキーイベント
標準のJavaScriptでは、イベント`keydown`は`keypress`と同じように、日本語の変換を確定したときの[enter]は拾わないようです。たとえば、つぎのように入力して変換していったとします([確定]は[enter]キーによる変換の決定です)。

> 日本語の[確定]項目を[確定]入力[確定]する[確定][enter]

すると、捕らえられた[enter]キーイベントとそのときの入力値はつぎのとおりです。以下のサンプル002で実際に確かめられます(enterは[enter]キーの単独入力)。

```
keyup: 日本語の
keyup: 日本語の項目を
keyup: 日本語の項目を入力
keyup: 日本語の項目を入力する
keydown: 日本語の項目を入力する
keypress: 日本語の項目を入力する
enter: 日本語の項目を入力する
keyup:
```

#### サンプル002■テキストの入力について[enter]キーのイベントを確かめる
<p data-height="265" data-theme-id="0" data-slug-hash="vvBJRw" data-default-tab="js,result" data-user="FumioNonaka" data-pen-title="Testing key events for [enter] when inputting Japanese characters" class="codepen">See the Pen <a href="https://codepen.io/FumioNonaka/pen/vvBJRw/">Testing key events for [enter] when inputting Japanese characters</a> by Fumio Nonaka (<a href="https://codepen.io/FumioNonaka">@FumioNonaka</a>) on <a href="https://codepen.io">CodePen</a>.</p>
<script async src="https://static.codepen.io/assets/embed/ei.js"></script>

Vue.jsもv2.3.4では、`keydown`イベントは日本語変換を確定する[enter]キーは拾いませんでした(「Vue.js: TodoMVCをつくる 01 ー 項目の追加と削除および残り項目数表示」[注[*1]](http://fumiononaka.com/Business/html5/FN1706008.html#note01)参照)。この動きが、v2.5.14で仕様変更されたようです(GitHubのjp.vuejs.org[[TodoMVC Example] [enter] key event to determin transform of Japanese characters makes the item added to list](https://github.com/vuejs/jp.vuejs.org/issues/1338)参照)。