uhyohyo.net

JavaScript初級者から中級者になろう

五章第二回 CSSの構造

CSSは、文書中で独自の構造を持っています。今回から、その構造と操作について解説します。

CSSStyleSheet

CSSでも、それぞれの要素などは全てオブジェクトで表されます。まずは、そのうちCSSStyleSheetという種類のオブジェクトについて解説します。

CSSStyleSheetは、CSSそのもので最も大きなまとまりで、

<!doctype html>
<html>
  <head>
    <title>test</title>
    <style type="text/css">
      body {
          background-color: aqua;
      }
      div {
          background-color: white;
      }
      p {
          background-color: yellow;
      }
    </style>
  </head>
  <body>
    <p>testtest</p>
  </body>
</html>

というHTMLがあったとき、<style>〜</style>の間の

body {
  background-color: aqua;
}
div {
  background-color: white;
}
p {
  background-color: yellow;
}

これ全体を表すオブジェクトです。<style>〜</style>が2つ以上あった場合も、それぞれのstyle要素についてCSSStyleSheetがあります。また、link要素を使って外部のCSSを読み込んだ場合も、そのlink要素についてCSSStyleSheetがつくられます。

では、このCSSStyleSheetはどのように取得すればいいのでしょうか。

それには、documentが持つstyleSheetsというオブジェクトを使います。前述の通りCSSStyleSheetはいくつもある可能性があるので、これはその文書が持つCSSStyleSheetのリストです。

lengthプロパティはその数を表し、itemメソッドでひとつひとつのCSSStyleSheetを取得できます。NodeListなどと似たような使い方ができます。例えば最初のスタイルシートを取得するには、

document.styleSheets.item(0)

という感じです。また、省略形としてdocument.styleSheets[0]というような形もあります。

さて、このCSSStyleSheetは、いろいろプロパティなどを持っています。いろいろ適当に紹介します。

<!doctype html>
<html>
  <head>
    <title>test</title>
    <style type="text/css">
      body {
          background-color: aqua;
      }
      div {
          background-color: white;
      }
      p {
          background-color: yellow;
      }
    </style>
  </head>
  <body>
    <p>testtest</p>

    <script type="text/javascript">
    var sheet = document.styleSheets.item(0);

    alert(sheet.disabled);
    sheet.disabled = true;
    console.log(sheet.ownerNode);
    </script>
  </body>
</html>

まずdisabledというプロパティを表示しています。これは、そのスタイルシートを無効化するかどうかの設定で、真偽値です。最初は当然無効化されていないからfalseです。

alertで表示しても、falseが表示されます。その後、それをtrueに変更しています。trueということは無効化するということです。最初にアラートが出たときはまだスタイルシートが有効だから背景色がついていたのが、設定したあとはスタイルシートが無効になり背景色がつかなくなります。ちなみに今回console.logではなくわざわざalertを使っているのは、変化前と後を比べられるようにするためです。

次の行で出ているのは、ownerNodeというプロパティです。その名の通り、ノードが入っています。どんなノードかというと、そのスタイルシートのもととなるノードで、今回の場合、このスタイルシートはstyle要素によって作られたから、style要素のノードが入っています。

他にもありますが、とりあえずこの程度でしょう。

CSSRule

さて、CSSStyleSheetは、

body {
    background-color: aqua;
}
div {
    background-color: white;
}
p {
    background-color: yellow;
}
          

これ全体を表すものでした。これは、さらに細かく分けることができます。具体的には、

body {
    background-color: aqua;
}
div {
    background-color: white;
}
p {
    background-color: yellow;
}

の3つに分かれます。このスタイルシートは、セレクタ { 〜 }の組が3つととらえることができますね。ちなみに、セレクタとは、そのスタイルが適用される範囲を指定する部分です。今回の場合、「body」「div」「p」の部分です。

このひとつひとつを表すCSSRuleというオブジェクトがあります。そもそもCSSのセレクタとスタイルの組のことをルールというので、CSSRuleという名前はそこから来ています。これらは、全体を司るCSSStyleSheetの下にあると考えることができます。これは、ノードの親と子の関係に似ていて、次のように表せます。

