利用HTML5,无JS实现各种交互效果

本文利用的是HTML5 details, summary

首先

一、了解HTML5 details, summary默认交互行为

<details> 标签在Chrome,Firefox等浏览器下默认是有展开收起行为的,例如下面HTML:

<details>

<summary>这是摘要1</summary>
<p>这里具体描述,标签相对随意,例如这里使用的&lt;p&gt;标签。</p>

</details>

结果UI表现为:

![details标签默认效果截图](http://upload-images.jianshu.io/upload_images/13133049-bb050040178dcd19.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)

具体描述为:

1.  只显示了`<summary>`标签内容,而`<p>`默认隐藏了;
2.  `<summary>`标签前面出现了一个小三角;

小三角图形的隐喻是:我是可点击的,点击我可能会出现宝箱。

OK,我们不妨就点击一下,结果如下图:

![image.png](https://upload-images.jianshu.io/upload_images/13133049-c4cfc9729c33334b.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)

具体描述为:

1.  原本隐藏的`<p>`标签显示出来了;
2.  `<summary>`标签前面的小三角方向朝下了;

此时我们再一次点击,`<p>`标签内容又会隐藏收起,箭头方向还原,如下图:

![image.png](https://upload-images.jianshu.io/upload_images/13133049-185788b32b785b95.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)


活脱脱一个天然的展开收起效果。

#### 展开与收起是通过open属性控制的

通过在`<details>`标签上添加布尔类型的`open`属性,可以让我们的详情信息默认就是展开状态,如下HTML示意:

<details open>

<summary>这是摘要2</summary>
<content>这里&lt;details&gt;标签设置了HTML布尔属性open,因此,默认是展开状态。</content>

</details>

结果如下截图:

![open属性下的信息默认展开](http://upload-images.jianshu.io/upload_images/13133049-1c326e560466146c.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)

如果我们使用JS脚本手动移除这个`open`属性,即使没有点击行为的发生,我们内容也会收起。

#### <summary>如果缺省

`<summary>`标签如果缺省,则`<details>`元素会在内部自动创建一个`<summary>`内容,默认的文案是“详细信息”。如下HTML代码:

<details open>

<p>如果&lt;summary&gt;缺省,则会自动补上,文案是“详细信息”。</p>

</details>

结果如下截图所示:

![summary 详细信息占位示意图](http://upload-images.jianshu.io/upload_images/13133049-67d3075f9f59d9fe.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)

二、details浏览器内置UI可以自定义

<details>标签默认的小三角样式有些简陋,在实际应用的时候,往往不是我们希望的样子,不要担心,我们是可以对其进行自定义的。在Chrome等浏览器下使用::-webkit-details-marker,在Firefox浏览器下使用::-moz-list-bullet可以对小三角进行UI控制,例如改变颜色,改变大小,使用自定义的图形代替,或者直接隐藏等,我们来看几个简单的案例。

案例1:小三角右侧显示同时颜色变淡

HTML代码如下:

<details class="details-1" open>

<summary>这是示例1</summary>
<content>本案例展示对小三角UI重定义:包括显示在右侧,颜色减淡等。</content>

</details>

CSS如下:

.details-1 summary {

width: -moz-fit-content;
width: fit-content;
direction: rtl;

}

.details-1 ::-webkit-details-marker {

direction: ltr;
color: gray;
margin-left: .5ch;

}

.details-1 ::-moz-list-bullet {

direction: ltr;
color: gray;
margin-left: .5ch;

}

结果如下图所示:

![三角位置和颜色示意](http://upload-images.jianshu.io/upload_images/13133049-94863e0d680fcef0.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)

当我们点击摘要标题升起的时候,表现为下图(截自Firefox):

![三角收起效果](http://upload-images.jianshu.io/upload_images/13133049-e585bee5dd43fcd9.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)

而实际上实际开发的时候,对小三角UI更便捷的定制方法是:隐藏浏览器原生的小三角,然后借助::before或::after伪元素重新生成我们想要的UI效果,下面这个案例就将展示相关的处理。

案例2:隐藏浏览器原生的小三角并使用自定义三角替换

HTML结构还是类似的:

<details class="details-2" open>

<summary>这是示例2</summary>
<content>本案例隐藏原生小三角,使用自定义小三角。</content>

</details>

CSS主要分为2部分,一部分是隐藏浏览器原生的小三角,另外一部分是使用伪元素生成自定义的三角效果。

首先看一下隐藏<details>标签默认的小三角的CSS:

/ 隐藏默认三角 /

.details-2 ::-webkit-details-marker {

display: none;

}

.details-2 ::-moz-list-bullet {

font-size: 0;

}

可以看到Chrome浏览器和Firefox浏览器的小三角隐藏采用的是不同的策略。在Chrome浏览器下,我们可以直接设置display:none进行隐藏,但是这一招在Firefox浏览器下确实没有效果的,即使设置display:none!important也是如此,根据我的测试,只有font-size:0能够比较完美的隐藏。类似position:absolute;visibility:hidden这种常见的隐藏也是不行的,因为position:absolute无法生效。

然后是自定义小三角显示的CSS,这里采用的是::after伪元素模拟的:

/ 自定义的三角 /

.details-2 summary::after {

content: '';
position: absolute;
width: 1em; height: 1em;
margin: .2em 0 0 .5ch;
background: url(./arrow-on.svg) no-repeat;
background-size: 100% 100%;
transition: transform .2s;

}

.details-2:not(open) summary::after {

margin-top: .25em;
transform: rotate(90deg);    

}

最终效果如下图所示:

![自定义三角效果截图](http://upload-images.jianshu.io/upload_images/13133049-a8a86d2f3dffbdab.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)

收起时候:

![收起时候效果](http://upload-images.jianshu.io/upload_images/13133049-ccd39e0f13291faa.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)



* * *

最后有一点需要注意一下,就是如果`<details>`标签内并没有`<summary>`元素,则我们的对三角的自定义代码都是无效的,可以使用一个空的`<summary>`元素占位,类似这样:

<details>

<summary></summary>
<content>内容。</content>

</details>

### 三、Chrome浏览器下点击时候outline轮廓等体验处理

UI可以定制了,但是还有个不容忽视的体验问题,那就是在Chrome浏览器下点击时候会出现`outline`轮廓,如下图所示:

![Chrome下的outline轮廓](http://upload-images.jianshu.io/upload_images/13133049-cedbfc4c9d2a79c9.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)

在实际项目开发的时候,产品和设计一定会让你把这个效果去掉的。以及,当我们`<summary>`元素点击较快的时候,文本会被选中,也不是我们想看到的。

阻止文本选中,我们可以:

summary {

-webkit-user-select: none;

-moz-user-select: none;

-ms-user-select: none;

user-select: none;

}

对于outline轮廓,比较直接的做法是:

summary {

outline: 0;

}

但是这样处理对无障碍访问而是非常不友好的,那有没有什么办法兼顾视觉体验和无障碍访问体验呢?

我的做法是这样子的:

1. 利用<a>标签的outline交互体验
浏览器对<a>标签元素的outline轮廓进行了专门的体验优化处理,鼠标点击的时候不显示轮廓,键盘访问时候显示轮廓。于是我们可采用李代桃僵策略,让<summary>元素的outline交给<a>元素,方法就是在<summary>中再内嵌一个<a>,同时通过tabindex属性remove掉<summary>原本的可访问性。HTML代码示意如下:

<details open>

<summary tabindex="-1"><a href="javascript:">这是示例</a></summary>
<content>点击无外框,键盘focus有。</content>

</details>

CSS如下:

summary {

user-select: none;
outline: 0;

}

summary a {

color: inherit;

}

此时,在Chrome浏览器下,我们点击摘要信息,没有任何`outline`轮廓出现;但是当我们使用Tab键索引时候,可以看到下图所示的轮廓效果:

![轮廓示意](http://upload-images.jianshu.io/upload_images/13133049-c02abcb5654dfc11.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)

轮廓区域比原生的`<summary>`要小,但这无伤大雅,而且实际项目开发的时候,我们会去掉小箭头,此时只要设置`<a>`标签`display:block`,则轮廓就可以和`<summary>`保持一致了。

接下来,我们按下Space空格键,就会发现`<details>`元素内的内容信息不断的展开与收起:

![收起状态](http://upload-images.jianshu.io/upload_images/13133049-e5acbfb877a7fba7.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)

然后上面实现并不完美,相比原生的<summary>元素,Enter回车键展开收起效果丢失了。这是因为HTML元素中如果多个focusable同时带click浏览器行为元素嵌套的时候,点击里面的元素,外部元素的浏览器行为是不会触发的。类似的有<label>内嵌<a>标签。

对于<a>标签,其浏览器行为只能通过回车键触发,空格键是无效的;但是对于<summary>,回车键和空格键都能触发展开收起行为,这就是为什么上面代码空格键有效,回车键无效的原因。

如果想要同时支持回车键展开与收起,可以对HTML如下处理:

<details open>

<summary tabindex="-1"><a href="javascript:" onClick="this.parentNode.click();">这是示例</a></summary>
<content>点击无外框,键盘focus有。</content>

</details>

需要注意的是上面处理在<summary>自己额外绑定click事件时候可能会有double触发的问题,此时,阻止<a>元素的冒泡即可。

2. JS捕获键盘行为手动设置outline
这个方法不需要对HTML进行任何的改动,是通过CSS和JS配合对全局的<summary>元素进行outline优化。

CSS如下:

summary {

user-select: none;
outline: 0;

}

summaryfocus {

outline: 1px dotted;
outline: 5px auto -webkit-focus-ring-color;

}

JS如下:

window.addEventListener('keydown', function () {

window.isKeyEvent = true;
setTimeout(function () {
    window.isKeyEvent = false;
}, 100);    

});

document.addEventListener('focusin', function (event) {

var target = event.target;
if (target && target.tagName.toLowerCase() == 'summary' && window.isKeyEvent == true) {
    target.setAttribute('focus', '');
}

});

document.addEventListener('focusout', function (event) {

var eleFocusAll = document.querySelectorAll('summary[focus]');
[].slice.call(eleFocusAll).forEach(function (summary) {
    summary.removeAttribute('focus');
});

});

只要把上面的CSS和JS复制到页面中,视觉体验和交互体验完美支持的<summary>元素outline效果就有了。

表现为,点击<summary>没有任何outline,键盘focus时候出现,且和浏览器原生outline效果一模一样,Space键和Enter键展开与收起访问完全保留。

例如下图就是键盘Tab键`focus`后回车后的效果:

![键盘访问回车收起后效果截图](http://upload-images.jianshu.io/upload_images/13133049-be5af49117098a2e.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)

每每看到如此极致的用户体验处理,心情都大好。

**原理:**
关键是全局监听`keydown`事件,如果有发生,则认为此100毫秒内的页面`focus`行为均是键盘产生,从而有效区分是点击触发的`focus`行为还是键盘触发的`focus`行为,如果是键盘触发,给`<summary>`元素手动增加`outline`效果。

### 四、基于details元素行为的各种交互效果案例

了解了`<details>`元素的点击交互行为;解决了UI定制难题;解决了`outline`的体验问题,下面我们就可以付诸实践,不借助任何JS来实现各种我们平常见到的交互效果。

#### 案例1:“更多”展开与收起效果

实现最终效果如下gif:

![更多展开收起gif效果](http://upload-images.jianshu.io/upload_images/13133049-d412f45518bc8d91.gif?imageMogr2/auto-orient/strip)

因为“更多”元素是在底部,因此效果实现的要点的所有的内容信息都放在`<summary>`元素内部,然后通过`<details>`元素的`open`属性控制UI的变化。

HTML和CSS代码如下,其中,最核心部分已经红色高亮:

<details>

<summary>
    <p>据台媒报道,大...青睐。</p>
    <div class="more">
        <p>其他几首歌曲...</p>
    </div>
    <a>更多</a>
</summary> 

</details>

::-webkit-details-marker {

display: none;

}

::-moz-list-bullet {

font-size: 0;
float: left;

}

.more {

display: none;

}

open .more {

display: block;

}

open summary a {

font-size: 0;

}

open summary a::before {

content: '收起';
font-size: 14px;

}

把“更多”对应的信息放在.more元素内,然后通过[open]属性选择器控制器显示,效果即达成。

#### 案例2:无JS实现点击显示悬浮菜单,自定义下拉框等效果

效果如下gif:

![下拉菜单gif效果](http://upload-images.jianshu.io/upload_images/13133049-5775c63eaa1c5d60.gif?imageMogr2/auto-orient/strip)

没有任何JS参与。HTML结构如下:

<details>

<summary>我的消息</summary> 
<div class="box">
    <a href>我的回答<sup>12</sup></a>
    <a href>我的私信</a>
    <a href>未评价订单<sup>2</sup></a>
    <a href>我的关注</a>
</div>

</details>

然后CSS让.box元素绝对定位即可,显示和隐藏<details>元素内置行为就搞定了。

#### 案例3:accordion多项折叠效果

此效果常见于条目比较多的垂直导航栏,新闻条目等。

例如下面实现的效果:

![多条目菜单展开与收起效果](http://upload-images.jianshu.io/upload_images/13133049-58a33307d77b9c9d.gif?imageMogr2/auto-orient/strip)

这个更加简单了,就是一堆`<details>`元素并排放置就可以了,如下HTML:

<details open>

<summary><dt>订单中心</dt></summary> 
<dd><a href>我的订单</a></dd>
<dd><a href>我的活动</a></dd>
<dd><a href>评价晒单</a></dd>
<dd><a href>购物助手</a></dd>

</details>

<details open>

<summary><dt>关注中心</dt></summary> 
<dd><a href>关注的商品</a></dd>
...

</details>

<details open>

...

</details>

计算CSS没有任何设置,效果也天然达成。

案例3中的展开项显示的时候是非常生硬的突然显示,实际上我们可以借助一些选择器技巧以及CSS3 `transition`属性让菜单展开收起的时候是有动画效果的,效果如下gif截图:

![含slideup/slidedown动画的展开与收起](http://upload-images.jianshu.io/upload_images/13133049-3cb428c5a850e450.gif?imageMogr2/auto-orient/strip)

此效果实现原理核心是`[open]`属性选择器,和加号`+`相邻兄弟选择器。

首先看下HTML,展开列表结构发生了变化,不是作为`<details>`的子元素,而是作为其相邻兄弟元素存在,HTML示意:

<details open><summary>订单中心</summary></details>

<dl>

<dd><a href>我的订单</a></dd>
<dd><a href>我的活动</a></dd>
<dd><a href>评价晒单</a></dd>
<dd><a href>购物助手</a></dd>

</dl>

...

上面<dl>定义列表就是展开收起的内容,其作为兄弟元素和<details>元素平起平坐,于是,我们就可以利用点击<summary>元素<details>元素的open属性会变化的特性实现我们想要的动画效果,CSS如下:

details + dl {

max-height: 0;
transition: max-height .25s;
overflow: hidden;

}

open + dl {

max-height: 100px;

}

借助相邻兄弟选择器以及max-height任意元素slideUp/slideDown技术就可以效果达成。

#### 案例5:多级嵌套的树形菜单交互效果

这里的树形菜单效果实现也很简单,多个`<details>`元素相互嵌套就可以,效果Gif如下:

![树形菜单展开gif效果截图](http://upload-images.jianshu.io/upload_images/13133049-bdecc3fcf1eb52f7.gif?imageMogr2/auto-orient/strip)

HTML结构大致如下:

<details>

<summary>我的视频</summary>
<details>
    <summary>爆肝工程师的异世界狂想曲</summary>
    <div>tv1-720p.mp4</div>
    <div>tv2-720p.mp4</div>
    ...
    <div>tv10-720p.mp4</div>
</details>
<details>
    <summary>七大罪</summary>
    <div>七大罪B站00合集.mp4</div>
</details>
<div>珍藏动漫网盘地址.txt</div>
<div>我们的小美好.mp4</div>

</details>

CSS的主要工作就是绘制菜单前面的加号和减号图形,例如我们可以借助background线性渐变,相关CSS如下:

details {

padding-left: 20px;

}

summary::before {

content: '';
display: inline-block;
width: 12px; height: 12px;
border: 1px solid #999;
background: linear-gradient(to right, #999, #999) no-repeat center, linear-gradient(to top, #999, #999) no-repeat center;
background-size: 2px 10px, 10px 2px;
vertical-align: -2px;
margin-right: 6px;
margin-left: -20px;

}

open > summary::before {

background: linear-gradient(to right, #999, #999) no-repeat center;
background-size: 10px 2px;

}

效果即达成!

五、如果只想要details/summary的语义不要行为

如果只想要<details>元素,<summary>元素的语义,但是并不需要点击展开收起的行为,该怎么处理呢?

例如,某评论,或者某帖子有标题和正文,非常符合详情-概要-内容的语义,但是希望是纯展示的,点击时候不收起,可以这么处理:

1.<summary>标签设置tabindex="-1"让键盘无法访问;
2.设置CSS:

summary {

outline: 0;

pointer-events: none;

}

这样就不能点,也不会有outline轮廓。

### 六、兼容性以及Polyfill

兼容性如下图:

![detailas兼容性](http://upload-images.jianshu.io/upload_images/13133049-abddc2ee6f73d984.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)

除了IE和Edge浏览器,大好河山一片绿,至少移动端可以用得比较开心。

如果想要在桌面web网页使用`<details>`元素的棒棒哒特性,我们可以对其进行Polyfill

对键盘访问,事件toggle都做了兼容。

如果开发策略是对不支持的IE进行特异处理,则下面的JS判断是否支持<details>元素的脚本可能对你有用:

```var isSupportDetails = 'open' in document.createElement('details');```

最后,无JS实现的好处有:

省了代码,加载快了;

实现更简单了,开发快了;

JS还没加载交互也能进行,体验好了;

键盘无障碍和aria阅读设备无障碍天然支持,体验档次高了。

这里推荐一下我的前端学习交流群:784783012 ,里面都是学习前端的,如果你想制作酷炫的网页,想学习知识。自己整理了一份2018最全面前端学习资料,从最基础的HTML+CSS+JS到移动端HTML5到各种框架的学习资料都有整理,送给每一位前端小伙伴,有想学习web前端的,或是转行,或是大学生,还有工作中想提升自己能力的,正在学习的小伙伴欢迎加入学习。

原创声明,本文系作者授权云+社区发表,未经许可,不得转载。

如有侵权,请联系 yunjia_community@tencent.com 删除。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏前端儿

细说移动端 经典的REM布局 与 新秀VW布局

说到前端页面的布局方案,可以从远古时代的Table布局说起,然后来到 DIV+CSS布局,之后有了Float布局,Flex布局,Column布局,Grid布局等...

2752
来自专栏数据小魔方

创意饼图的制作技巧——图标填充饼图!

今天给大家介绍一种创意饼图的制作技巧——图标填充饼图! 创意饼图 ▽ 觉得默认的饼图不够炫酷、不够养眼,木有逼格 没关系,今天就交给大家一种创意饼图的制作技...

38210
来自专栏一“技”之长

iOS开发CoreAnimation解读之一——初识CoreAnimation核心动画编程

        众所周知,绚丽动画效果是iOS系统的一大特点,通过UIView层封装的动画,基本已经可以满足我们应用开发的所有需求,但若需要更加自由的控制动画的...

2273
来自专栏Material Design组件

Material Design — 按钮( Buttons)

41316
来自专栏何俊林

Android TV开发总结(五)TV上屏幕适配总结

前言:前面几篇总结一些TV上的小Sample,开源到GitHub: https://github.com/hejunlin2013/TVSample, 点击”阅...

2879
来自专栏GIS讲堂

地图切片详解

地图切片很多做GIS的筒子都不陌生,但是其中的原理一直是马马虎虎,在此,就地图切片中的几个重要的概念做一个介绍,一则自己做个总结,二则希望能够帮助到有此困惑的战...

5422
来自专栏编程

小白必知什么是css和盒模型

CSS盒模型概念 CSS 盒模型也叫框模型 (Box Model) ,包含了元素内容(content)、内边距(padding,也叫填充)、边框(border)...

1987
来自专栏IMWeb前端团队

animation动画实践

由于业务关系,有幸参与腾讯课堂app下载页面制作,原则上ie8、9可表现为静态版本,而高级浏览则为动画版本。在这把一些重要思想和中间遇到的一些问题记录下,以供知...

2330
来自专栏前端知识分享

第131天:移动web页面的排版与布局

html{  font-size:1mm;  }  .titleheight{  height:10rem; //这里等于10mm  width:11rem; ...

2781
来自专栏九彩拼盘的叨叨叨

Web动画性能介绍

帧率(FPS):描述每秒播放的帧数,单位为 Hz 或者 frame/s (帧/秒)。

1063

扫码关注云+社区

领取腾讯云代金券