In 'n' Out

知識を取り込み、そして発信する

親子・子孫・兄弟の指定を理解する

HTML構造を理解するとセレクタの意味が見えてくる

CSSセレクタは、どの要素にスタイルを当てるかを指定するための仕組みです。
その指定は、HTMLの構造をどう捉えるかによって大きく変わります。
ここでは親子・子孫・兄弟といった構造をもとに、基本的なセレクタの考え方を整理します。

親子関係とは?

親・子・兄弟・孫と言われても、タグに親とかの関係性があるのか聞いてもわかりにくいと思います。 まずはHTMLタグとイメージを表示します。

コード例

<div>
    <h4></h4>
    <p></p>
    <div>
        <p></p>
    </div>
    <ul>
        <li></li>
    </ul>
</div>
親子関係イメージ図

コード例に対するイメージ図を見てください。
ピンクの背景に含まれる要素が親、水色の背景に含まれる要素が子、緑の背景色に含まれる要素が孫となります。
ただし、一番上に来るものが親なのかというと必ずしもそうではありません。
水色の子要素を親要素として扱う時は、緑の孫要素は子要素にあたります。
つまり、親・子・孫という呼び方は、常に相対的な関係であり、要素そのものに固定された役割があるわけではありません。
HTMLの家系図と言えばわかりやすいと思います。
このイメージを頭に思い浮かべつつ、以下のセレクタの解説を見ると腑に落ちやすいのではないでしょうか。

親子セレクタ

要素の直下関係だけを対象にするセレクタです。

構文

親セレクタ > 子セレクタ{
    プロパティ:値;
}

親セレクタの直下にある子セレクタのみを指定し、孫要素以降には影響しません。

コード例

<div class="parent">
    <p>1つ目の段落</p>
        <div>
            <p>ここにはCSSが適用されません。</p>
        </div>
    <p>2つ目の段落</p>
</div>
.parent > p{
    background-color: #ceffbe;
}

出力例

1つ目の段落

ここにはCSSが適用されません。

2つ目の段落

parentクラス直下のpには緑の背景色が付きましたが、段落の中にあるdivの下にあるpには緑の背景色が適用されません。
しかし、2つ目の段落には緑の背景色が適用されます。
これは.parentクラスを親としてその直下にあるpに背景色を付けなさいという指定が成立しているためです。
そのため、pの直下にあるdiv以降の要素は子要素と認識されないため、このような出力例になっています。
構造を厳密に反映できるため安全ですが、HTML階層変更時は指定が効かなくなる点に注意しましょう。

子孫セレクタ

指定要素内に含まれるすべての対象要素に適用します。

構文

セレクタ1 セレクタ2{
    プロパティ:値;
}

セレクタ1の中に存在するセレクタ2を階層を問わずまとめて指定します。

コード例

<div class="parent">
    <p>
        1つ目の段落
        <div>
            <p>ここにもCSSが適用されます。</p>
        </div>
    </p>
    <p>2つ目の段落</p>
    <div>
        <p>ここにもCSSが適用されます。</p>
</div>
.parent p{
    background-color: #ceffbe;
}

出力例

1つ目の段落

ここにもCSSが適用されます。

2つ目の段落

ここにもCSSが適用されます。

parentクラス直下のpは当然のこと、孫要素などすべてのpに緑の背景色が付きます。
これは.parentクラスにあるpに背景色を付けなさいという指定が成立しているためです。
そのため、pタグのすべてに背景色が反映されることになります。
柔軟な反面影響範囲が広がりやすく、深い階層では意図しない適用に注意が必要です。

兄弟セレクタ

同じ親を持つ要素同士の並びを利用した指定方法です。

構文

セレクタ1 + セレクタ2{
    プロパティ:値;
}

セレクタ1 ~ セレクタ2{
    プロパティ:値;
}

セレクタ1 + セレクタ2は直後のセレクタ2のみ、セレクタ1 ~ セレクタ2は後続するすべてのセレクタ2を対象にします。

コード例

<div>
    <h3 class="brother1">兄弟1のみに反映されます。</h3>
    <p>兄弟1</p>
    <p>兄弟2</p>
    <p>兄弟3</p>
</div>
<div>
    <h3 class="brother2">兄弟すべてに反映されます。</h3>
    <p>兄弟1</p>
    <p>兄弟2</p>
    <p>兄弟3</p>
    <div>
        <h4>孫要素なので反映されません。</h4>
        <p>孫</p>
    </div>
</div>
.brother1 + p{
    background-color: #ceffbe;
}
        
.brother2 ~ p{
    background-color: #ceffbe;
}

出力例

兄弟1のみに反映されます。

兄弟1

兄弟2

兄弟3

兄弟すべてに反映されます。

兄弟1

兄弟2

兄弟3

孫要素なので反映されません。

1つ目の例は構造的に親であるdivの下に子要素としてh3とpがあります。
h3とpは同じdiv下にあるため兄弟要素となります。
.brother1 + pを指定すると、指定したセレクタの1番目の要素の次に指定した要素に背景色が反映されます。
一方で2つ目の例は1つ目の例に孫要素であるdivとpが存在しています。
.brother2 ~ pを指定すると、指定したセレクタの1番目の要素の次に指定した要素すべてに背景色が反映されます。
しかし、孫要素には背景色が反映されていません。
兄弟セレクタは「同じ親を持つ」かつ「後ろに並んでいる要素」のみが対象になります。
順序に依存するためHTMLの並びが変わると効果が変化します。

構造を意識するとセレクタは迷わなくなる

ここまで、HTML構造をもとに要素を指定する基本的なCSSセレクタを見てきました。
構造を意識してセレクタを選ぶことで、指定の理由が明確になり、スタイルの影響範囲も把握しやすくなります。
この考え方を土台として、次の要素の状態や順番といった別の条件を使った指定方法を理解していくと、CSSの表現の幅はさらに広がっていきます。