How to Close SLDS Modal on Escape(Esc) key in LWC with KeyboardEvents

How to Close SLDS Modal on Escape(Esc) key in LWC with KeyboardEvents

MUCH MORE THAN THE MOUSE

While it’s important to consider mouse interaction and visual layout of a design, we must also consider how the feature work with keyboard because it can have many benefits

  • Make it much more accessible and easy to use

  • Enables users to switch between or use a combination of mouse and keyboard

One such feature I really find useful is to be able to close the Modals in Salesforce with the Escape key. This is an out of the box feature for the standard modals that we see in the Lightning Experience. Personally, I find it useful because once the modal is open and if I need to close it, I don’t have to move my mouse pointer around to click on the close button, I can just use the Esc key to do it. Saves time and effort. Perfecto!

It is also documented as one of the expected keyboard interactions for Modals in the Lightning Design System.

So I definitely wanted to make this a part of my latest build, which I did and I wanted to share it with you. Spoiler: it is super simple!

HERE IS MAGICAL CODE THAT MAKES IT HAPPEN:

magicalCode.js

//add eventListner to handle keystrokes
connectedCallback() {
    document.addEventListener("keydown", this.handleEscapeKey.bind(this));
}

handleEscapeKey(event){
    //check if the escape key was pressed and if the modal is open
    if(event.key === 'Escape' && this.isModalOpen){
        //close the modal
        this.closeModal();
    }
}
JAVASCRIPT EXPLANATION:
  1. Line 3 – Simply by listening to the KeyboardEvent in JavaScript, specifically the keydown event.

  2. Line 8 – Once we get hold of the keydown event, we verify if the key pressed is the Escape key

  3. Line 8 – Next, we also verify if our modal is open

  4. Line 10 – If both the conditions are true, we close the modal

That is pretty much it!

WANT THE COMPLETE MODAL CODE? CHECK IT OUT BELOW!!

lwcSldsModal.html

<template>
    <lightning-button variant="brand" label="Show Modal" title="Show Modal" onclick={showModal}
        class="slds-m-left_x-small"></lightning-button>

    <section role="dialog" tabindex="-1" aria-labelledby="modal-heading-01" aria-modal="true"
        aria-describedby="modal-content-id-1" class={modalClass}>
        <div class="slds-modal__container">
            <header class="slds-modal__header">
                <button class="slds-button slds-button_icon slds-modal__close slds-button_icon-inverse" title="Close"
               onclick={closeModal}>
               <lightning-icon icon-name="utility:close" alternative-text="Close Modal" variant="inverse" size="small">
               </lightning-icon>
            </button>
                <h2 id="modal-heading-01" class="slds-text-heading_medium slds-modal__title slds-hyphenate">
                    Modal Header
                </h2>
            </header>
            <div class="slds-modal__content slds-p-around_medium" id="modal-content-id-1">
                <p>
                    Modal Content Here...
                </p>
            </div>
            <footer class="slds-modal__footer">
                <lightning-button variant="neutral" label="Close" title="Close" onclick={closeModal}>
                </lightning-button>
                <lightning-button variant="brand" class="slds-m-left_x-small" label="Save" title="Save" onclick={closeModal}>
                </lightning-button>
            </footer>
        </div>
    </section>
    <div class={modalBackdropClass}></div>
</template>

lwcSldsModal.js

import { LightningElement, track } from "lwc";

export default class LwcSldsModal extends LightningElement {

    //track whether the modal is open(true) or closed(false), closed by default
    @track isModalOpen = false;

    //add eventListner to handle keystrokes
    connectedCallback() {
        document.addEventListener("keydown", this.handleEscapeKey.bind(this));
    }

    //sets the isModalOpen property to true, indicating that the Modal is Open
    showModal() {
        this.isModalOpen = true;
    }

    //sets the isModalOpen property to false, indicating that the Modal is Closed
    closeModal() {
        this.isModalOpen = false;
    }

    handleEscapeKey(event){
        //check if the escape key was pressed and if the modal is open
        if(event.key === 'Escape' && this.isModalOpen){
            //close the modal
            this.closeModal();
        }
    }

    /* 
  can be used instead of the above two methods - showModal() & closeModal()
  just toggles the isModalOpen property - true if false, false if true 
  */
    toggleModal() {
        this.isModalOpen = !this.isModalOpen;
    }

    //compute the CSS classes of the Modal(popup) based on the value of isModalOpen property
    get modalClass() {
        return `slds-modal ${this.isModalOpen ? "slds-fade-in-open" : ""}`;
    }

    //compute the CSS classes of the Modal Backdrop(grey overlay) based on the value of isModalOpen property
    get modalBackdropClass() {
        return `slds-backdrop ${this.isModalOpen ? "slds-backdrop_open" : ""}`;
    }
}

lwcSldsModal.js-meta.xml

<LightningComponentBundle xmlns="http://soap.sforce.com/2006/04/metadata" fqn="lwcSldsModal">
    <apiVersion>46.0</apiVersion>
    <isExposed>true</isExposed>
    <targets>
        <target>lightning__AppPage</target>
        <target>lightning__RecordPage</target>
        <target>lightning__HomePage</target>
    </targets>
</LightningComponentBundle>
RESOURCES

Thank you for checking this out, take care, stay safe & I’ll see you in the next one!