前言你有没有被业务要求“改变这个单选按钮的颜色!让它与主题颜色相匹配!”,然后苦于原来的颜色不支持改变,最后被迫临时使用它。如果抛开input[type=radio],重新开发一个,你会发现模拟选中、未选中、不可用状态很麻烦,遇到单选按钮组就更烦了。实际上,我们可以使用label、::before、and:checked和tabindex,然后加入少量的JavaScript脚本来模拟一个更丰富的“原生”单选按钮。让我们一起来试试吧!了解一下单选按钮——既然我们的目标是改变单选按钮的颜色,其他外观特征和行为都与原来的单选按钮保持一致,那么我们首先要了解单选按钮的主要外观特征和行为有哪些。一、外观特点1.1.normalstylemargin:3px3px0px5px;border:none0;padding:0;box-sizing:border-box;display:inline-block;line-height:normal;position:static;注:外观方面,我们必须确保布局特征与原始特征一致。否则,更换自定义单选按钮很可能会影响整体布局。最终,我们将被迫调整其他元素的布局特征,以达到整体协调,从而扩大修改范围。1.2.焦点样式outline-offset:0px;大纲:-webkit-focu-ring-colorauto5px;注意:此处的焦点样式仅通过键盘Tab键生效。获得焦点,但是上面的样式没有生效。1.3.样式颜色设置为禁用:rgb(84,84,84);2.行为特征单选按钮的行为特征有明显的选中与否,以及选中状态的变化事件,所以我们必须不停地向外界提供变化事件。另外值得注意的是,在按键盘上的Tab键选中单选框后,再按Space键会选中单选框。有了以上的认识,我们就可以开始码字了!废话少说,上面代码左边是原生单选按钮,右边是我们自定义的单选按钮。从上到下,样式为未选中、选中、聚焦和禁用。CSSpartlabel.radio{/*确保布局特征保持一致*/margin:3px3px0px5px;显示:内联块;框大小:边框框;宽度:12px;高度:12px;}.radio__appearance{显示:块;/*如果设置为block,则不会受到vertical-align的影响,所以不会意外影响.radio的linebox高度*/position:relative;盒子阴影:0001px番茄;/*box-shadow不像border那样会影响框的边框高度*/border-radius:50%;高度:90%;宽度:90%;text-align:center;}label.radio[type=radio]+.radio__appearance::before{content:"";显示:块;边界半径:50%;宽度:85%;身高:85%;位置:绝对;顶部:50%;左:50%;转换:翻译(-50%,-50%);过渡:背景.3s;}label.radio[type=radio]:checked+.radio__appearance::before{background:tomato;}label.radio[type=radio][disabled]+.radio__appearance{opacity:.5;}label.radio:focus{outline-offset:0px;outline:#999auto5px;}/*当鼠标点击获得焦点时,轮廓效果不会生效*/label.radio.clicked{outline:none0;}/*自定义单选按钮的行为主要是基于原生radiobutton,所以先隐藏原生radiobutton藏文*/label.radioinput{display:none;}HTML部分
JavaScript部分varradios=document.querySelectorAll(".radio")radios.forEach(radio=>{//在模拟鼠标点击之后:焦点样式无效{tar.classList.remove("clicked")clearInterval(fp)}},400)})//模拟通过键盘获得焦点后,按下`Space`键执行选择操作makeradio.addEventListener("keydown",e=>{if(e.keyCode===32){e.target.click()}})})这个实现有3个注意点:鼠标点击事件通过将标签添加到关联的输入[type=radio],这样你就可以安全地隐藏单选按钮并使用单选按钮自身的特性。但是由于label控件本身的限制,如果默认不是可聚焦元素,则无法将键盘按键事件传递给radiobutton。Box,即使加入了tabindex功能,也需要手写JS实现;当tabindex大于等于0时,表示该元素可以获得焦点;为0时表示获取焦点的顺序按照元素的位置排列;重点;由于radiobox的显示是inline-block,所以radiobox会影响linebox的高度。当自定义radio框内的元素使用inline-block时,vertical-align设置稍有不慎,会导致内层元素所在的linebox抬高,导致自定义radio所在的linebox高度箱子所在的位置变大了。所以这里采用设置内部元素显示为block的方式,直接让vertical-align失效,提高可控性。通过opacity:0实现(2018/10/5添加)上面我们将display:noneinput[type=radio]和label关联起来,通过input[type=radio]来简化自定义单选按钮的实现,但还是需要写JS有没有别的方法可以实现按空格键选择的行为特征?我们只是不想让用户看到原生单选按钮,所以将其设置为opacity:0还不够吗?!CSSpart.radio{/*确保布局特征保持一致*/margin:3px3px0px5px;显示:内联块;框大小:边框框;宽度:13px;height:13px;}/*自定义radiobox的行为主要是基于原生的radiobutton,所以首先让原生的radiobutton透明覆盖整个父元素*/.radioinput{opacity:0;位置:绝对;z-索引:1;/*需要Overlayon.radio__appearance来响应鼠标事件*/width:100%;高度:100%;}.radio__container-box{位置:相对;宽度:100%;高度:100%;}.radio__appearance{显示:块;/*如果设置为block,则不会受到vertical-align的影响,所以不会意外影响.radio的linebox高度*/position:relative;盒子阴影:0001px番茄;/*box-shadow不影响像border框的边框高度*/border-radius:50%;高度:90%;宽度:90%;text-align:center;}.radio[type=radio]+.radio__appearance::before{content:"";显示:块;边界半径:50%;宽度:85%;身高:85%;n:绝对的;顶部:50%;左:50%;转换:翻译(-50%,-50%);过渡:背景.3s;}.radio[type=radio]:checked+.radio__appearance::before{background:tomato;}.radio[type=radio][disabled]+.radio__appearance{opacity:.5;}.radio:focus-within.radio__appearance{outline-offset:0px;outline:#999auto5px;}/*当鼠标点击获得焦点时,轮廓效果不生效*/.radio.clicked.radio_appearance{outline:none0;}HTML部分
JavaScript部分varradios=document.querySelectorAll(".radio")radios.forEach(radio=>{//模拟鼠标点击后:焦点样式无效radio.addEventListener("mousedown",e=>{vartar=e.currentTargettar.classList.add("clicked")varfp=setInterval(function(){if(!tar.contains(document.activeElement){tar.classList.remove("clicked")clearInterval(fp)}},400)})})总结我们可以稍微修改复选框,然后通过VUE、React等框架稍微封装,提供更简单的API,使用起来更方便阿伯丁约翰