はじめに

Vue.js でキーボード操作で要素間を移動させる方法について整理した。

イメージとしてはツリービューみたいなやつとかを、 キーで移動するような感じ。

TL;DR

  • tabindex を移動させたい要素に設定
  • @keyup.up.exact, @keyup.down.exact でイベントハンドラを設定
  • ハンドラ内で次要素の tabindex を算出して focus
この記事が参考になった方
ここここからチャージや購入してくれると嬉しいです(ブログ主へのプレゼントではなく、ご自身へのチャージ)
欲しいもの / Wish list

目次

  1. はじめに
  2. TL;DR
  3. 環境・条件
  4. 詳細
    1. 前置き
      1. HTML で要素間の移動
      2. Vue.js でキーボード操作のハンドリング
    2. 本題
  5. まとめ
  6. 参考文献

環境・条件

1
2
3
4
5
6
7
8
9
10
11
$ cat /etc/os-release
PRETTY_NAME="Debian GNU/Linux 10 (buster)"

$ node -v
v12.16.1

$ npm -v
6.13.4

$ npm ls vue
vue@2.6.11 | MIT | deps: none | versions: 263
  • Google Chrome バージョン: 80.0.3987.149(Official Build) (64 ビット)

詳細

前置き

HTML で要素間の移動

まず前提として、HTML で input 以外の要素を TAB キーで移動可能にさせるには tabindex を設定する。

1
2
<div tabindex="0">foo</div>
<div tabindex="1">bar</div>

See the Pen tabindex by 17num (@17num) on CodePen.

Vue.js でキーボード操作のハンドリング

参考: キー修飾子 - イベントハンドリング

Vue.js でキーボード操作をハンドリングするには keyup, keydown, keypress を利用する。

input にキーボード操作でのイベントハンドラを設定する場合は以下のイメージ。

1
2
3
4
5
<input type="text"
@keyup="onKeyup"
@keydown="onKeydown"
@keypress="onKeypress"
>

それぞれのイベントのざっくりとした違い。(自分調べなので細かい誤りはあるかも)

  • keyup: キーが離された時に発火
  • keydown: キーが押されている間 発火し続ける。日本語入力の時も発火する
  • keypress: キーが押されている間 発火し続ける。日本語入力の時は発火しない

また、enterup などでキーの指定が可能。

参考: キーコード

1
2
3
4
5
<input type="text"
@keyup.enter="onKeyup"
@keydown.tab="onKeydown"
@keypress.up="onKeypress"
>

Shift や Ctrl キーなども shift, ctrl などの修飾子で指定可能。

参考: システム修飾キー

1
2
3
4
5
<input type="text"
@keyup.shift.enter="onKeyup"
@keydown.ctrl.tab="onKeydown"
@keypress.alt.up="onKeypress"
>

exact を付けることで、Shift + Enter のみに限定、などの厳密な指定も可能。
※つけていないと、Shift + Ctrl + Enter, Shift + Alt + Enter なども対象になる。

1
2
3
4
5
<input type="text"
@keyup.shift.enter.exact="onKeyup"
@keydown.ctrl.tab.exact="onKeydown"
@keypress.alt.up.exact="onKeypress"
>

本題

前置きの内容を組み合わせることで、矢印キーによる移動を簡単に実現可能。

完成イメージ。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
<template>
<div>
<div tabindex="0"
@keyup.up.exact="up"
@keyup.down.exact="down"
>
Foo
</div>
<div tabindex="1"
@keyup.up.exact="up"
@keyup.down.exact="down"
>
Bar
</div>
</div>
</template>

<script>
export default {
methods: {
down(event) {
const tabindex = Number(event.target.getAttribute('tabindex'));
const nextTabindex = Math.min(1, tabindex + 1);
document.querySelector(`[tabindex="${nextTabindex}"]`).focus();
},
up(event) {
const tabindex = Number(event.target.getAttribute('tabindex'));
const nextTabindex = Math.max(0, tabindex - 1);
document.querySelector(`[tabindex="${nextTabindex}"]`).focus();
},
},
};
</script>

実際には tabindex の割当を良い感じに計算させたりとか、現在の最大の tabindex を取得(※)したりとか、細かい考慮は必要。
※これは data で持てば良いだけかも

あと、場合によっては以下あたりも考慮した方が良い。

  • click も実行するべきか
  • keyup ではなくて keydown にする
    • keydown にするなら移動速度(初速・加速度・最大速度)の考慮

まとめ

  • tabindex を移動させたい要素に設定
  • @keyup.up.exact, @keyup.down.exact でイベントハンドラを設定
  • ハンドラ内で次要素の tabindex を算出して focus

参考文献

関連記事

この記事が参考になった方
ここここからチャージや購入してくれると嬉しいです(ブログ主へのプレゼントではなく、ご自身へのチャージ)
欲しいもの / Wish list