【Vue.js】動的クラスとスタイルを切り替える方法|v-bindと条件分岐の組み合わせ

【Vue.js】動的クラスとスタイルを切り替える方法|v-bindと条件分岐の組み合わせ Vue.js

状態によって見た目を変えたい場面は、とても多くあります。選択中のタブを強調する、エラーの入力欄を赤くする、進み具合に合わせてバーの幅を変える、などです。Vueでは、:classと:styleが、そのための専用の仕組みです。この記事では、Vue 3(<script setup>)を前提に、基本の書き方から、使い分けの判断、アクセシビリティまでを整理します。

スポンサーリンク

クラスの切り替え:オブジェクト構文

オブジェクトのキーがクラス名、値が条件です。値が真のキーだけが、クラスとして付きます。静的なclassとも併用でき、両方が合わさって出力されます。クラス名にハイフンを含むときは、'text-danger'のように、引用符で囲みます。

Vue:オブジェクト構文
<script setup>
import { ref } from 'vue'

const isActive = ref(true)
const hasError = ref(false)
</script>

<template>
  <!-- 値が真のキーだけが、クラスになる -->
  <div :class="{ active: isActive, error: hasError }">状態によってクラスが変わる</div>

  <!-- 静的な class と併用できる → class="box active" -->
  <div class="box" :class="{ active: isActive, 'text-danger': hasError }">併用する例</div>
</template>

クラスの切り替え:配列構文

複数のクラスを並べたいときは、配列構文を使います。配列の中の偽の値(空文字、false、null)は、無視されるので、hasError && 'has-error'のような書き方ができます。配列の中に、オブジェクトを混ぜることもできます。

Vue:配列構文と混在
<script setup>
import { ref } from 'vue'

const baseClass = ref('card')
const isActive = ref(true)
const hasError = ref(false)
</script>

<template>
  <!-- 配列の中では、偽の値(''・false・null)は無視される -->
  <div :class="[baseClass, isActive ? 'is-active' : '', hasError && 'has-error']">配列構文</div>

  <!-- 配列の中に、オブジェクトを混ぜる -->
  <div :class="[baseClass, { 'is-active': isActive, 'has-error': hasError }]">混在</div>
</template>

条件が複雑なら computed にまとめる

テンプレートに条件を書きすぎると、読みにくくなります。条件が複雑なときは、computedでオブジェクトを作り、:classに渡します。computedの基本はcomputedプロパティで動的なデータ処理を行う方法をご覧ください。

Vue:computedでクラスのオブジェクトを作る
<script setup>
import { ref, computed } from 'vue'

const isActive = ref(true)
const error = ref(null)

// 条件が複雑なときは、computed にまとめる
const classObject = computed(() => ({
  active: isActive.value && !error.value,
  'text-danger': error.value && error.value.type === 'fatal'
}))
</script>

<template>
  <div :class="classObject">computed で作ったクラス</div>
</template>

コンポーネントにクラスを渡す

ルート要素が1つのコンポーネントにclassを渡すと、子のルート要素のクラスと、自動でマージされます。ルート要素が複数あるコンポーネントでは、どの要素にクラスを付けるかが決められないので、$attrs.classで明示的に結びつけます。

Vue:コンポーネントへのclass
<!-- 子コンポーネント MyButton.vue(ルート要素が1つ) -->
<template>
  <button class="btn">ボタン</button>
</template>

<!-- 親:渡した class は、子のルート要素のクラスとマージされる -->
<MyButton class="primary" :class="{ large: isLarge }" />
<!-- → <button class="btn primary large"> -->

<!-- ルート要素が複数あるときは、どこに付けるかを $attrs.class で明示する -->
<!-- <p :class="$attrs.class">...</p><span>...</span> -->

インラインスタイルの切り替え::style

:styleは、CSSのプロパティをオブジェクトで渡します。プロパティ名は、camelCase(fontSize)が推奨ですが、ハイフンのままでも、引用符で囲めば書けます。配列で複数のスタイルを渡すと、マージされて、同じ要素に適用されます。ベンダープレフィックスは、Vueが実行時に、ブラウザの対応に応じて自動で付けます。

Vue:styleのオブジェクトと配列
<script setup>
import { ref } from 'vue'

const progress = ref(40)
const fontSize = ref(16)
</script>

<template>
  <!-- 実行時にしか決まらない数値だけを、:style にする -->
  <div class="bar" :style="{ width: progress + '%' }"></div>

  <!-- camelCase が推奨。ハイフンを使うなら、引用符で囲む -->
  <p :style="{ fontSize: fontSize + 'px' }">camelCase</p>
  <p :style="{ 'font-size': fontSize + 'px' }">kebab-case</p>

  <!-- 配列で、複数のスタイルをマージできる(あとのものが優先) -->
  <div :style="[baseStyles, overridingStyles]"></div>
</template>

CSSで書けることは、CSSに任せる

状態に応じた見た目の切り替えは、基本は、クラスの付け外しで行います。:styleは、実行時にしか決まらない数値(バーの幅の%など)に限ると、整理しやすくなります。ホバーのような、CSSで書ける状態を、JavaScriptで管理する必要はありません。固定の値(transitionなど)も、CSSのクラスに書きます。

Vue:避けたい書き方と、良い書き方
<!-- 避けたい書き方:JavaScript でホバーを管理し、固定の値まで :style に書く -->
<div
  :class="{ highlight: isHovered }"
  :style="{ width: progress + '%', transition: 'width 0.3s' }"
  @mouseenter="isHovered = true"
  @mouseleave="isHovered = false"
>
</div>

<!-- 良い書き方:ホバーは CSS の :hover、固定の値は CSS、動的な数値だけを :style に -->
<div class="bar" :style="{ width: progress + '%' }"></div>

<style scoped>
.bar { transition: width 0.3s; }
.bar:hover { background: #42b983; }
</style>

styleの中でv-bind()を使う

Vue 3の単一ファイルコンポーネントでは、<style>の中でv-bind()を使い、状態をCSSの値に直接結びつけられます。JavaScriptの式を使う場合は、引用符で囲みます。公式の説明によると、実際の値は、ハッシュ化したCSSカスタムプロパティとしてコンパイルされ、コンポーネントのルート要素にインラインで適用されます。状態が変われば、リアクティブに更新されます。CSSの本体は静的なままなので、見通しがよくなります。

Vue: