Edited at
CSSDay 21

input[type="checkbox"]やinput[type="radio"]を悪用してJSの出番を減らそう

More than 1 year has passed since last update.

本稿は『CSS Advent Calendar 2016 - Qiita』21日目の記事です.

なんかみんな「ぼくがかんがえたさきょうのしーえすえすせっけい」について語る雰囲気っぽですが,ここは空気を読まずに小ネタでいきます.

ぜんぜんどうでもいい話ですが,僕はCSS設計ネタでは『morishitterのCSSの書き方(2016年夏) - morishitter blog

』と『Clean and Unbreakable CSS // Speaker Deck』が好きです.



TL;DR



  • :checked擬似クラスと兄弟セレクタ~を利用すると…


    • JSを書かずに「ステート(on/offの2値)によるスタイルを切り替え」が実現できる




  • label要素とinput[type="checkbox"]またはinput[type="radio"]を利用すると…


    • JSを書かずに「ある要素がクリックされたらステート(on/offの2値)が変わる」が実現できる




サンプル: Pure-CSSなTODOリスト

タスクの完了状態の切り替え,およびタスクを1つ選択することができる(さすがにCSSだけで永続化は不可能,たぶん).


https://gyazo.com/cbb4adceeda21cb54723731f1c3c017b

Pure CSS TODO list | CodePen


マークアップはこんな感じ(pugでごめんなさい).

// completeId, selectId, taskは変数

li.TaskList__item
input.TaskList__item-complete(type="checkbox" id=completeId)
input.TaskList__item-select(type="radio" name="TaskList__item-select" id=selectId)

label.TaskList__item-icon(for=completeId)
label.TaskList__item-body(for=selectId) #{task}


実装

以降のスニペットについては,関係ないところは省略してある.

// display: noneになってる

input.TaskList__item-complete(type="checkbox" id=completeId)

// 見えてるチェックボックスはlabelの::before疑似要素
// forでcheckboxのidを指定しているので,これをクリックするとcheckboxの状態も変わる
label.TaskList__item-icon(for=completeId)
.TaskList__item-body #{task}

/*

* チェックボックスの枠
*/

.TaskList__item-icon::before {
font-family: "FontAwesome";
content: "";
display: block;
border: 1px solid #444;
border-radius: 4px;
margin: calc(2 * var(--spacing)) var(--spacing);
width: var(--TaskList__item-icon-size);
height: var(--TaskList__item-icon-size);
line-height: var(--TaskList__item-icon-size);
cursor: pointer;
}

/*
* チェックボックスの中身
* FontAwesomeの`fa-check`
* 「チェックが入ったチェックボックスと同じ階層にある.TaskList__item-iconの::before疑似要素」にチェックマークを入れる
*/

.TaskList__item-complete:checked ~ .TaskList__item-icon::before {
content: "\f00c";
}

/*
* 「チェックが入ったチェックボックスと同じ階層にある.TaskList__item-body」に取り消し線を入れる
*/

.TaskList__item-complete:checked ~ .TaskList__item-body {
text-decoration: line-through;
}

タスクの選択も同様.


実用するには…

以下についてJSでサポートしてあげる:


  • checkbox/radioのchecked属性に永続化されてるタスクの状態を反映する

  • checkbox/radioのchangeイベントを拾ってWeb API叩くなりする


Pure CSSだとなにが嬉しいの?

単純に楽しいだけ.

JSとしてはchecked属性とchangeイベントだけ見てればいいので,見た目とかはすべてCSSに丸投げできる.

よくあるクリックイベント拾って$.addClass()して…みたいなのを書かなくていい,という建前

取りうる状態が2種類しかない場合(on/off, open/close, complete/incomplete, running/stopped, etc.)に雑にスタイルを切り替えたい場合は利用価値があるかも.

微妙にテストが書きづらくなるとか,ムダに詳細度上げてる説があったり等のデメリットもあるので濫用しすぎない方がいいのかもしれない.


他には?


おわりに

CSSには他にもattr()CSSカウンタのような使い所の難しいおもしろ機能がいっぱいあるので,じゃんじゃん悪用してたのしくCSSを書いていきましょう.

暇な時に仕様書を眺めてみると,新たな発見があるかもしれません.

個人的にはattr()がcontentプロパティ以外にも利用できるようになるのが非常に楽しみです.