CommentOut

先に出てくる兄弟要素にスタイルを効かせる方法 先に出てくる兄弟要素にスタイルを効かせる方法

先に出てくる兄弟要素にスタイルを効かせる方法

公開日:  最終更新日:

何言ってるかわからないと思いますが、例えば以下のような時

<div class="group">
    <label>名前</label>
    <input type="text"/>
</div>

上記のようなHTMLで、テキスト入力にフォーカスされたら、ラベルタグの背景色を変えたい時
普通に考えると、兄弟要素がフォーカスされた時に、ラベルの背景色を変えるので以下のようになります。

input[type^=text]:focus + label {
    background: #000; // 変更後の背景色
}

しかし、このCSSは効きません。

どうして兄弟要素に兄弟セレクタが効かない?

兄弟セレクタには大きな罠があります。
『兄弟』セレクタなので、兄弟で例えてわかりやすく説明しましょう。
兄弟セレクタは“弟”や“妹”には効かせられるが、“兄”や”姉”には効かせられないんです!

つまり先ほどの例だと、inputタグの状態に合わせて、labelの色を変えようとしました。

<div class="group">
    <label>名前</label>
    <input type="text"/>
</div>

しかし、inputにとって、labelは先に定義されているので、兄や姉の関係になるんですね。
兄弟セレクタは先に出てきた要素には効かせられないので、labelにはスタイルを反映させることができないんです。

先に定義されたタグには兄弟セレクタでスタイルを切り替えられない?

結論から言うと、「はい、先に定義された要素には兄弟セレクタでスタイルを切り替えることができません。」

しかし、やりたい動きを実現する方法はあります。(あきらめないで!)

兄弟要素に兄弟セレクタでスタイルを効かせる方法

以下のサンプルを見てください。

See the Pen 兄弟要素を動かす by Uilou (@uilou754) on CodePen.

このサンプルでは、入力エリアがフォーカスされると一緒にラベルの色も変わります。
本来であれば、左に配置されているラベルが先に書かれるのでラベルは兄弟要素で指定できないのですが、上記サンプルではCSSを以下のように記述しています。

// 入力エリアがフォーカスされた時、兄弟要素のラベルのスタイルを指定
.input-bundle > input[type^=text]:focus + label {
	color: white;
	background: #66f;
}

これがなぜ動くのか

CSSのflex-directionでreverseを使う

まず、labelやinputを囲っているdivタグをflexにしています。

// ラベルと入力エリアをまとめる要素
.input-bundle {
	display: flex;
}

そして、flex-directionを使って、labelとinputを横並びにしています。

// ラベルと入力エリアをまとめる要素
.input-bundle {
	display: flex;
	flex-direction: row;
}

ただし、flex-direction: row;で横並びにしても、先ほどと同様に兄弟セレクタではスタイルを切り替えられません。
そこで使うのが、row-reverseです。

<div class="input-bundle">
	<input type="text" placeholder="お名前を入力してください。">
	<label>お名前</label>
</div>

labelとinputの記述順を逆にします。

// ラベルと入力エリアをまとめる要素
.input-bundle {
	display: flex;
	flex-direction: row-reverse;
}

flex-direction: row-reverse;を使います。

DOMの順番としてはinput → labelの順番なのですが、row-reverseにより、横並びの順番反転状態になります。

See the Pen 兄弟要素を動かす(reverseのon/off) by Uilou (@uilou754) on CodePen.

比較用

このように、row-reverseで表示だけ反転させることで、DOMの上ではinput → labelの順番なので、先ほどの“input[type^=text]:focus + label”でスタイル指定が効くのです。

// 入力エリアがフォーカスされた時、兄弟要素のラベルのスタイルを指定
.input-bundle > input[type^=text]:focus + label {
	color: white;
	background: #66f;
}

このように、一見仕様上実現不可能に思えることでも、工夫次第で解決できてしまうのが、CSSの面白い所でもあり、厄介な所でもあります。
私も最初にCSS触り始めた頃は兄弟要素の指定でかなり混乱しました。

display: flex;で片方は固定幅で、片方は可変幅にする方法

ちなみに、先ほどのサンプルでは可変幅に対応しています。
画面の横幅が小さくなると、ラベル部分は変わらず、入力エリアの幅が変わります。
このようにflexの中で画面幅に合わせて、片方は固定幅で、片方は横幅が動くようにする方法があります。

// 2つの要素を横並びにするラッパー
.flex_block {
    display: flex;
    flex-direction: row;
}
// 固定幅の要素
.flex_block > .width_fix {
    width: 100px;
}
// 可変幅の要素
.flex_block > .width_variable {
    flex: 1;
}

上記のように、固定幅の要素には実際の横幅を設定し、可変幅にはflex: 1;を設定します。
そうすると、flex: 1;を指定した方が親要素の幅に合わせて、伸縮するようになります。

宣伝
WordPressサイトのテンプレート編集やトラブル対応、バグ修正、簡単なJavascriptの作成(カルーセルやバリデーション等)など、小規模なスポット対応を受け付けております。
もしお困りごとがありましたら、お問い合わせフォームよりご相談ください。

この記事を書いた人

uilou

uilou

プログラマー

基本的に、自分自身の備忘録のつもりでブログを書いています。 自分と同じ所で詰まった人の助けになれば良いかなと思います。 システムのリファクタリングを得意としており、バックエンド、フロントエンド、アプリケーション、SQLなど幅広い知識と経験があります。 広いだけでなく、知識をもっと深堀りしていきたいですね。