CSSStyleSheet
|
├――CSSRule
|
├――CSSRule
|
└――CSSRule
          

これらはCSSStyleSheet側から取得することができます。

CSSStyleSheetはcssRulesというプロパティを持ち、それが、そのスタイルシートが持つCSSRuleのリストです。これまた、lengthプロパティとitemメソッドを持っています。このitemメソッドが返すのが、それぞれのCSSRuleです。

だから、例えば

document.styleSheets.item(0).cssRules.item(0)

のようにして取得します。こうしてみると長いですが、もちろん適宜document.styleSheets.item(0)を変数に代入するなどします。さて、上のサンプルの場合、最初のcssRuleは

body {
  background-color: aqua;
}

の部分のCSSRuleだから、それが返ってくることになります。

さて、それではこのCSSRuleからスタイルについての情報を得る方法ですが、いくつは方法はあります。ひとつは、cssTextプロパティを見る方法です。

このcssTextには、そのCSSRuleが表す部分の文字列がそのまま入っています。例えば、

<!doctype html>
<html>
  <head>
    <title>test</title>
    <style type="text/css">
      body {
          background-color: aqua;
      }
      div {
          background-color: white;
      }
      p {
          background-color: yellow;
      }
    </style>
  </head>
  <body>
    <p>testtest</p>

    <script type="text/javascript">
      var cssrule = document.styleSheets.item(0).cssRules.item(0);

      console.log(cssrule.cssText);
    </script>
  </body>
</html>
          

のようにしたら、最初のCSSRuleは

body {
  background-color: aqua;
}

の部分だから、それが表示されます。ただし、前回解説したのと同じで、書いてあるソースがそのままそっくり表示されているというわけではないかもしれません。しかし意味は同じはずです。

しかし、このように文字列で取得すると、人間から見ると分かりやすいですが、プログラムからは扱いづらいです。そこで、もっとプログラムで扱いやすい方法を紹介していきます。

CSSStyleRule

実は、CSSRuleには、さらに何種類も種類があります。今まで扱ってきた

セレクタ {
    スタイル
}

という書式で具体的にスタイルを記述するものは、CSSStyleRuleというオブジェクトで表されます。他にどんな種類があるかは後述します。

上のサンプルでは3つCSSRuleがありましたが、いずれもCSSStyleRuleです。それ以外の種類は出てきてません。

このCSSStyleRuleしか持たないプロパティが2つあります。

ひとつはselectorTextです。これには、セレクタの部分が文字列で入っています。

body {
    background-color: aqua;
}

の場合、セレクタは"body"ですね。このプロパティは変更可能です。

もうひとつはstyleです。このプロパティはCSSStyleDeclarationです。このCSSStyleDeclarationは前回出てきたもので、これを通してひとつひとつのCSSのプロパティを扱うことができるのでした。

このstyleが表すスタイルは、当然このCSSStyleRuleのスタイル指定です。ここを試しに変更してみましょう。

<!doctype html>
<html>
  <head>
    <title>test</title>
    <style type="text/css">
      body {
          background-color: aqua;
      }
      div {
          background-color: white;
      }
      p {
          background-color: yellow;
      }
    </style>
  </head>
  <body>
    <p>testtest</p>

    <script type="text/javascript">
      var cssrule = document.styleSheets.item(0).cssRules.item(0);

      cssrule.style.backgroundColor = "blue";
    </script>
  </body>
</html>

JavaScriptによってbackground-colorを"blue"に変更したので、背景色が青になっています。

また、CSSStyleDeclarationを操作ためのメソッドがいくつかあるので、紹介しておきます。最初に紹介するのはsetPropertyです。これは名前の通り、1つのプロパティの値をセットするものです。第一引数がプロパティ名で、第二引数が値です。つまり、端的にいえば、上のサンプルの

cssrule.style.backgroundColor = "blue";

は次の文と同じ意味です。

cssrule.style.setProperty("background-color","blue");

