How do I handle the "keydown.esc" event in Vue3?

Viewed 4158

I have a <Parent> component that renders the <Modal> component if a variable showModal is true.

The variable showModal is defined in the setup(){} function of the <Parent> component.

I want to close the modal when a user:

  1. clicks the close button
  2. when they hit the ESC button

Parent component

<template>
  <Modal v-if="isShownModal" @clickedModalClose="isShownModal=false" @keydown.esc="isShownModal=false">
  //removed some code for clarity purposes
</template>

<script>
export default {
  components: {Modal},
  setup(){

    const isShownModal = ref(false)
    ...

Notice the @clickedModalClose="isShownModal=false" and @keydown.esc="isShownModal=false"

Modal component

<template>

  <div class="modal is-active">
    <div class="modal-background"></div>  
    
    <div class="modal-content box">
      <slot name="header"></slot>
      <slot name="body"></slot>  
    </div>
  
    <!-- Listen to a custom event @clicked-modal-close (v-on) --> 
    <button @click="$emit('clickedModalClose')" class="modal-close is-large" aria-label="close"></button>
  </div>
  
</template>

To close the modal I'm emitting an event when a close button is clicked within the child component and listening for that event @clickedModalClose and as expected that closes the modal correctly

Nonetheless, when I hit the ESC button nothing happens even though I have the event handler declared on the Modal component: <Modal v-if="isShownModal" @clickedModalClose="isShownModal=false" @keydown.esc="isShownModal=false">

I also tried attaching the @keydown.esc="closeTheModal()" to a DIV and template tag within the <Modal> and thenemitted the clickedModalClose event programatically within the closeTheModal() function , but to no avail

Could someone, please, shed some light to this problem :/

UPDATE

The correct answer is given by @blackgreen

After the component is rendered in order to handle the ESC event that event has to be attached to a DOM element within the component.

What I did is I simply used template references on the Modal closing button to have it available in the onMounted(){} hook and called the focus() method on it

Parent component

<Modal @keydown.esc="isShownModal=false">

Child component

<template>
<button ref="closingButton" class="modal-close is-large" aria-label="close"></button>
</template>

and in the JS part:

<script>
import {ref, onMounted} from 'vue';
export default {
  setup(props, context){
    
    const closingButton = ref(null)
    onMounted(()=>{
      closingButton.value.focus()
    })

    return {closingButton}

  }
}
1 Answers

The binding @keydown.esc is correct (demonstrative fiddle).

But for the event to be actually fired, the element on which the listener is declared must be on focus.

In this fiddle example, the mounted hook sets the focus on a <div> element with tabindex=0 (a trick so that it can have focus). If you press Esc while that div is focused, you'll see the counter incrementing as well.

Related