本文作者:张敏
原创声明:本文为阅文前端团队 YFE 成员出品,请尊重原创,转载请联系公众号 (id: yuewen_YFE) 获取授权,并注明作者、出处和链接。 这是一个不断探究再探究的小故事。
为啥会有用css实现评星想法?
像上面这种评星是一种很常见的需求,我原来是借助JS实现的,没有遇到多大的挑战。
后来张老师在code review
我这段代码的时候,顺便提了一嘴type="range"
应该可以实现,当时的我想破脑袋也想不出来,脑子是蒙的。
因为是自己写这种评星的功能,所以封装了一下,方便下次使用,就按照日常的习惯写了篇博客js 实现评分组件,事情本来就这么过去了,可是后来同事文彬无意间看到了我的博客,觉得我的实现方式不优雅,和我聊了几句:
后来我根据文彬的建议,继续优化我的代码,我很开心的和他说我把代码进行了优化,效果很不错,于是我把优化以后的代码发给他看。可他却用纯css
实现了,真的是道高一尺魔高一丈。
震惊!居然就是张老师一开始顺嘴一提的type="range"实现的。这也太厉害了吧,只有你想不到,没有你实现不了的。我们先看看来实现效果
如何 type="range" 用评星功能
input type="range" 只是一个简单的元素吗?
不是的,他是一个组件,在DOM结构里面有一种特殊的 DOM 叫shadow DOM
通过浏览器控制台可以发现原来input type="range"
元素里面是包含了其他的元素的,只是平常的时候我们见不着,他是被隐藏起来,没有设置特定的选项,我们看不见他。
那我们怎么让他显示出来???
可以在 Chrome 中开启shadow-dom的显示,方式为在控制台右上角setting > preferences > Elements,勾选Show user agent shadow DOM(已经勾选过的可以忽略)
打开那个控制开关之后,我们就发现input type="range"
标签下面有一个shadow-root
节点。节点里面就有很多的DOM
节点,再一次证实了input type="range"
并不是一个标签,而是一个组件,只是这个组件不容易被发现。
继续探究如何自定义input type="range"
的样式
在元素审查的时候,我们发现了一个东西
既然可以定位到dom,还能看到样式,那修改他是不是有效果呢?试一试,或许就成功了呢。
咦,成功了耶!
原来那两个伪元素是可以用来修改元素的样式:
::-webkit-slider-runnable-track:表示其在type为range的input标签内中的滑块凹槽,也就是滑块可滑动的区域(简单说就是滑块凹槽区域的样式)
::-webkit-slider-thumb:这是type为range的input标签内的一种伪类样式,用于设置range的滑块的具体样式,该伪类只在内核为webkit/blink的浏览器中有效(简单说就是滑块区域的剩下的部分)
接下来的问题就是,如何把星星当背景且数量可控制的覆盖上去呢?
脑子灵感一闪客栈说书:CSS遮罩CSS3 mask/masks详细介绍里面提到了把图案遮罩在背景之上,尝试一下嘛,说不定有收获呢。
到此完美实现input type="range"
的评星功能,并且图片颜色随着背景的变化而变化 => 图案颜色随着背景颜色自由变化
核心代码:
input[type="range"]{
-webkit-appearance: none; //去掉浏览器自带的样式
width: 100px;
margin: 0;
outline: 0;
}
input[type="range" i]::-webkit-slider-runnable-track {
background: coral;// 设置五角星的颜色
height: 20px;
-webkit-mask: url("data:image/svg+xml,%3Csvg width='12' height='11' viewBox='0 0 12 11' fill='none' xmlns='http://www.w3.org/2000/svg'%3E%3Cpath d='M6 0l1.693 3.67 4.013.476L8.74 6.89l.788 3.964L6 8.88l-3.527 1.974.788-3.964L.294 4.146l4.013-.476L6 0z' fill='%23F67600'/%3E%3C/svg%3E"); // 遮罩的图案,可以任意定制
-webkit-mask-size: 20px;// 遮罩图片的大小
-webkit-mask-repeat: repeat-x;
}
input[type="range" i]::-webkit-slider-thumb {
-webkit-appearance: none;
width: 0;
height: 100%;
box-shadow: 999px 0px 0px 999px #E8EAED;
}
纯css
实现了,评星功能,代码量少,且灵活。
兼容性怎么样?还能再继续完善吗?
只要兼容input type="range"
应该都可以实现。
在测试火狐浏览器的时候,发现在这个评星挂了。
我继续和文彬讨论,如何解决这个兼容性问题:
核心实现代码:
input[type=range]{
-webkit-mask: url("data:image/svg+xml,%3Csvg width='12' height='11' viewBox='0 0 12 11' fill='none' xmlns='http://www.w3.org/2000/svg'%3E%3Cpath d='M6 0l1.693 3.67 4.013.476L8.74 6.89l.788 3.964L6 8.88l-3.527 1.974.788-3.964L.294 4.146l4.013-.476L6 0z' fill='%23F67600'/%3E%3C/svg%3E");
-webkit-mask-size: 20px;
-webkit-mask-repeat: repeat-x;
height: 20px;
}
input[type=range]::-moz-range-track{
background: #E8EAED;
height: inherit;
}
input[type=range]::-moz-range-progress {
background: coral;
height: inherit;
}
input[type=range]::-moz-range-thumb {
width: 0;
opacity: 0;
}
冰冻三尺非一日之寒,用input type="range"
实现这个功能并非一朝一夕,没有一番苦功夫,是不可能的。
这个实现很不错,于是考虑是否可以加到开源LuLu UI中去,但是在开源 UI 里面考虑的情况就多了很多,比如:如果不是5颗星星,用户要自定义xx个星星,用户怎么改?评分范围不是0-5,而是0-100...遇到的难题不少,差点我就放弃了,多亏了同事的继续指点:
整合后的最新代码,就期待最新主题的LuLu UI吧 ,预计年中对外,哈哈!
总结
我的同事们脑子里装一定是的百科全书,仿佛就没有他们不知道的东西。好几次都找同事们,帮忙看问题解决问题,他们的脑袋转悠转悠,贼快的给出了我要的答案,那速度比谷歌还快。真开心,真羡慕自己能够拥有一群这样的大佬同事。以后做需求写功能都可以像他们取经,是否有更好的实现方式,交流讨论,然后成长,选择比努力更重要。
年底了,你们不用羡慕我,你值得拥有,来试一试我公司吧,先了解一下团队的日常活动,每个月都有活动哟,快来和我一起尽情工作、拼命玩。
无论你的技术栈偏前还是偏后,都有合适你的位置,可以年前面试年后入职,欢迎简历至 zhangmin.a@yuewen.com,
工作地点:上海浦东微电子港,本科以上学历,2年以上工作经验,简历必回。