概要
Vueのコンポーネントにおいては要素のclass属性に対してv-bindを使うことで動的にスタイルの切り替えが可能になっています。
v-bindを使うのでJSの力をフルに活用できますが、templateの中に条件分岐が増えてくると把握が難しくなります。
そこでそれをほぐすためのCSSの属性セレクタを使う手法を解説します
複雑なclassの問題
以下公式の解説で紹介されているサンプルです
<div v-bind:class="[isActive ? activeClass : '', errorClass]"></div>
<div v-bind:class="[{ active: isActive }, errorClass]"></div>
これぐらいのコードであれば実際にはそこまで読みにくいわけでもないですが、実際のアプリケーションではもっと条件が複雑になっていきます。
条件分岐を含むクラスが破綻しやすくなる理由としては単純で、「恒常的なスタイル」と「一時的な状態・性質のためのスタイル」が混在するからです。
- 恒常的なスタイル
- コンポーネントのサイズ、位置など
- 一時的な状態・性質のためのスタイル
- ローディング中、 クリック可能か、など
一時的な状態や性質のためのスタイルは大抵の場合条件分岐が絡みます。
そこでそれらを三項演算子ではなく属性セレクタによって分離すると、template側もstyle側もコードの見通しがスッキリします
属性セレクタを利用したスタイリング
先程の例を属性セレクタを利用して書いてみます
<div class="sample" :aria-disabled="''+disabled" :data-has-error="''+hasError">
<style lang="scss" scoped>
.sample {
color: green;
&[aria-disabled="true"] {
color: grey;
}
&[data-has-error="true"] {
color: red;
}
}
</style>
恒常的なスタイルと可変的なスタイルが分離されて、コードの意図が明瞭になったのではないかと思います
このようにすると、WAI-ARIAで用意されているような性質の場合にはそれを使うことでアクセシビリティ対応も同時にできるという利点もあります。
適切なaria属性がない場合にはdata属性を使うようにするといいのではないかと思います。
一つ気をつけないといけないのはv-bindでbooleanをバインドする場合、falseが入ると属性自体が消えてしまうので、バインドする値は文字列に変換しておくほうが安全です。
<template>
<div class="sample" :aria-busy="loading"> # 良くない例。loadingがfalseだとaria-busyが付与されないため動かない </div>
<div class="sample" :aria-busy="''+ loading"> # 良い例。一度文字列に変換する。 :aria-busy="`${loading}`"のようにも書ける </div>
</template>
<script>
data() {
return {
loading: false;
}
}
</script>
<style lang="scss" scoped>
.sample {
&[aria-busy="false"] {
color: blue;
}
&[aria-busy="true"] {
color: grey;
}
}
</style>