HTML原生UI的實現

Smohan發表於2018-07-26

HTML5中有很多十分有趣的標籤,相比於article/section等這些我們所熟知並且已經應用的標籤,還有些標籤或者因為相容性,或者因為還處於草稿階段而不那麼為人所知。在使用Bootstrap/Element等UI元件時,常常看到某個標籤上會應用一些如role="dialog"的不常見的屬性,這是因為HTML語義化的需要。role屬性主要供有障礙人士使用,作用是告訴如螢幕朗讀程式等輔助程式當前元素所扮演的角色。由於HTML5標籤本身已經實現了語義化,role屬性不建議被新增。而對於一些老瀏覽器或者一些模擬元素(如div模擬dialog),role屬性則應該被註明。Bootstrap中有很多註明了role屬性的元件,而這些元件有一部分已經被HTML5所實現,並已經標籤化,如dialog,progress等。當然,這些標籤的原始樣式各個瀏覽器不統一,並且可以說很簡陋,對此,可以通過一些自定義CSS樣式來進行優化。

特別說明:本文介紹的標籤,目前相容性都很差(雖然部分標籤有對應的polyfill),僅供學習和了解,請勿用於生產環境。同時,DEMO僅限Chrome瀏覽器預覽。

專案地址

可以在github上關注該專案,目前已經實現了dialog,progress,range,collapse等元件,後續會陸續總結其他。

Dialog

dialog元素表示一個對話方塊或者互動式視窗元件.

Example.

dialog元素可以說完全滿足了對互動的需求,如我們所期望的完全的在頁面上水平垂直居中,智慧的啟用層置頂(無需手動設定z-index), 共享backdrop層等。並且diaolog有對應的open(show,showModal)和close方法。dialog的預設樣式如下圖:

dialog-default-style
新增自定義樣式後:
dialog-custom-style

自定義樣式

  • 可以通過::backdrop偽類優化遮罩層
.nui-dialog::backdrop {
  background-color: rgba(0, 0, 0, .5)
}
複製程式碼
  • open屬性:當呼叫show或者showModal方法時,瀏覽器會自動為dialog新增上open的屬性,用來表示當前dialog處於啟用狀態,因此可以通過給open屬性新增動畫來實現dialog啟用時的動畫效果
.nui-dialog[open] {
  animation: slide-up 0.4s ease-out;
}

@keyframes slide-up {
  0% {
    opacity: 0;
    transform: translate(0, 15px);
  }
  100% {
    opacity: 1;
    transform: translate(0, 0);
  }
}
複製程式碼

JavaScript方法和事件

支援方法
  • show : 啟用dialog, 基於DOM流的位置顯示元素
  • showModal: 啟用dialog, 推薦 .預設顯示在頁面的中心位置,並且常駐頂層(無需設定z-index)
  • close: 用來關閉dialog
支援事件
  • close : dialog 關閉時觸發
  • cancel : dialog 取消(如按esc鍵)時觸發
虛擬碼
<script>
  var dialog = document.getElementById('dialog')
  
  button.addEventListener('click', function(e){
    dialog.showModal() // or .show
  })

  closeBtn.addEventListener('close', function(e){
    dialog.close() // dialog.close('type') 可以傳遞一個字串
  })

  // 當關閉時會觸發 close 事件
  dialog.addEventListener('close', function(e) {
    console.log(e) // e.type 就是呼叫close方法時傳遞的type字串,預設值為close
  })

  // 當取消時(如按esc鍵)會觸發 cancel 事件
  dialog.addEventListener('cancel', function(e) {})
</script>  
複製程式碼
為指定按鈕繫結關閉事件

通過事件代理,可以很方便的為標記了[data-dismiss="modal"][aria-label="Close"]屬性的元素來邦定關閉事件。

<button type="button" class="nui-dialog__close" data-dismiss="modal" aria-label="Close">
  <span aria-hidden="true">&times;</span>
