複選框 Checkbox 是 Web 應用經常使用控件,隨處可見,原生的複選框控件通常就像下面這樣:css
選中狀態 未選狀態html
這取決於操做系統和瀏覽器,有些時候,這種樣子並不能知足設計要求,這時須要更爲精緻的複選框樣式。以往只有少數瀏覽器才支持對這類控件應用樣式,好比拿到這樣一張設計圖:html5
blue.pngjava
在過去,想要經過簡單地修改樣式達成這種設計效果根本不行,不過,如今藉助強大的 CSS3 屬性 appearance 能夠對該類控件有空前的樣式控制能力:css3
input[type="checkbox"] { -webkit-appearance: none; }
這樣設置該屬性值爲 none
就去掉了複選框原有的呈現方式,變成了一個普普統統的元素,而後就能夠爲之應用樣式了,添加以下樣式:web
input[type="checkbox"] { -webkit-appearance: none; background: #fff url(i/blue.png); height: 22px; vertical-align: middle; width: 22px; }
經過結合使用狀態僞類選擇器 :checked
能夠爲選中狀態下的 checkbox 設置不一樣的樣式,用以從視覺上區別:瀏覽器
input[type="checkbox"]:checked { background-position: -48px 0; }
此時點擊複選框,能夠看到複選框樣式的變化效果,另外,根據那張設計圖片所示還得加上獲取焦點,禁用等狀態的樣式:app
input[type="checkbox"]:focus, input[type="checkbox"]:hover { background-position: -24px 0; outline: none; } input[type="checkbox"]:checked { background-position: -48px 0; } input[type="checkbox"][disabled] { background-position: -72px 0; } input[type="checkbox"][disabled]:checked { background-position: -96px 0; }
由於圖片已經事先合併成一張了,簡單修改一下 background-position
就能夠了,同時前面幾個選擇器的優先級(權重)同樣,因此書寫順序很重要。性能
目前只兼容 Webkit 系列瀏覽器;雖然 Firefox 也實現了替代的 -moz-appearance
屬性,不過控件原有的背景顏色、邊框樣式沒法修改,暫時也不大好用;IE 系列暫時不支持該屬性,更詳細的兼容狀況查看 Caniuse/appearance。所以須要爲 IE 瀏覽器清除掉背景圖片的影響:this
input[type="checkbox"] { background: #fff url(i/blue.png); background: none\0; *background: none; ... }
爲了兼容更多的主流瀏覽器,須要尋求另外的解決方案,在全部主流瀏覽器裏,點擊關聯某個複選框的 label 時,產生的效果和點擊元素自身相同,會切換複選框控件的選中狀態。瀏覽器的這種行爲給了咱們一個相當重要的掛鉤,既然能依靠 label 元素來控制原生複選框的狀態,那麼就能夠沒必要直接操做實際的複選框元素,而把操做和樣式都轉移到與之關聯的 label 元素上去:
<input id="example" type="checkbox"> <label for="example"></label>
確保 label 元素的 for
屬性的值和複選框 input 的 id
值一致,同時將 label 元素放置於 input 以後,這樣 CSS 能夠經過相鄰兄弟選擇器(Adjacent sibling selector)定位到這個 label 元素併爲之應用樣式:
input[type="checkbox"] + label:before { background: #fff url(i/blue.png); content: " "; height: 22px; left: 0; position: absolute; width: 22px; }
有了樣式化的 label 元素來提供交互,原生的 checkbox 控件就顯得有點多餘了,雖然能夠用 display: none
把它隱藏掉,不過隱藏後的表單元素是不能得到焦點的,因此最好的方式仍是用 label 元素把它遮住,這樣既能支持鍵盤交互,同時當圖片加載失敗的時候,又能保證原生的控件可用:
input[type="checkbox"] { box-sizing: border-box; left: 4px; margin: 0; padding: 0; position: absolute; top: 3px; }
圖片要足夠大能將原生的 checkbox 控件徹底遮擋住,由於這裏用到了絕對定位,因此須要增長一個定位參照:
<!-- HTML --> <div class="checkbox"> <input id="exampleCheckbox" type="checkbox"> <label for="exampleCheckbox"></label> </div> /* CSS */ .checkbox { min-height: 24px; padding-left: 24px; position: relative; }
左邊預留內邊距是爲了排版更美觀,同時,和以前同樣,搭配上其它狀態下的樣式:
input[type="checkbox"]:focus + label:before, input[type="checkbox"] + label:hover:before { background-position: -24px 0; } input[type="checkbox"]:checked + label:before { background-position: -48px 0; } input[type="checkbox"][disabled] + label:before { background-position: -72px 0; } input[type="checkbox"][disabled]:checked + label:before { background-position: -96px 0; }
只要支持 CSS3 selectors 的瀏覽器基本上都能兼容,同時具有原生控件的絕大多數交互特性。IE 8 不支持 :checked
僞類選擇器,將僞元素 :before
修改成雙冒號 ::before
能夠去掉對 IE 8 的影響:
input[type="checkbox"] + label::before { ... }
關於僞元素生成內容的兼容性見 CSS Generated content for pseudo-elements。誠然,上面的方法假設了支持 :checked
僞類選擇器的瀏覽器同時也支持雙冒號僞元素寫法,而不支持的瀏覽器則都不支持,這是一種不太好的方式,這種假設事實上也是不正確的,形成了部分老舊瀏覽器不可用的問題,若是使用 :not()
選擇器則更爲合理,使用 :not(:checked)
來爲未選中的控件添加樣式,:not()
和 :checked
同屬一個規範 css3-selectors,兼容性應該一致 CSS3 selectors。不過寫法有點變化,:checked
和 :not(:checked)
都須要添加上基本的樣式:
input[type="checkbox"]:checked + label:before, input[type="checkbox"]:not(:checked) + label:before { background: #fff url(i/blue.png); content: " "; height: 22px; left: 0; position: absolute; width: 22px; } input[type="checkbox"]:not(:checked):focus + label:before, input[type="checkbox"]:not(:checked) + label:hover:before { background-position: -24px 0; } input[type="checkbox"]:checked + label:before { background-position: -48px 0; } input[type="checkbox"][disabled]:not(:checked) + label:before { background-position: -72px 0; } input[type="checkbox"][disabled]:checked + label:before { background-position: -96px 0; }
查看簡單示例,對於那些並不支持 :checked
僞類選擇器的瀏覽器(好比 IE 8),則能夠藉助 javaScript 來根據控件狀態修改真正的 class 屬性達到區分不一樣狀態的目的,好比根據是否被選中來添加或刪除一個 checked
的 class:
// jQuery $('input[type="checkbox"]').on('change', function() { $(this)[$(this).prop('checked') ? 'addClass' : 'removeClass']('checked'); }); /* CSS */ input[type="checkbox"].checked + label:before { ... }
有了前面的基礎,要製做相似於開關按鈕的控件也是很是簡單的了,仍是熟悉的結構:
<div class="checkbox"> <input id="example" type="checkbox"> <label for="example">Check</label> </div>
首先勾勒出開關的形狀,一個圓角矩形中間放一個圓形按鈕:
input[type="checkbox"]:checked + label, input[type="checkbox"]:not(:checked) + label { background-color: #e0e0e0; border-radius: 24px; cursor: pointer; display: inline-block; height: 24px; position: relative; text-indent: -9999px; width: 48px; } input[type="checkbox"]:checked + label:after, input[type="checkbox"]:not(:checked) + label:after { background-color: #fff; border-radius: 20px; content: " "; height: 20px; left: 2px; position: absolute; top: 2px; width: 20px; }
選中的效果只要簡單修改下外框的背景色和中間按鈕的位置就行:
input[type="checkbox"]:checked + label { background-color: #8c8; } input[type="checkbox"]:checked + label:after { left: 26px; }
不過這種跳躍式變化實在是太生硬了,添加點過渡效果,看上去更平滑:
input[type="checkbox"]:checked + label, input[type="checkbox"]:not(:checked) + label { -webkit-transition: background-color 0.3s; transition: background-color 0.3s; } input[type="checkbox"]:checked + label:after, input[type="checkbox"]:not(:checked) + label:after { -webkit-transition: left 0.3s; transition: left 0.3s; }
點擊就能看到效果,對於中間的按鈕部分使用 CSS3 Transforms
來替代 left
效果更好,速度更快:
input[type="checkbox"]:checked + label:after, input[type="checkbox"]:not(:checked) + label:after { -webkit-transition:left-webkit-transform 0.3s; -o-transition: -o-transform 0.3s; transition:lefttransform 0.3s; } input[type="checkbox"]:checked + label:after {left: 26px;-webkit-transform: translateX(24px); -ms-transform: translateX(24px); -o-transform: translateX(24px); transform: translateX(24px); }
不支持 CSS3 Transforms
的瀏覽器仍然能夠看到背景色的變化,不影響可用性,詳見 CSS3 Transforms。關於性能問題,請參考 High Performance Animations。快速點擊「控件」會因選中效果形成不能切換狀態的狀況,因此去掉「控件」能夠被選中的能力:
input[type="checkbox"]:checked + label, input[type="checkbox"]:not(:checked) + label { (-prefix-)user-select: none; }
這裏的瀏覽器廠商前綴根據須要替換成相應的,查看簡單示例。固然還須要提供聚焦以及禁用等狀態的樣式,就不在這裏重複了。以上全部技術可同時適用於單選框(radio)。