SMTC:纯CSS 星星打分评价

星星打分评价的页面功能,需求上经常会碰到。
网上搜一下,Js + CSS,纯 CSS 的版本也非常多。
觉得搜到的都不理想,下意识觉得应该能有更好的实现
自己动手尝试一下,果然能更好用
所以开篇文章,分享一下 ^_^

先来 HTML 代码感受下

<!-- 星星后面不需要文本的版本 -->
<!-- 不需要 label,不需要 label.for,不需要 input.id -->
<div class="star">
    <input type="radio" name="rdStar" value="" checked="checked"/>
    <input type="radio" name="rdStar" value="1"/>
    <input type="radio" name="rdStar" value="2"/>
    <input type="radio" name="rdStar" value="3"/>
    <input type="radio" name="rdStar" value="4"/>
    <input type="radio" name="rdStar" value="5"/>
</div>
<!-- 星星后面跟文本的版本 -->
<!-- 只需要 label,不需要 label.for,不需要 input.id -->
<div class="star">
    <input type="radio" name="rdStarLabel" checked="checked"/><label>请打分</label>
    <input type="radio" name="rdStarLabel" value="1"/><label>一星</label>
    <input type="radio" name="rdStarLabel" value="2"/><label>二星</label>
    <input type="radio" name="rdStarLabel" value="3"/><label>三星</label>
    <input type="radio" name="rdStarLabel" value="4"/><label>四星</label>
    <input type="radio" name="rdStarLabel" value="5"/><label>五星</label>
</div>

只讨论现代浏览器,非现代浏览器请出门左转

需要的 CSS 基础

总体没有用到很复杂或者罕见的 CSS 语法,对自己 CSS 水平有自信的可以跳过这一节

  • A ~ B MDN 文档:组合器和选择器组
    通用兄弟选择器:匹配B元素,满足条件:B是A之后的兄弟节点中的任意一个(AB有相同的父节点,B在A之后,但不一定是紧挨着A)
  • A + B MDN 文档:组合器和选择器组
    相邻兄弟选择器:匹配B元素,满足条件:B是A的下一个兄弟节点(AB有相同的父结点,并且B紧跟在A的后面)
  • E:hover MDN 文档::hover()
    鼠标悬停在元素上时提供的样式
  • E:checked MDN 文档::checked()
    处于选中状态的元素的样式,单选框<input type="radio" />,复选框<input type="checkbox" />,下拉选择select中的option
  • E:not(X) MDN 文档::not()
    匹配不符合参数选择器X描述的元素
  • Data Uri MDN 文档:Data URLs
    允许嵌入小文件(通常用来嵌入小图片,但不只是图片哦)

准备星星资源

为了 WYCIWYG,我没有用独立图片,通过 Data Uri 是嵌入小图片

