Recipe 13.3

Creating a Confirmation Dialog Web Component

This example creates a confirmation dialog wrapped in a web component (custom element). It makes the flow of showing the modal and waiting for the result a little easier by using a Promise.

When the custom dialog’s showModal method is called, it shows the underlying dialog and returns a Promise that will be resolved once the modal is either confirmed or cancelled. The value of the Promise will be a boolean - true if the user confirmed, or false if they canceled.

Demo

Are you sure you want to do that?

Code

JavaScript
const template = document.createElement('template');
template.innerHTML = `
  <dialog id="confirm">
    <h2>Confirm</h2>
    <p><slot></slot></p>

    <button type="button" class="btn btn-primary confirm-button">Confirm</button>
    <button type="button" class="btn btn-secondary cancel-button">Cancel</button>
  </dialog>
`;

class ConfirmDialog extends HTMLElement {
  connectedCallback() {
    const shadowRoot = this.attachShadow({ mode: 'open' });
    shadowRoot.appendChild(template.content.cloneNode(true));

    this.dialog = shadowRoot.querySelector('dialog');

    shadowRoot.querySelector('.confirm-button')
      .addEventListener('click', () => {
        this.dialog.close('confirm');
      });

    shadowRoot.querySelector('.cancel-button')
      .addEventListener('click', () => {
        this.dialog.close('cancel');
      });

    this.dialog.addEventListener('cancel', () => {
      this.dialog.returnValue = 'cancel';
    });
  }

  showModal() {
    this.dialog.showModal();

    return new Promise(resolve => {
      this.dialog.addEventListener('close', () => {
        resolve(this.dialog.returnValue === 'confirm');
      }, { once: true });
    });
  }
}

if (!customElements.get('confirm-dialog')) {
  customElements.define('confirm-dialog', ConfirmDialog);
}

const confirmDialog = document.querySelector('#confirm');
document.querySelector('#show-confirm')
  .addEventListener('click', async () => {
    const confirmed = await confirmDialog.showModal();
    if (confirmed) {
      result.textContent = '✅ User confirmed.';
    } else {
      result.textContent = '❌ User canceled.';
    }
  });
HTML
<style>
  confirm-dialog:not(:defined) {
    display: none;
  }
</style>

<confirm-dialog id="confirm">
  Are you sure you want to do that?
</confirm-dialog>

<button id="show-confirm" class="btn btn-primary">Show Confirmation</button>

<div id="result"></div>
Web API Cookbook
Joe Attardi