この書き方では、プロパティ名を文字列で指定するためハイフンマイナスの入ったプロパティ名もそのまま指定することができます。

また今回は省略しましたが、実は第三引数を指定することが可能です。これは文字列で、ようするに"important"を指定することができます。この指定をすると、

background-color: blue !important;

と指定したのと同じ意味になります。

逆に、セットされているプロパティの値を知るためのメソッドgetPropertyValueもあります。これは引数が一つだけで、プロパティ名です。つまり上の例のあとだと、cssrule.style.getPropertyValue("background-color")"blue"あるいは"#0000ffなどの値が返ってきます。返り値は常に文字列で、もしそのプロパティがセットされていなかったら""が返ってきます。

また、値ではなくimportantに相当する部分を取得するメソッドgetPropertyPriorityもあります。これも引数はプロパティ名です。もしなければこれまた""が返ってきます。

次に、プロパティを削除するメソッドremovePropertyを紹介します。これも引数がプロパティ名で、例えば

cssrule.style.removeProperty("background-color");

とした場合、background-color: blue;という一文が無かったことになります。

最後に、そのCSSStyleDeclarationにセットされているプロパティを全て調べたりするために、lengthプロパティとitemメソッドがあります。itemメソッドには0から(length-1)までの数値を引数として渡すことができ、その番号に対応したプロパティ名が返ります。上のだとcssrule.style.lengthは1で、cssrule.style.item(0)"background-color"が返ります。

その他のCSSRule

それでは、CSSStyleRule以外のCSSRuleについて解説します。

どういう場合にそれらが発生するのかというと、

セレクタ {
    スタイル
}

という形以外の記述があった場合にそれらが発生します。これ以外の書き方なんて見かけた事ないという人もいつかもしれませんが、この機会に知っておきましょう。

CSSCharsetRule

@charset "UTF-8"; のような記述がスタイルシートの先頭にあることがあります。見ての通り、そのスタイルシートの文字コードを書いておきます。HTML中にstyle要素を使って書く場合は、HTML文書の文字コードと同じである必要があります。

この文でひとつのCSSRuleが作られます。このとき作られるのは、CSSCharsetRuleというものです。

このCSSCharsetRuleが持つプロパティには、encodingがあります。これは簡単で、指定された文字コードが文字列で入っています。この場合は"UTF-8"ですね。

CSSCharsetRule

CSSFontFaceRuleは、次のような指定があるとき登場します。

@font-face {
  font-family: "testtesttest";
  src: url("http://example.com/test.ttf");
}

詳しい説明は省きますが、見て分かる通り、普通にスタイルを記述するのと同じ書式で設定をしているので、CSSStyleRuleと同じようにstyleプロパティを持ちます。

CSSImportRule

CSSImportRuleは、次のような指定があるとき登場します。

@import "style.css";

これは、スタイルシートの中から、別のスタイルシートを読み込むという指定です。HTMLから外部のcssを読み込むにはlink要素を使いますが、このようにCSS中からも読み込むことができます。

読み込み先のURLは、hrefというプロパティに入っています。これは書き換えできません。書き換えて読み込み先のファイルを変えるというような使い方はできません。

また、読み込んだスタイルシートをDOMで操作する方法もちゃんとあります。スタイルシートのファイルをまるまる1個読み込むのだから、読み込んだスタイルシートのファイルは上で解説したCSSStyleSheetで表されるはずです。

それは、styleSheetというプロパティで取得できます。これがあると、上ででてきた木構造も複雑になります。

CSSStyleSheet
|
├――CSSStyleRule
|
└――CSSImportRule
     |
     └――CSSStyleSheet
          |
          ├――CSSStyleRule
          …
          

という感じになりますね。

CSSMediaRule

CSSMediaRuleは、次のような指定があるとき登場します。

@media screen {
  スタイル
}

これはメディアによって適用されるスタイルを変えるための指定です。メディアとは、どういう手段でそのページを表示するかということです。

「当然パソコン(ブラウザ)で表示するに決まってる」と思うかもしれませんが、CSSではさまざまなメディアに対応ができるようになっています。

