Django、カスタムチェックボックスの作成
カスタムチェックボックスとは
今回、次のようなものを作ります。
これは、チェックボックスをカスタマイズして作っています。チェックボックスは、通常は次のようなものですね。これをCSSとかで、上のようにしました。
こういう見た目のものを作るだけならば、チェックボックスを使わなくても実装はできます。しかし、複数選択に対応したい場合だったり、他のフォーム部品との兼ね合い・・・例えばタグの他にキーワード検索も一緒にさせたいとか、そういったことになってくると、チェックボックスで実装するのが楽ちんです。
GithubにDjangoプロジェクトを置いているので、興味のある方は試してください。
チェックボックスのカスタマイズ方法
やり方は幾つかあるようなのですが、よく見かける、情報量の多い方法は隣接セレクタを使う方法があります。
ステップ1
まず、次のようにHTMLを書きます。
<input type="checkbox" name="tags" value="1" id="id_tags_0">
<label for="id_tags_0">Python</label>
この段階では、ただのチェックボックス。<input type="checkbox"
は四角いチェック部分で、<label
はPythonという文字部分です。
ステップ2
四角いチェックボックス部分を、display:none
で隠します。
<style>
input[type="checkbox"] {
display: none;
}
</style>
<input type="checkbox" name="tags" value="1" id="id_tags_0">
<label for="id_tags_0">Python</label>
チェック部分が消えて、Pythonという文字だけ、つまりlabel要素だけ表示されるようになりました。
ステップ3
label要素に対して、おしゃれなCSSを設定します。
<style>
input[type="checkbox"] {
display: none;
}
label {
display: inline-block;
border-radius: 20px;
text-align: center;
text-decoration: none;
border: solid 1px #ccc;
transition: 0.25s;
padding: 6px 18px;
cursor: pointer;
font-size: 14px;
margin: 3px;
}
</style>
<input type="checkbox" name="tags" value="1" id="id_tags_0">
<label for="id_tags_0">Python</label>
すると、それっぽい見た目になってきましたね。チェックボックスに紐づいたラベル(labelのforにチェックボックスのidを指定したもの)は、クリックするとチェックボックスにクリックしたことを伝えてくれます。なので、このラベルをクリックするだけで、内部的にはチェックボックスにチェックが入ってるのです。非表示にしているので、それが見えないのですが。
ステップ4
チェックボックスにチェックが入っているときに、見た目的にそれを教える必要があります。ここでよく使われるのが隣接セレクタです。
<style>
input[type="checkbox"] {
display: none;
}
label {
display: inline-block;
border-radius: 20px;
text-align: center;
text-decoration: none;
border: solid 1px #ccc;
transition: 0.25s;
padding: 6px 18px;
cursor: pointer;
font-size: 14px;
margin: 3px;
}
input[type="checkbox"]:checked + label {
background: #00809d;
color: #fff;
}
</style>
<input type="checkbox" name="tags" value="1" id="id_tags_0">
<label for="id_tags_0">Python</label>
増えたのは、input[type="checkbox"]:checked + label
という指定です。これはチェックが入ったチェックボックスすぐ後にあるラベルという意味になります。上に書いたように、ラベル部分をクリックでそのチェックボックスにチェックが入ってくれます。チェックボックスすぐ後にあるラベルというのは、クリックしたラベルのことなので、これでクリックしたときに色がついて分るようになります。
Djangoデフォルトのチェックボックス
Djangoが作るデフォルトのチェックボックスのHTMLについて知っておく必要があります。これは次のようなHTMLになります。
<ul id="id_tags">
<li>
<label for="id_tags_0">
<input type="checkbox" name="tags" value="1" id="id_tags_0" checked>
Python
</label>
</li>
</ul>
ulやliは問題ないのですが、上の隣接セレクタの方法を使うに当たっては、このHTMLだとちょっと難しそうです。なので、もう少しシンプルなHTMLを生成するウィジェットを作ってみましょう。
シンプルなチェックボックスウィジェット作成
カスタムウィジェットは、もう何度かブログでやってきました。専用のタグもつけているので、興味がある方は他の記事も見てみてください。
アプリケーション内にでも、widgets.py
といった分かりやすい名前のファイルを作ります。そして、次のように書きます。
from django import forms
class CustomCheckboxSelectMultiple(forms.CheckboxSelectMultiple):
template_name = 'app/widgets/custom_checkbox.html'
option_template_name = 'app/widgets/custom_checkbox_option.html'
def __init__(self, attrs=None):
super().__init__(attrs)
if 'class' in self.attrs:
self.attrs['class'] += ' custom-checkbox'
else:
self.attrs['class'] = 'custom-checkbox'
チェックボックスにはcustom-checkbox
というcssのclassを持たせて、HTMLを作るテンプレートを別に指定しています。
app/widgets/custom_checkbox.html
を作ります。
{% for group, options, index in widget.optgroups %}{% for option in options %}{% include option.template_name with widget=option %}{% endfor %}{% endfor %}
これは、もともともチェックボックスが使うテンプレートと殆ど同じです。ulとかliを作らないように、少しコードを削除しただけです。
app/widgets/custom_checkbox_option.html
を作ります。
{% include "django/forms/widgets/input.html" %}<label for="{{ widget.attrs.id }}" class="custom-checkbox-label">{{ widget.label }}</label>
これが、inputの横にlabelを配置するようにしています。そして、labelのclassにはcustom-checkbox-labelという指定もしています。
後は、このウィジェットを使うだけです。例えば、フォームで次のように指定します。
from django import forms
from .models import Tag
from .widgets import CustomCheckboxSelectMultiple
class SampleForm(forms.Form):
tags = forms.ModelMultipleChoiceField(
label='タグ', queryset=Tag.objects, required=False,
widget=CustomCheckboxSelectMultiple, # ここで、今作ったウィジェットを指定
)
最後に、CSSを設定しておきましょう。これで、最初に紹介したような見た目になります。
/* カスタムチェックボックス */
.custom-checkbox {
display: none;
}
.custom-checkbox:checked + .custom-checkbox-label {
background: #00809d;
color: #fff;
}
.custom-checkbox-label {
display: inline-block;
border-radius: 20px;
text-align: center;
text-decoration: none;
border: solid 1px #ccc;
transition: 0.25s;
padding: 6px 18px;
cursor: pointer;
font-size: 14px;
margin: 3px;
}
.custom-checkbox-label:hover {
opacity: 0.5;
}