はじめに
記事を見ていただいて、ありがとうございます!
Webエンジニアをしているsannoと申します。
CSSってWebシステムを開発する上で、知っていて当たり前って顔してますが難しいですよね…
CSSを指定しているのに、なんでスタイルが当たらないの、といったことはあるあるだと思います。
見知らぬところでスタイルを当てていて、なぜそこで効かせているのだと頭を抱えたり。
それが秩序に則ってそうしているのであれば問題ないのですが、意図していないものだとさらに頭を抱えたり。
そこで本来であれば大元のCSSを修正すべきですが、何らかの理由でそれが許されないという状況もあるかもしれません。
そんな時にスタイルを上書きする方法を知っていれば、助かることもあるかもしれません。
特に今回は大いなる力である!importantは使わずに、スタイルを上書きする方法を考えたいと思います。
With great power comes great responsibility
スパイダーマン
!importantを使わずにスタイルを上書きする方法
最初に結論
!importantを使わずにスタイルを上書きする方法について先に結論を言ってしまいます。
その方法は、大元のスタイルを指定しているCSSセレクターよりも詳細度の高いCSSセレクターを使って上書きするということです。
キーワードは詳細度です。
詳細度の正確な説明については上記のMDNのリンクを参照していただきたいですが、要点を抜き出して説明すると以下のようなことです。
詳細度の計算方法
詳細度のアルゴリズムは、CSS セレクターの重みを計算し、競合する CSS 宣言の中からどのルールを要素に適用するかを決定します。
詳細度
CSSセレクターの重みによって、詳細度を計算するアルゴリズムがあります。
詳細度アルゴリズムは、基本的に 3 種類のセレクターに対応する ID、CLASS、TYPE という 3 つの分類または重みの 3 列の値で構成されます。この値は、それぞれの重み分類に含まれるセレクターの成分の数を表し、ID – CLASS – TYPE のように書かれます。3 つの列は、要素に一致するセレクターの中で、各セレクターの重み分類のセレクターの成分の数を数えることによって作成されます。
詳細度
詳細度はCSSセレクターのID, CLASS, TYPE を使って計算します。
ID, CLASS, TYPEの個数を数え、ID-CLASS-TYPEの形で表して詳細度を比較します。
これだけだとイメージできないと思いますので、具体例で見てみましょう
例えば以下のようなCSSがあった場合に、詳細度は以下のようになります。
#child-id {
color: blue;
}
/* 1-0-0 */
.child-class {
width: 50px;
}
/* 0-1-0 */
p {
color: orange;
}
/* 0-0-1 */
.parent-class div input:required {
width: 100px;
}
/* 0-2-2 */
#child-idはID、.child-classはCLASS、pはTYPE、とそれぞれ対応します。
ですので、ID-CLASS-TYPEに当てはめてみるとそれぞれ、1-0-0、0-1-0、0-0-1、となります。
もう少し複雑な.parent-id div input:requiredはどうでしょうか。
それぞれの成分は以下のような対応になります。
CSSセレクターの成分 | 種類 |
.parent-id | CLASS |
div | TYPE |
input | TYPE |
:required | CLASS |
ですので、.parent-id div input:requiredの詳細度は0-2-2になります。
また、詳細度の計算結果から、適用するスタイルはどのように決定するのでしょうか?
同じ要素に異なるプロパティ値を提供する宣言が 2 つ以上ある場合、一致するセレクターを持つスタイルブロックの宣言値のうち、アルゴリズムによる重みが最も大きいものが適用されます。
詳細度
詳細度の計算結果で重みが最も大きいものが適用されます。
比較方法が分からないと、重みが最も大きいものを見つけられませんね。
詳細度の計算結果を比較する方法について、実際のコードで確認してみましょう。
See the Pen Untitled by hashipon (@rikuro-aub) on CodePen.
child-idを付与したpを表示しているpタグについて、pと#child-idのCSSセレクターが競合しています。
この時に比較する詳細度の計算結果は、1-0-0と0-0-1ですね。
それぞれの列を前から比較していって、先に大きな値が出てきた方の勝ちです。
列の比較で勝敗が決まったら、それ以降の列は比較しません。
/* #child-idのCSSセレクター */ 勝ち 1 - 0 - 0 ↕︎1の勝ち 0 - 0 - 1 /* pのCSSセレクター */ 負け
詳細度の計算結果を前の列から比較し、先に大きな数値が出てきた#child-idの勝ちで、#child-idのスタイルが適用されます。
もう一つ例を見てみましょう。
divで囲ってchild-classを付与したinputのラベルを持つ入力欄で、.child-classと.parent-class div input:requiredのCSSセレクターが競合しています。
こちらも同様に、それぞれの列を前から比較してみましょう。
/* .child-classのCSSセレクター */ 負け 0 - 1 - 0 ↕︎引き分け ↕︎2の勝ち 0 - 2 - 2 /* .parent-class div input:requiredのCSSセレクター */ 勝ち
詳細度の計算結果を前の列から比較し、先に大きな数値が出てきた.parent-class div input:requiredの勝ちで、.parent-class div input:requiredのスタイルが適用されます。
!importantを使わずにスタイルを上書きする方法
詳細度の計算方法が分かったところで、!importantを使わずにスタイルを上書きする方法に戻ると、以下でしたね。
大元のスタイルを指定しているCSSセレクターよりも詳細度の高いCSSセレクターを使って上書きする。
詳細度を高くする方法はいくつもあると思うのですが、一例を実際のコードで確認してみます。
See the Pen !importantを打ち消す by hashipon (@rikuro-aub) on CodePen.
.parent-class p CSSセレクターの詳細度は0-1-1です。
.parent-classを持つ要素の子孫pタグはすべて、.parent-class pのスタイルが適用されます。
.parent-classを持つ要素の子孫pタグに別のスタイルを適用したいときに、別のclass(コードの例で.child-class)を用意して適用するのが単純で良さそうです。
ですが、そのまま.child-classのCSSセレクターでスタイルを適用しても、詳細度は0-1-0で.parent-class pの詳細度0-1-1には勝てません。
そこで、トリッキーですが.child-class.child-classのCSSセレクターで同じclassを2つ繋げることで、詳細度を0-2-0にします。
詳細度を0-2-0にすることで0-1-1に勝つことができ、無事に目的のスタイルを適用することができました。
この、同じclass繋げて詳細度を高くする方法は以下の記事を参考にしました。
詳細度を高くする他の方法も紹介されていて、とても素敵な記事です。
おわりに
CSSを上書きする方法はいかがでしたでしょうか?
CSSを秩序ある形で保つために、詳細度を理解しておくことは重要だと思います。
詳細度を高くすることで解決できるところを!importantで解決してしまうと、後の自分や仲間を苦しめることに繋がってしまうかもしれません。
そうならないために、詳細度を理解して、!important以外でスタイルを上書きする方法を知っておくのは大切ではないでしょうか。
この記事がなにかのヒントになりましたら、幸いです。
最後まで記事を読んでいただいて、ありがとうございました!!
コメント