iT邦幫忙

2021 iThome 鐵人賽

DAY 8
0
Modern Web

Web Component 網頁元件之路系列 第 8

[Day08] - 彈跳視窗 Modal - Slot 介紹

網頁元件中 , 常會使用 Modal 這種類型的元件

如果我們將其製作成一個 <Modal> 的 WebComponent , 之後在使用時 , 應該會輕鬆不少吧 !

下面我們來製作 <Modal> 吧 !


前期準備

one 我們先到 CodePen 下載今天的 Modal 範本

two 解壓縮後 , 觀察 html 中的 pop-up-container 結構

我們可以發現 Modal 的大致結構如下

<div class="pop-up-container" style="display: none;">
  <div class="pop-up-container-root">
    <div class="pop-up-box">
      <div class="pop-up-title">
        <h3>Title</h3>
        <close-icon/>
      </div>
      <div class="pop-up-content">
      </div>
      <div class="pop-up-action">
        <button onclick='closeModal()'>取消</button>
        <button onclick='closeModal()'>確定送出</button>
      </div>
    </div>
  </div>
</div>
區塊名稱 描述
pop-up-container Modal 的容器 , 黑底遮罩放在這 , 不會改變
pop-up-container-root Modal 的捲動區塊 , 不會改變
pop-up-box Modal 的內容放在其中 , 不會改變
pop-up-title 標題區塊 , 可能會修改其內容
pop-up-content 內文區塊 , 可能會修改其內容
pop-up-action 按鈕區域 , 可能會修改其內容

pop-up-title . pop-up-content . pop-up-action 這 3 個區塊需要塞入 html 內容

Day02 介紹 wired-element 時 , 我們了解到 slot 可以塞入 html 內容

不過 , 需要區分 pop-up-title . pop-up-content . pop-up-action 3 個區塊的 slot 要如何製作呢 ?

three Name Slot 的介紹

我們可以在 shadow-dom 中設定 <slot name="modal-body"> 在使用 <Modal> 時設定 slot="modal-body" ,

// in html usage 
<Modal>
  <h2 slot="modal-body">
    這是內文...這是內文...這是內文...這是內文...
  </h2>
</Modal>
// in web component 
class Modal extends HTMLElement {

  connectedCallback() {

    const htmlStr = `
        <div>
           其他文字
          <div class="pop-up-content">
            <h2 slot="modal-body">
              這是內文...這是內文...這是內文...這是內文...
            </h2>
          </div>
        </div>
    `

    // 啟用 shadow dom
    this.attachShadow({mode: 'open'}).innerHTML = htmlStr
  }

}

window.customElements.define('my-modal', Modal);

Pink Tree 其實做法跟 Vue 的 Slot 是相似的

實作開始

one 建立 customElements - my-modal

class Modal extends HTMLElement {

}

window.customElements.define('my-modal', Modal);

twoshadow-dom 開啟 & 將 html 結構複製到 class 中

class Modal extends HTMLElement {

  connectedCallback() {

    const styleStr = `<link rel="stylesheet" href="./modal.css">`

    const htmlStr = `
        <div class="pop-up-container" style="display: none;">
          <div class="pop-up-container-root">
            <div class="pop-up-box">
              <div class="pop-up-title flex justifyContent">
               <h3>這是 Modal 的 Title</h3>
                <img class='close' src="./close.png" />
              </div>
              <div class="pop-up-content">
                這是 Modal 的 Body
              </div>
              <div class="pop-up-action flex justifyContent">
                  <button class='close'>取消</button>
                  <button class='confirm'>確定送出</button>
              </div>
            </div>
          </div>
        </div>
    `

    // 啟用 shadow dom
    this.attachShadow({mode: 'open'}).innerHTML = styleStr + htmlStr
  }

}

window.customElements.define('my-modal', Modal);

three 將 Name Slot 設定到 html 中

  • 將右側的 3 區塊 pop-up-title . pop-up-content . pop-up-action 設定對應的 slot
區塊名稱 Slot 名稱
pop-up-title modal-title
pop-up-content modal-body
pop-up-action modal-action
<div class="pop-up-box">
  <div class="pop-up-title flex justifyContent">
+   <slot name="modal-title"><h3>這是 Modal 的 Title</h3></slot>
    <img class='close' src="" />
  </div>
  <div class="pop-up-content">
+    <slot name="modal-body">這是 Modal 的 Body</slot>
  </div>
  <div class="pop-up-action flex justifyContent">
+    <slot name="modal-action">
       <button class='close'>取消</button>
       <button class='confirm'>確定送出</button>
+    </slot>
  </div>
</div>

four 設定 Modal 的 open . close 事件

class Modal extends HTMLElement {

  connectedCallback() {

    this.attachShadow({mode: 'open'}).innerHTML = styleStr + htmlStr

    this.shadowRoot.querySelector('.close:nth-child(1)').addEventListener('click', () => this._close())
    this.shadowRoot.querySelector('.close:nth-child(2)').addEventListener('click', () => this._close())
    this.shadowRoot.querySelector('.confirm').addEventListener('click', () => this._confirm())
  }

  _open() {

    const shadowRoot = this.shadowRoot;
    const modalWrap = shadowRoot.querySelector('.pop-up-container');
    const popup = modalWrap.querySelector('.pop-up-box');

    modalWrap.style.display = 'flex';
    popup.style.transform = 'scale(0)';

    setTimeout(() => popup.style.transform = 'scale(1)', 0)
  }

  _close() {

    const shadowRoot = this.shadowRoot;
    const modalWrap = shadowRoot.querySelector('.pop-up-container');
    const popup = modalWrap.querySelector('.pop-up-box');

    popup.style.transform = 'scale(0)';

    setTimeout(() => modalWrap.style.display = 'none', 300)
  }
  
}

window.customElements.define('my-modal', Modal);

five 在頁面中使用 my-modal

<body>
<my-modal @confirm="() => console.log('確定送出 !')">
  <h2 slot="modal-title">
    表頭寫入
  </h2>
  <h2 slot="modal-body">
    這是內文...這是內文...這是內文...這是內文...
  </h2>
</my-modal>

<script src="./modal-wc.js"></script>
</body>
</html>

six 建立按鈕來開啟今天製作的 Modal

<button onclick='showModal()'>
  開啟 Modal
</button>
<script>
  function showModal() {
    document.querySelector('my-modal')._open()
  }
</script>

完成 !!

成果

如果想直接體驗成果 , 請到 web-component-modal.html 查看

題外話

其實 HTML Tag 有一個 <dialog> 跟今天製作的元件很相似

參考資料 :


上一篇
[Day07] - 新擬物風按鈕(五) - 參數改變 & 監聽變化
下一篇
[Day09] - 未知網址的彈跳視窗 - is 屬性
系列文
Web Component 網頁元件之路30

尚未有邦友留言

立即登入留言