單選/復選框是 Web 應用常用控件,隨處可見,原生的單/復選框控件的外觀一般都不怎么美觀,有些時候,原生的控件模樣並不能滿足設計要求,這時需要更為精致的控件樣式,我們更希望其樣式可以允許自定義。
CSS3 新增了一個偽類選擇器 :checked
,用於選擇被勾選的單/復選框,利用該選擇器可以分別為未選中和選中狀態的單/復選框應用不同的樣式,怎么給它應用樣式呢?直接修改單/復選框的樣式比較困難,不過可以使用一些其它方法來實現。
單選/復選框作為一個表單控件,可以使用一個 <label>
元素與之關聯,將 <label>
元素的 for
屬性指向復選框的 id
屬性,這里使用一個復選框來舉例說明:
<input id="example-checkbox" type="checkbox">
<label for="example-checkbox">Check</label>
此時,每次點擊這個 <label>
元素會發現復選框的選擇狀態也會變化,測試表明,點擊關聯某個單/復選框的 <label>
時,產生的效果和點擊單/復選框自身相同。然后,因為 <label>
元素緊鄰在單/復選框之后,通過組合使用相鄰元素選擇器(Adjacent sibling selector),我們可以把相應的樣式應用到 <label>
元素上去,相比較而言,給 <label>
元素應用樣式要比給原生的單/復選框應用樣式簡單多了。
相鄰元素選擇器,在兩個選擇器中間添加一個加號,像這樣 selector1 + selector2,表示選擇 selector1 元素之后緊鄰的 selector2 元素:
input[type="checkbox"] + label {
/* 未選中狀態 */
}
input[type="checkbox"]:checked + label {
/* 選中狀態 */
}
為了不對 <label>
元素自身的樣式造成影響,這里使用生成內容偽元素(::before/::after)來應用單/復選框的自定義樣式,首先使用 ::before 偽元素來生成復選框的外框樣式:
input[type="checkbox"] + label::before {
background-color: #fff;
border: 1px solid #50a7f8;
border-radius: 3px;
content: "\00a0";
height: 13px;
left: 0;
position: absolute;
width: 13px;
}
然后使用 ::after 偽元素來生成復選框中間的勾的樣式,用一個矩形只應用左邊框和下邊框,然后逆時針旋轉 45 度即可做成一個簡單的勾形:
input[type="checkbox"] + label::after {
background: transparent;
border: 0 solid #50a7f8;
border-width: 0 0 3px 3px;
content: "\00a0";
height: 3px;
left: 3px;
position: absolute;
top: 3px;
transform: scale(0) rotate(-45deg);
transition: transform 0.2s linear;
width: 6px;
}
為了在沒有勾選的時候不顯示這個勾,這里用 transform: scale(0)
來隱藏了這個勾,當復選框被勾選的時候才顯示出來:
input[type="checkbox"]:checked + label::after {
transform: scale(1) rotate(-45deg);
}
這里使用了絕對定位,所以給 <label>
元素增加一個相對定位,並在左邊預留一定的空間:
input[type="checkbox"] + label {
min-height: 20px;
padding-left: 20px;
position: relative;
}
原生的控件可以把它隱藏掉了:
input[type="checkbox"] {
display: none;
}
不過這樣隱藏后就不能通過 Tab 鍵來與控件交互了,所以可以用另外的方法來隱藏控件,比如設置不透明度為 0:
input[type="checkbox"] {
opacity: 0;
position: absolute;
}
進一步,可以定義控件獲取焦點時以及控件禁用時的樣式:
input[type="checkbox"]:focus + label::before {
outline: 1px dotted #aaa;
}
input[type="checkbox"]:disabled + label::before {
background-color: #eee;
border-color: #aaa;
}
input[type="checkbox"]:disabled + label::after {
border-color: #aaa;
}
更進一步,給勾選添加過渡效果:
input[type="checkbox"] + label::after {
transition: transform 0.2s linear;
}
單選框
和復選框一樣,只是把自定義樣式中的外框換成圓形外框,內部的勾換成一個實心圓:
<input id="example-radio" type="radio">
<label for="example-radio">Check</label>
HTML 結構是一樣,樣式也是類似:
input[type="radio"] {
opacity: 0;
position: absolute;
}
input[type="radio"] + label {
min-height: 20px;
padding-left: 20px;
position: relative;
}
input[type="radio"] + label::before {
background-color: #fff;
border: 1px solid #50a7f8;
border-radius: 50%;
content: "\00a0";
height: 13px;
left: 0;
position: absolute;
top: 0;
width: 13px;
}
input[type="radio"] + label::after {
background: #50a7f8;
border-radius: 50%;
content: "\00a0";
height: 9px;
left: 3px;
position: absolute;
top: 3px;
transform: scale(0);
transition: transform .2s linear;
width: 9px;
}
input[type="radio"]:checked + label::after {
transform: scale(1);
}
input[type="radio"]:focus + label::before {
outline: 1px dotted #aaa;
}
input[type="radio"]:disabled + label::before {
background-color: #eee;
border-color: #aaa;
}
input[type="radio"]:disabled + label::after {
background-color: #aaa;
}
這篇文章里用到了很多種類的選擇器,詳情可以參考Selectors Level 3
我將這篇文章里涉及到的內容整合成了一個插件,Radio 歡迎吐槽。