iT邦幫忙

2021 iThome 鐵人賽

DAY 4
0
Modern Web

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

[Day04] - 新擬物風按鈕(二) - shadow dom 介紹

  • 分享至 

  • xImage
  •  

昨天我們做了一個不能點的 neuomorphic-button

今天我們把他可以點擊 & 加上 shadow-dom 讓元件內外的 styling 區隔開來吧 !


回顧昨天進度

昨天我們做了一個不能點的 neuomorphic-button 元件

// neuomorphic-button.js
class NeuomorphicButton extends HTMLElement {

  // as Component mounted to page
  constructor() {

    // Always call super first in constructor
    super();

    // Element functionality written in here
    const div = document.createElement('div')
    div.classList.add('icon-box')
    div.innerHTML = `<i class="fas fa-wifi"></i>`

    this.append(div)
  }
}

window.customElements.define('neuomorphic-button', NeuomorphicButton);

首先我們先補上 checkbox 讓其可以被點擊 ,

因為我們要客製化 checkbox 的樣式 , 因此會需要設定 checkbox 的樣式為 display:none 把它隱藏起來

這時我們可以請出 label 這個 Html tag , 用 labelinput 元素包住 , 將 onclick 事件轉送到 input[type="checkbox"]

// neuomorphic-button.js
class NeuomorphicButton extends HTMLElement {

  constructor() {

    super();

    // 利用 div 顯示按鈕的樣式
    const div = document.createElement('div')
    div.classList.add('icon-box')
    div.innerHTML = `<i class="fas fa-wifi"></i>`

    // label & 其內部的 checkbox
    const label = document.createElement('label')
    label.innerHTML = `<input type="checkbox">`
    label.append(div)

    this.append(label)
  }
}

window.customElements.define('neuomorphic-button', NeuomorphicButton);

當然在 html 中我們需要補上一些樣式設定

<style>
    /* neuomorphic-button 的樣式 */
    label input[type='checkbox'] {
      display: none;
    }

    label .icon-box {
      width: 60px;
      height: 60px;
      position: relative;
      background-color: #ebf5fc;
      box-shadow: 8px 8px 16px #bcbcbc,
      -8px -8px 16px #ffffff;
      display: flex;
      justify-content: center;
      align-items: center;
      border-radius: 10px;
      cursor: pointer;
      transition:all 0.3s;
    }

    label .icon-box:hover {
      transform:translateY(3px)
    }

    label .icon-box i {
      font-size: 2em;
      color: #6a9bd8;
    }

    // 被點選後的樣式
    label input[type='checkbox']:checked ~ .icon-box {
      box-shadow: inset -2px -2px 5px rgba(255, 255, 255, 1),
      inset 3px 3px 5px rgba(0, 0, 0, 0.1);
    }

    label input[type='checkbox']:checked ~ .icon-box i {
      transform: scale(0.95);
      filter: hue-rotate(90deg);
    }

  </style>

之後我們得到了一個可點擊的 neuomorphic-button

目前我們的 style 是定義在 html 的 header 中 , 如果有其他的 css 設定可能會造成互相影響

這時我們就可以請出 shadow dom 來 ~~~

const shadowRoot = this.attachShadow({mode: 'open'})

既然我們都改用 shadow dom 那當然需要將掛在 this 上的那些 HTML 元素 , 改掛在 shadowRoot 的上面

// neuomorphic-button.js
class NeuomorphicButton extends HTMLElement {

  // as Component mounted to page
  constructor() {

    ...跟之前相同的設定

    // 將 label 改 append 到 shadowRoot 上面
    const shadowRoot = this.attachShadow({mode: 'open'})
    shadowRoot.append(label)
  }
}

window.customElements.define('neuomorphic-button', NeuomorphicButton);

改後我們會獲得以下截圖

奇怪 , 外部有定義樣式阿 ~ , 那 neuomorphic-button 元件內的樣式為何會沒有吃到呢 ?

原來 shadow dom 也就是 this.attachShadow({mode: 'open'}) 會將 shadowRoot 中的樣式跟外部 Html 的樣式做一個區隔

所以 , 我們需要在 shadowRoot 的內部再定義一次樣式

// neuomorphic-button.js
class NeuomorphicButton extends HTMLElement {

  // as Component mounted to page
  constructor() {

    ...跟之前相同的設定

     // 將樣式相關的部分 , 定義在此
+    const fontAwesomeStyle = `<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/5.15.1/css/all.min.css">`
+    const styleStr =  'neuomorphic-button 的樣式'

    // 將 label 改 append 到 shadowRoot 上面
    const shadowRoot = this.attachShadow({mode: 'open'})
+    shadowRoot.innerHTML = fontAwesomeStyle + styleStr  // 將定義出來的樣式 , 掛載到 shadowRoot 內部
    shadowRoot.append(label)
  }
}

window.customElements.define('neuomorphic-button', NeuomorphicButton);

太棒了 ! 按鈕吃到我們設定的樣式了 (^∀^●)ノシ

如果想查看實際頁面 , 請到 shadow-dom.html 查看

補充的小貼士

1. label 的特點

下方情況 label 會將 onclick 事件做一個轉送 , 事件將傳給對應的元素並觸發其 onclick

A. for 對應的那個 name HTML 元素

<div class="preference">
    <label for="cheese">Do you like cheese?</label>
    <input type="checkbox" name="cheese" id="cheese">
</div>

B. 包在 label 內的 input 元素

<label>
  <input type="checkbox">
  I agree to the Terms and Conditions
</label>

2. checkbox 無法用 css 直接設定底色 & 邊框樣式

因此我們需要利用 隱藏原生 checkbox + 設定 div 樣式 來製作自身想要的 checkbox

<label>
  <!-- 隱藏原生 checkbox -->
  <input type="checkbox" style="display:none">
  
  <!-- 將想要的 checkbox 樣式設定在 myCheckbox 中 -->
  <div class="myCheckbox">
</label>

詳細的 Styling checkbox 步驟請參考 這篇

參考資料 :


上一篇
[Day03] - 第一個 WebComponent 元件
下一篇
[Day05] - 新擬物風按鈕(三) - 參數設定
系列文
Web Component 網頁元件之路30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言