.star input {background-image:url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAB4AAAA8CAYAAABxVAqfAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAyJpVFh0WE1MOmNvbS5hZG9iZS54bXAAAAAAADw/eHBhY2tldCBiZWdpbj0i77u/IiBpZD0iVzVNME1wQ2VoaUh6cmVTek5UY3prYzlkIj8+IDx4OnhtcG1ldGEgeG1sbnM6eD0iYWRvYmU6bnM6bWV0YS8iIHg6eG1wdGs9IkFkb2JlIFhNUCBDb3JlIDUuMy1jMDExIDY2LjE0NTY2MSwgMjAxMi8wMi8wNi0xNDo1NjoyNyAgICAgICAgIj4gPHJkZjpSREYgeG1sbnM6cmRmPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5LzAyLzIyLXJkZi1zeW50YXgtbnMjIj4gPHJkZjpEZXNjcmlwdGlvbiByZGY6YWJvdXQ9IiIgeG1sbnM6eG1wPSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvIiB4bWxuczp4bXBNTT0iaHR0cDovL25zLmFkb2JlLmNvbS94YXAvMS4wL21tLyIgeG1sbnM6c3RSZWY9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC9zVHlwZS9SZXNvdXJjZVJlZiMiIHhtcDpDcmVhdG9yVG9vbD0iQWRvYmUgUGhvdG9zaG9wIENTNiAoV2luZG93cykiIHhtcE1NOkluc3RhbmNlSUQ9InhtcC5paWQ6RjJFRDE5Qjg0NTYwMTFFOTk5QzM5RjY0NTY0MDc0MEUiIHhtcE1NOkRvY3VtZW50SUQ9InhtcC5kaWQ6RjJFRDE5Qjk0NTYwMTFFOTk5QzM5RjY0NTY0MDc0MEUiPiA8eG1wTU06RGVyaXZlZEZyb20gc3RSZWY6aW5zdGFuY2VJRD0ieG1wLmlpZDpGMkVEMTlCNjQ1NjAxMUU5OTlDMzlGNjQ1NjQwNzQwRSIgc3RSZWY6ZG9jdW1lbnRJRD0ieG1wLmRpZDpGMkVEMTlCNzQ1NjAxMUU5OTlDMzlGNjQ1NjQwNzQwRSIvPiA8L3JkZjpEZXNjcmlwdGlvbj4gPC9yZGY6UkRGPiA8L3g6eG1wbWV0YT4gPD94cGFja2V0IGVuZD0iciI/PtLQhvIAAAOnSURBVHja7FdbSBRRGHbKSizKzIzNagnSpBvkbmEPXSiK6Lb0EEJEkkUURNFD0IOvQdAVFgoRErqgQQ/RDYSEjKCLuz5EJElEkZRb2YpGaBe371/+ibPTnHXO3CRw4OPMnDlzvvnPOf/l01KpVM5/dYVisYPAIrvfazZJS9B0AZ+AQDwcHlKdY4zNHz7HbTGw0ReLYW0AzQehK8FWp7y2+IzheQaw3lOLYS2RdJu8+giUqFitavEpST8t/1pPLIa10/kUy673QNCq1SoWnxzm/WxgtS2LYRU95wNkXRBYACwD1gBzLcxH/hwHHgHtwCteiSRWYjCDGGTL0e7jvy3zMNj9Ap4BreQZRHwHN5t9jrgR2uPtwBMfSWux7Lc03ttcNPdVDofN6xhIT6f3WE+L4Xh8LJp7wAaPSA/HQqHo38Ml5mOQ09LfBLa6THoApHUZp9pYCDB5E7DDJdIakDb848dmFQjIae8vA7scku7EnjaacZgSa5pGB24cbn84IG0FKQWeHDOObCFzkkNrl9qN1XMcEk/Gqo23Q7zEhYNVZId4lQvE8+wQuxG/K5SIOYQGskxYD0zjtBnPMk5aleRK+gsl/TeAQ3CTBD9/JbfHj4bRXjNJq+ukhYAkgFSieSx0tQB7QfguS2mkcZK5SoWf8CoPkWvQqsVBbtuAahB2DLeZXGs9wA9QCbSFI18BuRXw2arFeWhmYrI3DrQVZbty4CUsTlkiHlWLo2pxpNXiJSb1XS1OBVZwgeibWoyCpBdts37A/FKLhSBOcj9Fqdt+qMULOilftNRf/FCLRSDuMXwX4XrcM7VYjwn2m2wDrVwPJwVHarGXB5CYew68Ztfpk+0hFw8FnBLLuMpcCVSaZMGIxvXzQx5A12JM/sLluH4EzXlBLZ4wU4vfgFK87HaJVN//TLVoyJ+6Whxg8i6HpFWsw9JqEfNFZadaVIskX+Zj8FubpHs4rKbVIuapyxpAmFxXi3QSF+KjTtVcTf6uq0V832ApcrE/62pxiH03aZF0E5q7olq0HLnYZWqEMb8VDP4pqMVGOyFTVIv9CsSdbqnFNsUkn3BLLTZL9nIWkG+yTQPsjo7V4lNjegSaOPj34/6oiWUtbqjFDiacAlzknFwlfH+Wf6Cag5BIXKFUCHAI1U9nObtVrUEtHud66wp9wv19wG5gAnCdCgQs/TYV4mLhkGRTi/p4mVr8jrETVYhdVYvGImBk1aLk791Si6VU/pjFgT8CDAD9y6CbITrTjwAAAABJRU5ErkJggg==');}

实际图片:SMTC:纯CSS 星星打分评价 宽 30 x 高 60,每个样子的图片 30 x 30

注意以下细节要求:(配合CSS)
1 小图片以列方式竖着依次保存
2 第一个是选中样子,第2个是未选中样子

* WYCIWYG :What You Copy Is What You Get

基本原理

Q:这个功能最重要最基础的部分是什么呢?
A:鼠标滑过,星星选中和取消

<style>
.star {
    font-size:30px;
}
.star input {
    display:block; /* 和 inline-block 的恶心留白说再见 */
    float:left; /* 多个星星排排坐,肩靠肩 */
    margin:0;padding:0; /* 重置 input 默认样式 */
    width:1em;height:1em; /* em 单位 棒!棒!棒! */
    font:inherit; /* 有这个 em 单位才能棒起来 */
    background:center 0/cover no-repeat; /* 准备好背景样式 */
    outline:0 none transparent; /* 再见! Chrome 的默认黄框框 */
    -webkit-appearance:none;-moz-appearance:none;appearance:none; /* 取消浏览器对 input 的默认渲染 */
}
.star input:hover ~ input {
    /*
    样式 .star input 把所有 input 元素 背景图 默认设为了选中样式
    当前 hover 的 input 元素之后的所有兄弟 input 元素,背景图调整为未选中样式
    -----------------
    default | ★★★★★
    hover   |     ✪
    style   | ★★★☆☆
    ---------------------
    Final   | ★★★☆☆
    */
    background-position:center -1em;
}
/*
 请在这里复制上面那段 data uri 的 CSS 哦,不然没有小星星
 因为有样式优先级,位置错了也可能没有小行星哦
*/
</style>
<div class="star">
    <input type="radio" name="rdStar" value="1"/>
    <input type="radio" name="rdStar" value="2"/>
    <input type="radio" name="rdStar" value="3"/>
    <input type="radio" name="rdStar" value="4"/>
    <input type="radio" name="rdStar" value="5"/>
</div>

鼠标滑过,星星变动一目了然

走过路过动图了解一下(更清楚的展示原理)
SMTC:纯CSS 星星打分评价

But:鼠标没放上去的时候是全选中的星星? [黑人问号???]
这部分只是基本原理,下面 实现详解 解决所有问题

实现详解

1 解决鼠标没放上去的时候是全选中的星星

在最前面增加一个 input ,表示为 `0星`,这个 `0星` input 可以隐藏不显示
为了演示,这个章节里不隐藏 `0星` input

2 增加 星星对应文本

每个 input 后面增加对应用 label 包起来的文本
input `float:left` label `float:right`

SMTC:纯CSS 星星打分评价

<style type="text/css">
.star {
    /* 基本原理 已有注释 */
    display:inline-block;height:1em;line-height:1em;font-size:30px;
}
.star input {
    /* 基本原理 已有注释 */
    display:block;float:left;margin:0;padding:0;width:1em;height:1em;font:inherit;background:center 0/cover no-repeat;outline:0 none transparent;
    -webkit-appearance:none;
    -moz-appearance:none;
    appearance:none;
}
.star input:first-child {
    /* 为了演示效果,不隐藏,但恢复浏览器渲染 */
    -webkit-appearance:radio;
    -moz-appearance:radio;
    appearance:radio;
}

.star input:checked ~ input {
    /* Style [C]
    已选中的 input 元素之后的所有兄弟 input 元素,背景图调整为未选中样式
    ---------------------
    default   | ★★★★★
    checked   |   ✪
    style [C] | ★★☆☆☆
    ---------------------
    Final     | ★★☆☆☆
    */
    background-position:center -1em;
}
.star:hover input:checked ~ input {
    /* Style [HC]
    鼠标悬浮在 star 元素上时,已选中的 input 元素之后的所有兄弟 input 元素,背景图调整为选中样子
    主要作用,覆盖上一条,将 input 元素 背景图全部恢复到 选中样子
    -----------------
    default    | ★★★★★
    checked    |   ✪
    style [C]  | ★★☆☆☆
    hover      |       ✪
    style [HC] | ★★★★★
    ---------------------
    Final      | ★★★★★
    */
    background-position:center 0;
}
.star:hover input:hover ~ input {
    /* Style [HH]
    基本原理 已有注释
    -----------------
    default    | ★★★★★
    checked    |   ✪
    style [C]  | ★★☆☆☆
    hover      |       ✪
    style [HC] | ★★★★★
    style [HH] | ★★★★☆
    ---------------------
    Final      | ★★★★☆
    */
    background-position:center -1em;
}

/* label 部分参照上面 inpu 的样式,试着自己理解 */
.star label {display:none;float:right;}
.star input:checked + label {display:block;}
.star:hover input:checked + label {display:none;}
.star:hover input:hover + label {display:block;}

/*
 请在这里复制上面那段 data uri 的 CSS 哦,不然没有小星星
 因为有样式优先级,位置错了也可能没有小行星哦
*/
</style>
<div class="star">
    <input type="radio" name="rdStar" checked="checked"/>
    <input type="radio" name="rdStar" value="1"/>
    <input type="radio" name="rdStar" value="2"/>
    <input type="radio" name="rdStar" value="3"/>
    <input type="radio" name="rdStar" value="4"/>
    <input type="radio" name="rdStar" value="5"/>
</div>
<br />
<div class="star">
    <input type="radio" name="rdStarLabel" checked="checked"/><label>请打分</label>
    <input type="radio" name="rdStarLabel" value="1"/><label>一星</label>
    <input type="radio" name="rdStarLabel" value="2"/><label>二星</label>
    <input type="radio" name="rdStarLabel" value="3"/><label>三星</label>
    <input type="radio" name="rdStarLabel" value="4"/><label>四星</label>
    <input type="radio" name="rdStarLabel" value="5"/><label>五星</label>
</div>

More 6666 ~~~

更多选择,更多欢乐 :)