</button>

<a data-dismiss="modal" aria-label="Close">關閉</a>
複製程式碼
document.addEventListener('click', function (e) {
  var target = e.target
  var selector = '[data-dismiss="modal"][aria-label="Close"]'
  if (target.matches(selector) || target.closest(selector)) {
    var modal = target.closest('dialog')
    modal && modal.close()
  }
})
複製程式碼

Progress

progress元素用來顯示一項任務的完成進度

Example.

預設樣式和修改後的樣式(自定義樣式採用Bootstrap中Progress bar的樣式)

progress 預設樣式和自定義樣式

動態改變progress value

自定義樣式的實現

  • ::-webkit-progress-bar : 偽類::-webkit-progress-bar用來定義Progress bar層的樣式
  • ::-webkit-progress-value: 偽類::-webkit-progress-value用來定義Progress value層的樣式
:root {
  --primary-color: #43a3fb; 
}
.nui-progress::-webkit-progress-bar{
  background-color: #e9ecef;
  border-radius: 0;
}

.nui-progress::-webkit-progress-value{
  background-color: var(--primary-color);
  cursor: default;
}
複製程式碼

Collapse

利用detailssummary標籤,實現摺疊皮膚的效果.

Example.

details用於描述文件的細節,與summary標籤配合使用可以為details定義標題。標題是可見的,使用者點選標題時,會顯示出 details

預設樣式
自定義樣式

自定義樣式

  • ::-webkit-details-marker:偽元素::-webkit-details-marker可以用來定義或覆蓋list-item的樣式.一般情況下,設定::-webkit-details-marker不可見,轉而為summary元素新增一個::before/::after的偽類來自定義icon樣式。然後再為details[open]狀態下重新定義icon(::before/::after)
.nui-collapse>.nui-collapse__title::-webkit-details-marker {
  display: none;
}
/* normal */
.nui-collapse>.nui-collapse__title::before {
  content: '';
  position: absolute;
  left: auto;
  right: 0;
  width: 6px;
  height: 6px;
  border: 1px solid var(--title-color);
  border-right-width: 0;
  border-bottom-width: 0;
  transform: translate(-50%) rotate(135deg);
  transition: transform .2s;
}
/* open */
.nui-collapse[open]>.nui-collapse__title::before {
  transform: translate(-50%) rotate(225deg);
}
複製程式碼
  • 利用open屬性,通過加入少許javascript程式碼,還可以實現手風琴效果

accordion

  <div class="nui-collapse-accordion">
    ...
    <details class="nui-collapse nui-collapse--right">
      <summary class="nui-collapse__title">Title</summary>
      <p class="nui-collapse__body">...</p>
    </details>
    ...
  </div>
  <script>  
  var ARRAY_SLICE = Array.prototype.slice
  document.addEventListener('click', function(e) {
    var target = e.target
    var selector = '.nui-collapse-accordion .nui-collapse__title'
    if (target.matches(selector) || target.closest(selector)) {
      var collapse = target.closest('.nui-collapse')
      if (collapse) {
        var group = collapse.closest('.nui-collapse-accordion')
        if (group) {
          ARRAY_SLICE.call(group.querySelectorAll('.nui-collapse')).forEach(function(el) {
            if (el !== collapse) {
              el.open = false
            }
          })
        }
      }
    }
  })
  </script>
複製程式碼

Range

input[type="range"]元素顯示為滑塊,表示輸入型別用於應該包含指定範圍值的輸入欄位

Example.

自定義range的樣式主要通過::-webkit-slider-runnable-track,::-webkit-slider-thumb來實現。

range
目前,就實現了這麼四個標籤的樣式重置Github,再加上之前實現的關於表單元素的樣式優化(radio, checkbox, switch),儼然是一個小UI庫了。當然,dialog等目前相容性極差,離投入生產還尚遠。請勿用於生產環境中。

本文釋出於《我的部落格

相關文章