Capture keypress for all keys

I am building a typing practice app and am having some trouble capturing key presses.

I found this post: Basic keyboard events not working and was able to start capturing all the keypresses with this code:

  mounted() {
    window.addEventListener("keypress", function(e) {
      console.log(String.fromCharCode(e.keyCode));
    });
  }

However then I have no way to notify the Vue instance of the key press, because this is bound to window in the above snippet. I have considered using a textarea and that works, but I don’t like some aspects of the way it works.

This is what my main js file looks like:

import Vue from 'vue'
import App from './app.vue'

document.addEventListener('DOMContentLoaded', () => {
  const app = new Vue(App).$mount('hello')
})

Here is part of app.vue

<template>
  ...
</template>

<script>
export default {
  name: 'example',
  data () {
    return {
      percentComplete: 0
    }
  },
  methods: {
    ...
  },
  computed: {
    ...
  },
  mounted() {
    window.addEventListener("keypress", function(e) {
      console.log(String.fromCharCode(e.keyCode));
    });
  }
}
</script>

<style scoped>
  ...
</style>

How do I capture all of the keypress events and notify vue of them?

Solution A: b.bind()

 mounted() {
    window.addEventListener("keypress", function(e) {
      console.log(String.fromCharCode(e.keyCode));
    }.bind(this));
  }

Solution B: a closure

 mounted() {
    const self = this
    window.addEventListener("keypress", function(e) {
      // use self instead of this in here
      console.log(String.fromCharCode(e.keyCode));
    });
  }

Solution C: Arrow function

 mounted() {
    window.addEventListener("keypress", e => {
      console.log(String.fromCharCode(e.keyCode));
    });
  }

This is not a Vue-specific problem - understanding how this works in Javascript is fundamental, you should read up on it.

6 Likes

@nate_w Here’s a solid article on this should you care for some reading https://codeburst.io/javascript-the-keyword-this-for-beginners-fb5238d99f85

2 Likes

Thanks a bunch!

It works perfect now.

Thanks for the article! It looks like a good resource.

Query - how would you handle removing the listener on destroyed()? I’m having an issue now where I use this on one view in an app and when I navigate back to it it is registered again.

I ended up with this hack:

if(!window.moverAdded) {
	console.log('ADDING EVENT HANDLER');
	window.addEventListener("keypress", e => {
		let typed = String.fromCharCode(e.keyCode);
		this.doCommand(typed);
		window.moverAdded = 1;
	});
}

I don’t like polluting the window space but it worked.

set the handler function as a method of your component, then pass that method to both the add- and removeEventListener() calls

And shoot… that ends up being a problem. The this scope in doCommand disconnects from the page’s data value. :\ Any ideas? This is what I’ve got so far: https://github.com/cfjedimaster/vue-demos/blob/master/taipan/src/views/Game.vue

Linus, I just saw your reply as I posted my other reply - I apologize. I’ll get back to you.

Thank you, thank you, thank you. I’m going to write this up as a blog, and credit you of course. I really appreciate it. (FYI depending on when you read this the fix may not be in GitHub yet.)