Vue.jsの画面全体でキー入力イベントを拾うには?【サンプルコード付き】

<input> タグに頼らずキー入力を取得したい!

みなさん、こんにちは。どんぶラッコです。

先日、Twitterでふとこんな投稿を目にしました。

自分がVue.jsを触っていることもあり、やり取りをさせていただきました。

話していくと、どうやら「inputではなく、画面全体のキー入力を取得したい!」ということのようでした。

Vue.jsには v-on という機能が備わっています。例えば、DOMに対して v-on:keydown と指定してあげるとキー入力のイベントを拾うことができます。

こういうことですね。

HTML
<div id="app">
  <input 
    autofocus
    placeholder="文字を入力"
    type="text"
    @keydown="onKeyDown"
  >
  <div>
    <p>key: {{key}}</p>
    <p>keyCode: {{keyCode}}</p>
  </div>
</div>
<script src="https://unpkg.com/vue@next"></script>

JavaScript
const App = {
  data() {
    return {
      key: '',
      keyCode: null
    }
  },
  methods: {
    onKeyDown(event) {
      this.key = event.key
      this.keyCode = event.keyCode
    }
  }
}

Vue.createApp(App).mount('#app')

v-on と @

@v-on のエイリアスです。

なので、 @keydownv-on:keydown と同義です。

See the Pen vuejs キーコード取得 by cha1ra (@cha1ra) on CodePen.

しかし、これでは input タグにフォーカスが当たっているときにしか keydown イベントが発火しません。

どうしたらいいのでしょうか?

mounted() の中に Pure JSで記述

この場合Vue.jsの機能に頼るのではなく、PureJS (生のJavaScript) で記述するしかないと思っています。

私の場合、 mounted() の中に記述しています。

またサンプルコードを示しますね!

HTML
<div id="app">
  <div>
    <p>key: {{key}}</p>
    <p>keyCode: {{keyCode}}</p>
  </div>
</div>
<script src="https://unpkg.com/vue@next"></script>

JavaScript
const App = {
  data() {
    return {
      key: '',
      keyCode: null
    }
  },
  mounted() {
    document.addEventListener('keydown', this.onKeyDown)
  },
  beforeDestroy() {
    document.removeEventListener('keydown', this.onKeyDown)
  },
  methods: {
    onKeyDown(event) {
      this.key = event.key
      this.keyCode = event.keyCode
    }
  },

}

Vue.createApp(App).mount('#app')

ポイントは JavaScript側の mounted() メソッドの中身です。

mounted()
document.addEventListener('keydown', this.onKeyDown)

この部分でイベントリスナーを指定しています。

イベントリスナーの設定方法については公式サイトをご確認ください。

この書き方でいいのかな…?

とはいえ、この書き方でいいのでしょうか…?Vue.jsのフレームワークの中にPureJSがガッツリ登場するのってありなんだろうか?

不安になったので、Vue.js の日本コミュニティに上記の書き方でいいと思うか、アドバイスを求めてみました。

すると、下記2つのフィードバックをいただきました。

いただいたフィードバック
  • 書き方はそれで問題ないと思う
  • グローバルなオブジェクトに対して addEventListener するなら removeEventListener についても記述するべき

書き方はそれで大丈夫

コメントを引用させていただきます。

個人的にはそれで問題ないと思いますよ。Vueはコンポーネントをうまく扱う、それについて v-on が簡単に使えるのがメリットで、Vueが関与しない部分(ページ全体)について無理にVueを使う必要はないと思います。ただ、やるとすれば一番RootのコンポーネントでListenする、というのはありかもです。

VueはあくまでもJSをうまく使うためのツールを提供しているのであって、そのツールで手が回らないところについてはPureJS書いちゃってもいいじゃない!という意見です。ごもっとも…!

最後に記載していただいている Rootのコンポーネントで... というのは、

<App @keydown="...">
  ...
</App>

というように記述することを指しています。

removeEventListener() もあったほうがいいよ!

また、このようなご意見もいただきました。

ちなみに、pure JSでdocumentやwindowなどグローバルなオブジェクトに対してaddEventListenerする場合は、beforeDestroyなどでremoveEventListenerしておいた方が安全です。

一番最初サンプルを提示したときに removeEventListener していませんでした。これを書かないと、リスナーがずっと残り続けてしまう可能性があります。

上記の指摘を受け、 beforeDestroy() の記述を追加しています。

JavaScript 内部の beforeDestroy()
  beforeDestroy() {
    document.removeEventListener('keydown', this.onKeyDown)
  },

removeEventListener の使い方は

対象.reomoveEventListener('イベントの種類', イベントリスナー関数)

のように、addEventListner で指定した関数をもう一回指定しなくてはなりません。今回で言うと this.onKeyDown ですね。

なので、 removeEventListener のことも考慮するとイベントリスナー関数は methods() として定義しておくのが良いでしょう。

この記事のまとめ

というわけでまとめると、PureJSを併用するのはOK、ただしその場合はイベントリスナーの削除を忘れない!ということがポイントになりそうですね!

今回のコードサンプルは Codepenに置いてあります。活用してください♪

See the Pen vuejs キーコード取得(document) by cha1ra (@cha1ra) on CodePen.

コメントを残す

メールアドレスが公開されることはありません。 * が付いている欄は必須項目です

CAPTCHA