In my case, I created a directive, v-esc.ts. (※ This is Vue3 directive writing way)
import { Directive } from 'vue'
const directive: Directive = {
beforeMount(el, binding) {
el._keydownCallback = (event) => {
if (event.key === 'Escape') {
binding.value()
}
}
document.addEventListener('keydown', el._keydownCallback)
},
unmounted(el, binding) {
document.removeEventListener('keydown', el._keydownCallback)
delete el._keydownCallback
}
}
export const esc = { esc: directive }
Then I can use it in any component like this.
(NOTE: you must pass a function param to v-esc, because the param executed as binding.value() in the directive)
<template>
<img
@click.prevent="close"
v-esc="close"
src="@/assets/icons/close.svg"
/>
</template>
<script lang="ts">
import { defineComponent } from 'vue'
import { esc } from '@/common/directives/v-esc'
export default defineComponent({
name: 'nitCloseButton',
...
methods: {
close() {
this.$emit('close')
}
},
directives: {
...esc
}
})
</script>
P.S
One month after, I also need arrow left and arrow right keys.
So, I've made this directive more general like this.
import { Directive } from 'vue'
const directive: Directive = {
beforeMount(el, binding) {
el._keydownCallback = event => {
console.log('keydown', event.key)
if (event.key === binding.arg) {
binding.value()
}
}
document.addEventListener('keydown', el._keydownCallback)
},
unmounted(el, binding) {
document.removeEventListener('keydown', el._keydownCallback)
delete el._keydownCallback
}
}
export const keydown = { keydown: directive }
You can detect any key's keydown by passing keyname as binding.args
(v-keydown:{keyName} like below)
<button
v-keydown:ArrowLeft="moveToPreviousPage"
class="controller-button lo-center"
@click="moveToPreviousPage"
>
<arrow-icon :rotation="180" />
</button>
<button
v-keydown:ArrowRight="moveToNextPage"
class="controller-button lo-center"
@click="moveToNextPage"
export default defineComponent({
name: 'componentName',
directives: {
...keydown
}
...
})