クリックすると色が変わるアイコンをHTML/CSSで簡単に作る

初投稿です:v:

クリック(もしくはタップ)すると色が反転するアイコンを、HTMLのcheckboxとCSSだけで簡単に作れたので、その方法をまとめます。
グーグル先生に聞けば同様の情報はたくさんありますが、かっこいい装飾がついた情報が多く、多少混乱しました。
この記事ではできるだけシンプルな例とするよう心がけています。

完成品はこちらです。電車のアイコンをクリックすると色が変わります。

<p data-height="265" data-theme-id="0" data-slug-hash="BVVzqO" data-default-tab="html,result" data-user="kumatira" data-embed-version="2" data-pen-title="change_icon_color" class="codepen">See the Pen <a href="https://codepen.io/kumatira/pen/BVVzqO/">change_icon_color</a> by kumatira (<a href="https://codepen.io/kumatira">@kumatira</a>) on <a href="https://codepen.io">CodePen</a>.</p>
<script async src="https://static.codepen.io/assets/embed/ei.js"></script>

JavaScriptが要るかなと思っていたのですが、CSSで必要十分でした。

# 誰が対象？
この記事はHTML/CSSに初めて触れて間もないという方が、読みやすいよう書きました。
中級者以上の方には冗長に感じる部分があると思いますので適時飛ばしてお読みください。

# いつ使うの？
On/Offをアイコンの色で表現する際、役に立ちそうです。
- 検索ページの条件の設定
- トイレの有無などのある情報のOn/Offを表したい時

応用すれば独自のチェックボックスを実装することもできます。実際MaterializeなどのCSSフレームワークもこの方法を使ってチェックボックスを独自のものに置き換えていました。

# 準備
この記事内で使うファイルは以下の通りです。

- index.html
- main.css
- train.svg   
    
