HTML原生UI的实现

3,180 阅读5分钟

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等目前兼容性极差,离投入生产还尚远。请勿用于生产环境中。

本文发布于《我的博客