Search code examples
javascriptfocusalpine.js

Alpine.js Focus and Keyup Event


I have a webpage using Alpine.js with Focus Plugin to open a modal when an item is clicked, I also want to be able to open the modal with the space bar and enter key

It works, but when using the enter key it seems to trigger again the click to the modal and it reopens at the moment

This is the code:

<div x-data="{ open: false }">  
<div class="mt-8 grid grid-cols-3 gap-8 mx-auto max-w-4xl">
    <div 
         class="aspect-square rounded bg-gray-300 grid place-items-center font-bold text-3xl"
         @click="open = 1" 
         @keyup.enter="open = 1" 
         @keyup.space="open = 1"
         tabindex="0"
         >1</div>
        <div 
         class="aspect-square rounded bg-gray-300 grid place-items-center font-bold text-3xl"
         @click="open = 2" 
         @keyup.enter="open = 2" 
         @keyup.space="open = 2"
         tabindex="0"
         >2</div>
        <div 
         class="aspect-square rounded bg-gray-300 grid place-items-center font-bold text-3xl"
         @click="open = 3" 
         @keyup.enter="open = 3" 
         @keyup.space="open = 3"
         tabindex="0"
         >3</div>
  </div>
  <div
            id="modal"
            class="bg-black/50 fixed top-0 left-0 w-full h-full p-4 grid place-items-center z-10"
            x-show="open"
            x-cloak
            x-trap.inert="open != 0" 
            x-transition.opacity
            >
            <div class="modal__body bg-white max-w-[760px] p-8 relative" x-transition.delay.50ms>
              <button 
                    type="button"
                    class="font-bold uppercase"
                    @click="open = false"
                    @keyup.enter="open = false" 
                    @keyup.space="open = false"
                    tabindex="0"
                    >
                    Close
                </button>
              <div>Modal content</div>
    </div>
  </div>

</div>

I've prepared a simple CodePen to see the issue, just tab to any element and hit enter to open modal, then hit enter to close


Solution

  • <button> elements automatically fire click events for Enter or Space keypresses, on the keydown of the keypress.

    Thus, when Enter or Space is pressed down on the close button of the modal, the modal closes. However, focus is then returned the <div> and when one releases the Enter or Space key, this activates the keyup event listener.

    To work around this, you could consider listening for keydown instead on the <div> elements:

    <script defer src="https://cdn.jsdelivr.net/npm/@alpinejs/focus@3.13.0/dist/cdn.min.js"></script>
    <script defer src="https://cdn.jsdelivr.net/npm/alpinejs@3.13.0/dist/cdn.min.js"></script>
    <script src="https://cdn.tailwindcss.com/3.3.3"></script>
    
    <div x-data="{ open: false }">  
      <div class="mt-8 grid grid-cols-3 gap-8 mx-auto max-w-4xl">
        <div 
          class="aspect-square rounded bg-gray-300 grid place-items-center font-bold text-3xl"
          @click="open = 1" 
          @keydown.enter="open = 1" 
          @keydown.space="open = 1"
          tabindex="0"
        >1</div>
        <div 
          class="aspect-square rounded bg-gray-300 grid place-items-center font-bold text-3xl"
          @click="open = 2" 
          @keydown.enter="open = 2" 
          @keydown.space="open = 2"
          tabindex="0"
          >2</div>
        <div 
          class="aspect-square rounded bg-gray-300 grid place-items-center font-bold text-3xl"
          @click="open = 3" 
          @keydown.enter="open = 3" 
          @keydown.space="open = 3"
          tabindex="0"
        >3</div>
      </div>
      <div
        id="modal"
        class="bg-black/50 fixed top-0 left-0 w-full h-full p-4 grid place-items-center z-10"
        x-show="open"
        x-cloak
        x-trap.inert="open != 0" 
        x-transition.opacity
      >
        <div class="modal__body bg-white max-w-[760px] p-8 relative" x-transition.delay.50ms>
          <button 
            type="button"
            class="font-bold uppercase"
            @click="open = false"
            tabindex="0"
          >
            Close
          </button>
          <div>Modal content</div>
        </div>
      </div>
    </div>