main.csvはhtmlとのリンクをしておいてください。
またtrain.svgはSVG形式の画像であればなんでも良いです。今回は商用可能で可愛らしいアイコンをSVG形式でも配布されている[ICOOON MONOさん](http://icooon-mono.com/)からいただきました。


## SVG画像について

SVGはScalable Vector Graphicsの略で、画像内の要素が全てコードで書かれています。その名の通り、単一の画像を用意するだけで、全ての解像度のデバイスで綺麗に表示されます。

例えば今回使用する画像はブラウザやIllustrator等で見ると以下の通りですが、
<img width="300" alt="スクリーンショット 2018-06-23 21.15.50.png" src="https://qiita-image-store.s3.amazonaws.com/0/159912/6a361bf0-7edb-47d3-bda3-aa64a62b0b5c.png">

Atomなどお手元のエディタやchrome DevToolで見ると、以下のようにコードで書かれていることがわかります。

```html:train.svg
<svg version="1.1" id="_x32_" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px" viewBox="0 0 512 512" style="width: 256px; height: 256px; opacity: 1;" xml:space="preserve">
<style type="text/css">
	.st0{fill:#4B4B4B;}
</style>
<g>
	<path class="st0" d="省略" style="fill: rgb(75, 75, 75);"></path>
	<rect x="98.027" y="240.189" class="st0" width="58.324" height="116.656" style="fill: rgb(75, 75, 75);"></rect>
	<rect x="217.921" y="240.189" class="st0" width="68.052" height="58.331" style="fill: rgb(75, 75, 75);"></rect>
	<rect x="345.921" y="240.189" class="st0" width="68.052" height="58.331" style="fill: rgb(75, 75, 75);"></rect>
</g>
</svg>
```
gタグの中にあるpathやrectが、イラストの中のそれぞれの要素(電車の枠や窓)を表しています。
いくつか属性を持っていますが、今回はpathやrectについているstyle属性をみてください。ここではfillというパラメーターでその要素の色を指定しています。

今回はCSSで要素の色を決めたいので、SVG内のstyleタグおよび、pathタグとrectタグ内のstyale属性を消しておきます。

# 実装!!
ではコードを書いていきましょう
## HTML
まずはチェックボックスと画像のHTMLをbodyタグ内に組みます。
なぜチェックボックス？という方がいらっしゃると思いますが、今回のサンプルでは画像がクリックされた(またはクリックされてない)という情報をチェックボックスで保持しています。
よってチェックボックスがonの時に画像の色が変わり、offになると元に戻リます。

```html:index.html
<div>
	<input type="checkbox" name="train" class="checkbox" id="chuo">
	<label for="chuo">
		<svg class="train-icon" version="1.1" id="_x32_" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"
			 x="0px" y="0px" viewBox="0 0 512 512" style="" xml:space="preserve">
			<g>
				<path class="st0" d="省略"></path>
				<rect x="98.027" y="240.189" class="st0" width="58.324" height="116.656"></rect>
				<rect x="217.921" y="240.189" class="st0" width="68.052" height="58.331"></rect>
				<rect x="345.921" y="240.189" class="st0" width="68.052" height="58.331"></rect>
			</g>
		</svg>
	</label>
</div>
```


ポイントが3点あります。
### (1)SVG画像はimgタグではなく、svgタグでそのまま埋め込む
画像を呼び出す時、普通は `<img src="./images/train.svg" alt="ChuoLine">`のようにimgダグを使いますよね。しかし今回のようにCSSでSVG画像を操作する場合、imgタグで画像として呼んでしまうと、その中身をCSSが操作することができなくなります。そのため上記のようにそのまま埋め込む必要があります。

なおコードがやたらと長くなり、どうしても見辛い場合は[deSVG](https://github.com/benhowdle89/deSVG)というimgで読み込んでもCSSで操作できるようになるJSライブラリがあります。併せて使ってみてください。
また同様の理由で、本記事のサンプルコードではSVGタグ内を一部省略して載せています。

### (2)画像をlabelタグ内に置く
labelタグのfor属性に紐付けたいinputタグのidを渡すと、そのlabel内にある要素へのクリックで紐付けたinputタグ(今回はチェックボックス)が反応するようになります。
今回は画像のクリックをチェックボックスと連動させたいため、labelタグ内にsvgタグを置いています。

labelタグ内にinput要素を置くというテクニックもありますが、今回は不要なので割愛します。

### (3)classとidをつける
inputタグには`id="chuo"`、svgタグには`class="train-icon"`をつけています。これらは後述のCSSから要素をセレクトする時に使います。


では他の電車も登場させましょう。それぞれに上記のようにidとclassをつけるのを忘れないでください！！

```html:index.html
<div>
	<input type="checkbox" name="train" class="checkbox" id="chuo">
	<label for="chuo">
		<svg class="train-icon" 省略 ><!-- svgタグ内省略 --></svg>
	</label>

    <input type="checkbox" name="train" class="checkbox" id="sobu">
	<label for="chuo">
		<svg class="train-icon" 省略 ><!-- svgタグ内省略 --></svg>
	</label>

    <input type="checkbox" name="train" class="checkbox" id="yamanote">
	<label for="chuo">
		<svg class="train-icon" 省略 ><!-- svgタグ内省略 --></svg>
	</label>
</div>
```
HTMLはこれでOKです！

## CSSの実装
CSSについては一気に説明します。

```css:main.css
.train-icon{
	width: 100px;
	height: auto;
	fill: aliceblue;
}

#chuo:checked + label .train-icon{
	fill: #C16543;
}

#sobu:checked + label .train-icon{
	fill: #FDBC00;
}

#yamanote:checked + label .train-icon{
	fill: #7BAB4F;
}

input{
	opacity: 0;
}
```

一番上の`.train-icon`に対するスタイルでは電車のアイコンの幅、デフォルトの色を決めています。(fill属性については後述)
また一番最後の`input`に対するスタイルでは、チェックボックスを透明化しています。要素として必要だけど見える必要はないからですね。

### 擬似クラスと子、子孫、隣接セレクタ
残りの`#chuo:checked + label .train-icon`というセレクタについてみてみましょう。(他の2つも同様です。)
`#chuo`は先ほどHTMLに作ったinput要素です。
ここに`:checked`がついています。[MDN web docs](https://developer.mozilla.org/ja/docs/Web/CSS/:checked)には
>CSS の `:checked `疑似クラスセレクターは、ラジオボタン（<input type="radio">）、 チェックボックス（<input type="checkbox">）、 オプションボタン（<select> の中の <option>）要素がチェックされていたり on の状態にあったりすることを表します。

と説明されています。
よって今回の場合、`#chuo:checked`は「選択状態にあるid="chuo"要素」という意味のセレクタになります。

次の　`+`は「隣接セレクタ」です。その左側にある要素の兄弟要素をさします。
また` (半角スペース)`は「子孫セレクタ」でその左側にある要素の子孫要素をさします。

よって`#chuo:checked + label .train-icon`は「選択状態にある#chuo要素の、兄弟要素のlabelの、子孫要素の.train-icon」という意味のセレクタになります。
index.htmlと見比べながら読むと電車の画像にたどり着きますね！!

### fill属性
はじめに今回使用するSVG画像を説明した際、消したfill属性を覚えていますか。
あの属性をここで指定しています。

ちなみにそれぞれの色は鉄道のラインカラーに関する[Wikipediaのページ](https://ja.wikipedia.org/wiki/%E6%97%A5%E6%9C%AC%E3%81%AE%E9%89%84%E9%81%93%E3%83%A9%E3%82%A4%E3%83%B3%E3%82%AB%E3%83%A9%E3%83%BC%E4%B8%80%E8%A6%A7)から取ってきました。
本当になんの情報でもありますね。

---
これでCSSも完成しました！！
「色が変わらないよ」という方はindex.htmlで、SVG内のstyleタグおよび、pathタグとrectタグ内のstyale属性を消したか確認してみてください

ブラウザからindex.htmlを見ると初めに紹介したサンプルのようになっているでしょうか?

## まとめ
ここまで、クリックすると色が変わるアイコンをHTML/CSSで簡単に作る方法を解説しました。
途中で述べたとおり、チェックボックスは選択されているかの情報を持っています。そのため、このままフォームの中に入れPOSTすることも可能です。

この方法を応用すれば、fill属性だけでなく要素の形を決める属性などもCSSやJavaScriptから操作できるため、色々できることが増えそうですね!!ぜひお試しください。


指摘、改善点等ありましたらコメントか編集リクエストをお待ちしています！！