首页 > 编程笔记

CSS :active伪类选择器用法详解

CSS :active 伪类可以用于设置元素激活状态的样式,可以通过点击鼠标主键或者用手指或者触控笔点击触摸屏触发激活状态。具体表现如下,按下触发 :active 伪类样式,抬起取消 :active 伪类样式的应用。

:active 伪类支持任意 HTML 元素,例如 <div>、<span> 等非控件元素,甚至是自定义元素:
p:active {
   background-color: skyblue;
}
x-element:active {
   background-color: teal;
}
然而,落地实现时 :active 伪类并没有理论上那么完美,表现为以下 3 点。

1) IE 浏览器下 :active 样式的应用是无法冒泡的,例如:
img:active {
   outline: 30px solid #ccc;
}
p:active {
   background-color: teal;
}
<p><img src="1.jpg"></p>
此时,点击 <img> 元素的时候,在 IE 浏览器下,<p> 元素是不会触发 :active 伪类样式的,实际上祖先元素的 :active 样式也应当被应用;在 Chrome 和 Firefox 等浏览器下,其表现符合预期。

2) 在 IE 浏览器下,<html>、<body> 元素应用 :active 伪类设置背景色后,背景色是无法还原的。具体来说,鼠标键按下时确实应用了 :active 设置的背景色,但是抬起后背景色没有还原,而且此时无论怎么点击鼠标,背景色都无法还原。这是一个很奇怪的 bug,普通元素不会有此问题,这个问题甚至比在 IE7 浏览器下链接元素必须失焦才能取消 :active 样式还要糟糕。
/* IE浏览器下以下:active背景色样式一旦应用就无法还原 */
body:active { background-color: gray; }
html:active { background-color: gray }
:root:active { background-color: gray; }
但是其他一些 CSS 属性表现正常,例如:
/* IE浏览器下以下:active样式正常 */
body:active { color: red; }
html:active { color: red; }
:root:active { color: red; }

3) 移动端 Safari 浏览器下,:active 伪类默认是无效的,需要设置任意的 touch 事件才能支持。我们可以添加如下一行 JavaScript 代码:
document.body.addEventListener('touchstart', function() {});
此问题在 Safari1 浏览器 16.3 版本下依然存在,当然,可能在之后的版本中会有所优化。就好比在之前的 Safari 浏览器中,:active 样式应用的时机有些问题,但是最近几个大版本中已经优化了。

因此,推荐在 Safari 浏览器下使用原生的 -webkit-tap-highlight-color 属性实现触摸反馈就过时了:
body {
   -webkit-tap-highlight-color: rgba(0,0,0,.05);
}
现在更推荐使用 :active 伪类实现点击反馈效果,因为同一套 CSS 适用于所有浏览器,包括桌面端浏览器。
body {
   -webkit-tap-highlight-color: transparent;
}
.cs-element:active {
    /* 点击反馈 */
}
另外,键盘访问无法触发 :active 伪类。例如,<a> 元素在 focus 状态下按下 Enter 键的事件行为与点击一致,但是,不会触发 :active 伪类。

最后,:active 伪类的主要作用是反馈点击交互,让用户知道他的点击行为已经成功触发,这对于按钮和链接元素是必不可少的,否则会有体验问题。由于 :active 伪类作用在按下的那一段时间,因此不适合用来实现复杂交互。

按钮的通用:active样式技巧

本技巧更适用的场景是移动端开发,因为桌面端可以通过 :hover 反馈状态变化,而移动端只能通过 :active 反馈。一个移动端项目会有非常多需要点击反馈的链接和按钮,如果对每一个元素都设置 :active 样式,成本还是挺高的。这里介绍几个通用处理技巧,希望可以借此节约大家的开发时间。

一种是使用 box-shadow 内阴影,例如:
[href]:active,
button:active {
   box-shadow: inset 0 0 0 999px rgba(0,0,0,.05);
}
这种方法的优点是可以兼容 IE9 浏览器,缺点是对非对称闭合元素无能为力,例如 <input> 按钮:
<!-- 内阴影无效 -->
<input type="reset" value="重置">
<input type="button" value="按钮">
<input type="submit" value="提交">

另一种方法是使用 linear-gradient 线性渐变,例如:
[href]:active,
button:active {
   background-image: linear-gradient(rgba(0,0,0,.05), rgba(0,0,0,.05));
}
这种方法的优点是对 <input> 按钮这类非对称闭合元素有效,缺点是 CSS 渐变从 IE10 浏览器才开始支持,如果你的项目仍需要兼容 IE9 浏览器,就会有一定的限制。

最后再介绍一种在特殊场景下使用的方法。有时候,链接元素包裹的是一张图片,如下:
<a href><img src="1.jpg"></a>
如果 <a> 元素四周没有 padding 留白,则此时上面两种通用技巧都无效,因为 :active 样式被图片挡住了。

对此,不少人会想到使用 ::before 伪元素在图片上覆盖一层半透明色来模拟 :active 效果,但这种方法对父元素有依赖,无法作为通用样式使用,此时,可以试试 outline,如下:
[href] > img:only-child:active {
   outline: 999px solid rgba(0,0,0,.05);
   outline-offset: -999px;
   clip-path: polygon(0 0, 100% 0, 100% 100%, 0 100%);
}
这种方法的优点是 CSS 的冲突概率极低,对非对称闭合元素有效。缺点是不适合需要兼容 IE 浏览器的产品,因为虽然 IE8 浏览器就已经支持 outline 属性,但是 outline-offset 从 Edge 15 才开始被支持。

另外还有一个缺点是,outline 模拟的反馈浮层并不是位于元素的底层,而是位于元素的上层,且可以被绝对定位子元素穿透,因此不适合用在包含复杂 DOM 信息的元素中,但是特别适用于图片这类单一元素。

总结一下,outline 实现 :active 反馈适合移动端,适合图片元素。

另外,还可以使用 border-image 属性代替 background-image 属性绘制渐变进行模拟。不过 border-image 属性的语法有些复杂,且会影响正常边框的显示,因此这里不展开介绍。

在实际开发中,大家可以根据自己的需求组合使用上面的几个技巧,以确保所有的控件元素都有点击反馈。例如:
body {
   -webkit-tap-highlight-color: rgba(0,0,0,0);
}
[href]:active,
button:active {
   background-image: linear-gradient(rgba(0,0,0,.05), rgba(0,0,0,.05));
}
[href] img:active {
   outline: 999px solid rgba(0,0,0,.05);
   outline-offset: -999px;
   clip-path: polygon(0 0, 100% 0, 100% 100%, 0 100%);
}
还有一招,就是<img>元素的点击反馈,我还经常使用 filter 滤镜属性实现,这种方法简单、快捷,效果很不错。
img:active {
   filter: brightness(0.95);
}

:active伪类与CSS数据上报

如果想要知道两个按钮的点击率,CSS 开发人员可以自己动手,无须劳烦 JavaScript 开发人员去埋点:
.button-1:active::after {
   content: url(./pixel.gif?action=click&id=button1);
   display: none;
}
.button-2:active::after {
   content: url(./pixel.gif?action=click&id=button2);
   display: none;
}
此时,点击按钮,相关行为数据就会上报给服务器,这种上报,就算禁用 JavaScript 也无法阻止,方便快捷,特别适合 A/B 测试。

推荐阅读