例えば上にもある「screen」なら普通にパソコンなどのブラウザで、他に「print」なら印刷時のときのスタイル、「aural」なら音声読み上げブラウザ用、「tty」なら携帯端末などの限られた表示領域を持つもの用…などと決められています。

つまり、パソコンで表示したときと印刷しようとしたときでは見た目が違うなんてことが起こりうるわけです。

さて、上の場合「screen」だから普通にパソコンで表示したときのみ適用されるスタイルを記述するわけですが、具体的にどこに記述するかというと、

@media screen {
  body {
    background-color: yellow;
    font-size:100em;
  }
  div {
    font-size:100em;
  }
}

のように、@media { 〜 }の中に記述します。

ここで注目するのが、CSSMediaRuleはあくまで@media { 〜 }をひとまとまりにして扱うので、そのCSSMediaRuleが別のCSSRuleを内包しているということです。

CSSStyleSheet
|
├――CSSStyleRule
|
└――CSSMediaRule
      |
      ├――CSSStyleRule
      …
          

という状態ですね。

これらを扱うために、CSSMediaRuleも、CSSStyleSheetと同じようにcssRulesを持っていて、同じように扱うことができます。これは前述のCSSImportRuleと違い、CSSStyleSheetを介さない点で違うので注意してください。CSSImportRuleはあくまで別のまとまりとしてのCSSを読み込むということでこのような違いが生じています。

また、どのメディアを対象にしているかを知るために、mediaというプロパティを持っています。このプロパティはMediaListというもので、名前のとおり対象となるメディアのリストです。

lengthとitemを持つリストです。itemは、メディアひとつを文字列で返します。また、mediaTextというプロパティは、メディアを全てコンマで区切ってまとめた文字列です。

CSSPageRule

CSSPageRuleは、次のような場合に登場します。

@page {
  スタイル
}

これは、

@page {
  margin: 3px;
}

のように中にスタイルを記述できるので、それを取得するためにstyleプロパティを持ちます。これはCSSStyleDeclarationだから、CSSStyleRuleのそれと同様に扱えます。

また、

@page:first {
}

のような場合にセレクタ部分を取得するために、selectorTextというプロパティも持っています。これは文字列で、今回の場合"@page:first"のように入っています。

とりあえず、これでCSSRuleの種類をすべて解説しました。CSSStyleSheetなどのcssRulesには、いろいろな種類のCSSRulesが一直線に並んで入っていることになります。CSSStyleSheet以外は書かないよという人はいいですが、どんな種類か分からないCSSRuleがあるとき、それの種類を知る必要があることもあります。

その方法はちゃんとあります。どんなCSSRuleも、typeというプロパティを持っていて、この値によってどの種類のCSSRuleであるか判別できます。この値は数値です。

1から順番に、CSSStyleRule、CSSCharsetRule、CSSImportRule、CSSMediaRule、CSSFontFaceRule、CSSPageRuleとなっています。

typeの値とこれらの数値を比較すればいいわけです。例えば、typeが1ならそれはCSSStyleRuleであることがわかります。

しかし、数値を比較すると分かりにくいので、定数というものがあります。

定数とは、変数のように値に名前がついたものですが、その値は固定されています。定数に名前がついていることによって、その数値が何を意味するのかが分かりやすくなります。

具体的に、1から順にSTYLE_RULE,CHARSET_RULE,IMPORT_RULE,MEDIA_RULE,FONT_FACE_RULE,PAGE_RULEという名前の定数があります。上と対応していますね。これらは、CSSRuleのプロパティとして参照することができます。つまり、変数aaaがあるCSSRuleであるとき、

aaa.type == aaa.STYLE_RULE

とすることでaaaがCSSSTyleRuleである場合のみ処理をしたりできます。見た目にも、それがCSSStyleRuleであることを確かめているのが分かりやすいと思います。これが

aaa.type == 1

であると、やっていることは同じですが、上と比べて分かりにくいですね。

今回は構造を知って参照するだけでしたが、次回はさまざまな操作などを解説します。