1 只能选一次,选完之后不能重新选择

/* pointer-events 请自行搜索了解 */
.star-once input:first-child:not(:checked) ~ input {background-position:center 0;pointer-events:none;}
.star-once input:not(:first-child):checked ~ input {background-position:center -1em;}

.star-once input:first-child:not(:checked) ~ label {display:none;}
.star-once input:not(:first-child):checked + label {display:block;}

2 已有默认选择,锁定只读,不能更改,用来展示

/* pointer-events 请自行搜索了解 */
.star-lock {pointer-events:none;}

3 多种尺寸只要修改 star 上的字体大小

/*
  除了 star 其他都是 em 单位,背景图用了 cover
  自由缩放,轻松愉快哦
*/
.star {font-size:50px;}

SMTC:纯CSS 星星打分评价

完整代码

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Star</title>
    <style type="text/css">

    .star {position:relative;display:inline-block;line-height:1em;}
    .star input {display:block;float:left;margin:0;padding:0;width:1em;height:1em;font:inherit;background:center 0/cover no-repeat;outline:0 none transparent;
        -webkit-appearance:none;
        -moz-appearance:none;
        appearance:none;
    }
    .star label {display:none;float:right;margin-left:0.5em;}
    .star input:first-child {
        display:none;
        /* -webkit-appearance:radio;-moz-appearance:radio;appearance:radio; */
    }

    .star input:checked ~ input {background-position:center -1em;}
    .star:hover input:checked ~ input {background-position:center 0;}
    .star:hover input:hover ~ input {background-position:center -1em;}

    .star input:checked + label {display:block;}
    .star:hover input:checked + label {display:none;}
    .star:hover input:hover + label {display:block;}

    .star-once input:first-child:not(:checked) ~ input {background-position:center 0;pointer-events:none;}
    .star-once input:not(:first-child):checked ~ input {background-position:center -1em;}

    .star-once input:first-child:not(:checked) ~ label {display:none;}
    .star-once input:not(:first-child):checked + label {display:block;}

    .star-lock {pointer-events:none;}

    /*
     以上的 css 可写入统一样式表
     以下的样式 可在用的地方分别处理,自定义 大小,星星图片
     */
    .star {font-size:30px;}
    .star input {background-image:url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAB4AAAA8CAYAAABxVAqfAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAyJpVFh0WE1MOmNvbS5hZG9iZS54bXAAAAAAADw/eHBhY2tldCBiZWdpbj0i77u/IiBpZD0iVzVNME1wQ2VoaUh6cmVTek5UY3prYzlkIj8+IDx4OnhtcG1ldGEgeG1sbnM6eD0iYWRvYmU6bnM6bWV0YS8iIHg6eG1wdGs9IkFkb2JlIFhNUCBDb3JlIDUuMy1jMDExIDY2LjE0NTY2MSwgMjAxMi8wMi8wNi0xNDo1NjoyNyAgICAgICAgIj4gPHJkZjpSREYgeG1sbnM6cmRmPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5LzAyLzIyLXJkZi1zeW50YXgtbnMjIj4gPHJkZjpEZXNjcmlwdGlvbiByZGY6YWJvdXQ9IiIgeG1sbnM6eG1wPSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvIiB4bWxuczp4bXBNTT0iaHR0cDovL25zLmFkb2JlLmNvbS94YXAvMS4wL21tLyIgeG1sbnM6c3RSZWY9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC9zVHlwZS9SZXNvdXJjZVJlZiMiIHhtcDpDcmVhdG9yVG9vbD0iQWRvYmUgUGhvdG9zaG9wIENTNiAoV2luZG93cykiIHhtcE1NOkluc3RhbmNlSUQ9InhtcC5paWQ6RjJFRDE5Qjg0NTYwMTFFOTk5QzM5RjY0NTY0MDc0MEUiIHhtcE1NOkRvY3VtZW50SUQ9InhtcC5kaWQ6RjJFRDE5Qjk0NTYwMTFFOTk5QzM5RjY0NTY0MDc0MEUiPiA8eG1wTU06RGVyaXZlZEZyb20gc3RSZWY6aW5zdGFuY2VJRD0ieG1wLmlpZDpGMkVEMTlCNjQ1NjAxMUU5OTlDMzlGNjQ1NjQwNzQwRSIgc3RSZWY6ZG9jdW1lbnRJRD0ieG1wLmRpZDpGMkVEMTlCNzQ1NjAxMUU5OTlDMzlGNjQ1NjQwNzQwRSIvPiA8L3JkZjpEZXNjcmlwdGlvbj4gPC9yZGY6UkRGPiA8L3g6eG1wbWV0YT4gPD94cGFja2V0IGVuZD0iciI/PtLQhvIAAAOnSURBVHja7FdbSBRRGHbKSizKzIzNagnSpBvkbmEPXSiK6Lb0EEJEkkUURNFD0IOvQdAVFgoRErqgQQ/RDYSEjKCLuz5EJElEkZRb2YpGaBe371/+ibPTnHXO3CRw4OPMnDlzvvnPOf/l01KpVM5/dYVisYPAIrvfazZJS9B0AZ+AQDwcHlKdY4zNHz7HbTGw0ReLYW0AzQehK8FWp7y2+IzheQaw3lOLYS2RdJu8+giUqFitavEpST8t/1pPLIa10/kUy673QNCq1SoWnxzm/WxgtS2LYRU95wNkXRBYACwD1gBzLcxH/hwHHgHtwCteiSRWYjCDGGTL0e7jvy3zMNj9Ap4BreQZRHwHN5t9jrgR2uPtwBMfSWux7Lc03ttcNPdVDofN6xhIT6f3WE+L4Xh8LJp7wAaPSA/HQqHo38Ml5mOQ09LfBLa6THoApHUZp9pYCDB5E7DDJdIakDb848dmFQjIae8vA7scku7EnjaacZgSa5pGB24cbn84IG0FKQWeHDOObCFzkkNrl9qN1XMcEk/Gqo23Q7zEhYNVZId4lQvE8+wQuxG/K5SIOYQGskxYD0zjtBnPMk5aleRK+gsl/TeAQ3CTBD9/JbfHj4bRXjNJq+ukhYAkgFSieSx0tQB7QfguS2mkcZK5SoWf8CoPkWvQqsVBbtuAahB2DLeZXGs9wA9QCbSFI18BuRXw2arFeWhmYrI3DrQVZbty4CUsTlkiHlWLo2pxpNXiJSb1XS1OBVZwgeibWoyCpBdts37A/FKLhSBOcj9Fqdt+qMULOilftNRf/FCLRSDuMXwX4XrcM7VYjwn2m2wDrVwPJwVHarGXB5CYew68Ztfpk+0hFw8FnBLLuMpcCVSaZMGIxvXzQx5A12JM/sLluH4EzXlBLZ4wU4vfgFK87HaJVN//TLVoyJ+6Whxg8i6HpFWsw9JqEfNFZadaVIskX+Zj8FubpHs4rKbVIuapyxpAmFxXi3QSF+KjTtVcTf6uq0V832ApcrE/62pxiH03aZF0E5q7olq0HLnYZWqEMb8VDP4pqMVGOyFTVIv9CsSdbqnFNsUkn3BLLTZL9nIWkG+yTQPsjo7V4lNjegSaOPj34/6oiWUtbqjFDiacAlzknFwlfH+Wf6Cag5BIXKFUCHAI1U9nObtVrUEtHud66wp9wv19wG5gAnCdCgQs/TYV4mLhkGRTi/p4mVr8jrETVYhdVYvGImBk1aLk791Si6VU/pjFgT8CDAD9y6CbITrTjwAAAABJRU5ErkJggg==');}
    </style>
</head>
<body>
star&ensp;
<div class="star">
    <input type="radio" name="rdStar" checked="checked"/><label>请打分</label>
    <input type="radio" name="rdStar" value="1"/><label>一星</label>
    <input type="radio" name="rdStar" value="2"/><label>二星</label>
    <input type="radio" name="rdStar" value="3"/><label>三星</label>
    <input type="radio" name="rdStar" value="4"/><label>四星</label>
    <input type="radio" name="rdStar" value="5"/><label>五星</label>
</div>
<br />
once&ensp;
<div class="star star-once">
    <input type="radio" name="rdStarOnce" checked="checked"/><label>请打分</label>
    <input type="radio" name="rdStarOnce" value="1"/><label>一星</label>
    <input type="radio" name="rdStarOnce" value="2"/><label>二星</label>
    <input type="radio" name="rdStarOnce" value="3"/><label>三星</label>
    <input type="radio" name="rdStarOnce" value="4"/><label>四星</label>
    <input type="radio" name="rdStarOnce" value="5"/><label>五星</label>
</div>
<br />
lock&ensp;
<div class="star star-lock">
    <input type="radio" name="rdStarLock"/><label>请打分</label>
    <input type="radio" name="rdStarLock" value="1"/><label>一星</label>
    <input type="radio" name="rdStarLock" value="2"/><label>二星</label>
    <input type="radio" name="rdStarLock" value="3" checked="checked"/><label>三星</label>
    <input type="radio" name="rdStarLock" value="4"/><label>四星</label>
    <input type="radio" name="rdStarLock" value="5"/><label>五星</label>
</div>
<br />
<div class="star">
    <input type="radio" name="rdStarX" checked="checked"/>
    <input type="radio" name="rdStarX" value="1"/>
    <input type="radio" name="rdStarX" value="2"/>
    <input type="radio" name="rdStarX" value="3"/>
    <input type="radio" name="rdStarX" value="4"/>
    <input type="radio" name="rdStarX" value="5"/>
</div>
</body>
</html>

相关推荐