復選框的 CSS 魔法
原文: Checkbox Trickery with CSS
翻譯:涂鴉碼龍
Checkbox 復選框相當好用,加對 CSS 魔法有奇效。此文旨在展示一些利用 checkbox 實現的有創意的東西,并且文中的例子 沒用 JavaScript 喲。
基本配方
從 HTML 著手。
<input id="toggle" type="checkbox"> <label for="toggle">
此處無技巧可言。 <label> 的 for 屬性匹配 <input> 的 id 屬性,因此點擊 <label> 可以控制 <input> 復選框。這點尤其重要,因為下一步將隱藏 <input> 。
input { position: absolute; left: -9999px; }
為什么不用 display: none ?因為屏幕閱讀機和鍵盤 Tab 會忽略它。此方法讓 <input> 保持在文檔流中,但是讓它離屏隱藏(超出屏幕可見范圍達到隱藏)。
隱藏 <input> 以后,我們更容易大展身手。我們仍需傳達選中/未選兩種狀態,但是可以通過 <label> 完成。真正的派對開始啦。
input:checked + label { /* 牛X閃閃的樣式 */ }
我們使用 :checked 偽類, 和相鄰兄弟元素選擇器( + )的組合達到目的,當復選框選中時,找到緊隨其后的 <label> 元素,加上想要的樣式。還可以利用 <label> 中的偽元素( ::before 和 ::after )實現更有創意的想法。
input:checked + label::before { /* 指示器的樣式 */ }
來,看看實際效果吧。例子用到了以上提及的基本配方,把一個普普通通的復選框改造得當人眼前一亮。
See the Pen Checkbox Trickery: Simple Toggle by Will Boyd ( @lonekorean ) on CodePen .
最大的好處是,包含在 <form> 中的復選框的值仍然可以被提交。我們只改變了外觀,并沒有影響功能。
隱藏/顯示內容
目前為止,我們都是給 <label> 加樣式,我們可以更進一步。這個例子會根據用戶的選擇,動態地隱藏/顯示表單的部分內容。
See the Pen Checkbox Trickery: Form Disclosure by Will Boyd ( @lonekorean ) on CodePen .
:checked 偽類對單選按鈕同樣奏效,考慮到這一點,“How did you hear about us?”這塊的 HTML 用到了單選按鈕。
<input id="how-friend" name="how" type="radio"> <label for="how-friend" class="side-label">From a friend</label> <input id="how-internet" name="how" type="radio"> <label for="how-internet" class="side-label">Somewhere on the internet</label> <input id="how-other" name="how" type="radio"> <label for="how-other" class="side-label">Other...</label> <div class="how-other-disclosure"> <label for="how-other-explain" class="top-label">Please explain</label> <textarea id="how-other-explain"></textarea> </div>
單選按鈕指示器利用 <label> 內的 ::before 偽元素(外部的圓環)和 ::after 偽元素(內部的綠點)實現,當單選按鈕選中 / 未選時顯示 / 隱藏 ::after 偽元素相當簡單。
.side-label::after { display: none; /* 其它樣式 */ } input:checked + .side-label::after { display: block; }
<div> 一直隱藏,直到“Other…”單選按鈕選中時才顯示。我使用了 display: none 隱藏 <div> ,因為這次我確實想讓屏幕閱讀器和鍵盤 Tab 在內容隱藏時忽略它。當單選按鈕選中時,利用 CSS 顯示 <div> 。
#how-other:checked ~ .how-other-disclosure { display: block; }
之前我們一直使用相鄰兄弟選擇器( + ),不過這次得用一般兄弟選擇器( ~ )。它倆很相似,但是可以找到非相鄰的兄弟元素,比如我們的 <div> 。
樹狀文件夾
我們可以活用之前例子中的技巧,實現一個樹狀文件夾組件,同樣具備隱藏 / 顯示兩種功能。
See the Pen Checkbox Trickery: Folder Tree by Will Boyd ( @lonekorean ) on CodePen .
單個文件夾的 HTML 如下: <label> 是文件夾,兩個 <a> 元素是文件夾里的文件。
<div> <input id="n-1" type="checkbox"> <label for="n-1">Blue</label> <div class="sub"> <a href="#link">Mana Leak</a> <a href="#link">Time Warp</a> </div> </div>
Font Awesome 圖標用于表示選中(打開)和未選(關閉)狀態。
label::before, a::before { display: block; position: absolute; top: 6px; left: -25px; font-family: 'FontAwesome'; } label::before { content: '\f07b'; /* closed folder */ } input:checked + label::before { content: '\f07c'; /* open folder */ } a::before { content: '\f068'; /* dash */ }
文件夾里的內容通過一般兄弟選擇器( ~ )實現顯示 / 隱藏。這就是 HTML 外面包裹額外 <div> 的原因,為了確保選擇器能夠選到元素,打開兄弟文件夾。
input:checked ~ .sub { display: block; }
理所當然,文件夾可以嵌套。只需把另一個文件夾的 HTML 放入 <div class="sub"> 即可。點擊 “Multicolor”看看效果吧。
最后,我們聊一下重置按鈕。
<input type="reset" value="Collapse All">
表單的重置按鈕很少有人用,不過這里巧用了一下。點擊重置按鈕,所有復選框恢復成初始的未選中狀態,關閉了所有文件夾。有意思吧。
Split List(分隔列表)
這個例子依據已做或未做,把列表選項分成兩部分。
See the Pen Checkbox Trickery: To-Do List by Will Boyd ( @lonekorean ) on CodePen .
HTML 像這樣:
<div class="items"> <input id="item1" type="checkbox" checked> <label for="item1">Create a to-do list</label> <!-- more items --> <h2 class="done" aria-hidden="true">Done</h2> <h2 class="undone" aria-hidden="true">Not Done</h2> </div>
分隔列表是由 CSS flexbox 實現的,這是關鍵的 CSS。
.items { display: flex; flex-direction: column; } .done { order: 1; } input:checked + label { order: 2; } .undone { order: 3; } label { order: 4; }
CSS flexbox 可以使用 order 屬性重排元素。當復選框選中的時候, <label> 的 order 值由 4 變為 2 ,列表選項就從 “Not Done” <h2> 下面移到了 “Done” <h2> 下面。
不幸的是,鍵盤導航和 許多屏幕閱讀器 會遵循元素在 DOM 中的順序,即使它們被 CSS flexbox 做了視覺上的重排。這就導致 “Done”和“Not Done”的標題對于屏幕閱讀器無用,這便是我給它們加了 aria-hidden="true" 的原因 —— 被忽略總比引起混淆強。此外,通過鍵盤和屏幕閱讀器完全可以控制分隔列表,正確顯示列表項的狀態(選中 / 未選)。
如果你對“Done”和“Not Done”其后的計數實現感到好奇,它們用到了 CSS counters。想深入學習的話,可以看 這篇文章 。
分組篩選
壓軸的例子展示了如何根據篩選條件,高亮顯示交叉區域的數據。
See the Pen Checkbox Trickery: Group Filter by Will Boyd ( @lonekorean ) on CodePen .
這是簡短的 HTML 。注意一下 data-teams 屬性是由空格分隔的列表,它們每一項恰恰與單選按鈕的 id 屬性一致。我就是這么把角色跟隊伍匹配起來的。
<input id="original" type="radio" name="team" checked> <label for="original">Original X-Men</label> <!-- 省略更多團隊 --> <br> <ul class="characters"> <li id="angel" data-teams="original force factor hellfire"> <h2>Angel</h2> <img src="ct-angel.png" alt=""> </li> <!-- 省略更多角色 --> </ul>
關于可訪問性,我使用了空的 alt 屬性,因為角色的名字已顯示在 <h2> 標簽中了 —— 每個名字讀兩次也沒有必要。此外,我沒有真正的隱藏 <img> 元素(只是收縮和漸隱),屏幕閱讀器可以輕易的跳過未高亮的角色。我僅僅需要隱藏 <h2> 。
當選擇團隊的時候,高亮對應的角色,CSS 是這么實現的。
#original:checked ~ .characters [data-teams~="original"] h2, #force:checked ~ .characters [data-teams~="force"] h2, #factor:checked ~ .characters [data-teams~="factor"] h2, #hellfire:checked ~ .characters [data-teams~="hellfire"] h2 { /* styles to show character name */ } #original:checked ~ .characters [data-teams~="original"] img, #force:checked ~ .characters [data-teams~="force"] img, #factor:checked ~ .characters [data-teams~="factor"] img, #hellfire:checked ~ .characters [data-teams~="hellfire"] img { /* styles to show character avatar */ }
我清楚這些選擇器看起來有點嚇人,不過也不賴。我們來仔細分析下例子的第一行(譯者注:以 CSS 中最長的選擇器開始算)。當 id 為 ‘original’ 的元素被選中時,找到角色元素里面 data-teams 屬性包含 ‘original’ 的元素,然后找到它里面的 <h2> 。2-4行的 ‘force’, ‘factor’, 和 ‘hellfire’ 重復上一步。8-11行繼續重復上一步,只不過把 <h2> 換成了 <img> 。
綜述
我希望你跟我一樣從這些例子中找到了一些樂趣。通過復選框去實現一些好玩的東西,對我而言非常有趣。我并沒有排斥在適當的時候使用 JavaScript ,但是沒用它也可以實現這么多東西,也是蠻開心的。謝謝閱讀!
來自: http://jinlong.github.io/2016/01/14/checkbox-trickery-with-css/