<script>
  const css = `
    #favDialog {
      position: relative;
      padding-top: 2em;
    }
    #close {
      position: absolute;
      font-weight: 700;
      right: 0;
      top: 0;
      padding: .5em;
      cursor: pointer;
    }
    #close:hover {
      background-color: #e3e3e3;
    }
  `;
  const html = `
    <dialog id="favDialog" open>
        <span id="close">&#9587;</span>
        <slot name="my-content">Defaut Content</slot>
      </dialog>
  `;
  customElements.define('my-popup', class extends HTMLElement {
    constructor() {
      super();
    }
    #myTemplate =  document.createElement('template');
    #shadow = this.attachShadow({ mode : 'open'});
    #slot = null;

    #render() {
      const myTemplate = document.createElement('template');
      myTemplate.innerHTML = `<style>${css}</style>${html}`
      this.#shadow.appendChild(myTemplate.content.cloneNode(true));
      this.#slot = this.#shadow.querySelector('slot[name=my-content]');
    }

    // part 3
    #handleClose() {
      this.parentNode.removeAttribute('open');
      this.dispatchEvent(new Event('onPopupClose',  { bubbles: true, composed: true }));
    }
    #handleCancel() {
      this.dispatchEvent(new Event('onPopupCancel', { bubbles: true }));
    }
    #handleSubmit() {
      const data = document.getElementById('favAnimal').value;
      this.dispatchEvent(
        new CustomEvent('onPopupSubmit', {
          bubbles: true,
          detail: {
            selectedValue: data,
          },
        })
      );
    }
    #addEvents() {
      this.#shadow.getElementById('close').addEventListener('click', this.#handleClose);
      this.#slot.addEventListener('slotchange', (e) => {
        let form = this.#slot.assignedNodes()[0];
        form.querySelector('#cancel').addEventListener('click', this.#handleCancel);
        form.querySelector('#submit').addEventListener('click', this.#handleSubmit);
      });

    }
    #removeEvents() {
      this.#shadow.getElementById('close').removeEventListener('click', this.#handleClose);
      this.#slot.addEventListener('slotchange', (e) => {
        let form = this.#slot.assignedNodes()[0];
        form.querySelector('#cancel').removeEventListener('click', this.#handleCancel);
        form.querySelector('#submit').removeEventListener('click', this.#handleSubmit);
      });
    }
    // EO: part 3

    connectedCallback() {
      this.#render();
      this.#addEvents();
    }

    // part 3
    disconnectedCallback() {
      this.#removeEvents();
    }
    // EO: part 3
  });
</script>
 
<body>  
  <my-popup>
    <style>
      my-popup .menu {
        margin-top: 2em;
        display: flex;
        justify-content: space-between;
        align-items: center;
      }
      .events-monitor {
        margin: 1em auto;
        width: 300px;
        height: 200px;
        max-height: 200px;
        overflow-y: auto;
        border: 1px solid red;

      }
    </style>
    <form method="dialog" slot="my-content" onSubmit="return customSubmit(event);">
      <section>
        <p><label for="favAnimal">Favorite animal:</label>
          <select id="favAnimal" name="favAnimal">
            <option></option>
            <option>Brine shrimp</option>
            <option>Red panda</option>
            <option>Spider monkey</option>
          </select></p>
      </section>
      <div class="menu">
        <button id="cancel">Cancel</button>
        <button id="submit">Confirm</button>
      </div>
    </form>
  </my-popup>
  
  <div class="events-monitor">
    <ul class="event-list"></ul>
  </div>
  
  <script>
  function customSubmit(e) {
    e.preventDefault();
    return false;
  }
  
  function addEventToList(data) {
    console.log('data', JSON.stringify(data));
    document.querySelector('.event-list').innerHTML += `<li>${JSON.stringify(data)}</li>`;
  }
  
  const myPopup = document.querySelector('my-popup');
  
  myPopup.addEventListener('onPopupCancel', (e) => addEventToList('cancel clicked'));
  myPopup.addEventListener('onPopupClose', (e) => addEventToList('close clicked'));
  myPopup.addEventListener('onPopupSubmit', (e) => addEventToList(e.detail));
</script>
</body>

External CSS

This Pen doesn't use any external CSS resources.

External JavaScript

This Pen doesn't use any external JavaScript resources.