uhyohyo.net

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

五章第二回 CSSの構造

このページの最終更新日:

ご存知の通り、CSSは独自の文法を持っおり、したがって独自の構造を持っています。今回から、その構造と操作について解説します。

CSSStyleSheet

CSSを構成するさまざまな要素はDOMでは全てオブジェクトで表されます。まずは、そのうち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の取得

では、このCSSStyleSheetはどのように取得すればいいのでしょうか。ここでは方法を2つ紹介します。

1つ目の方法は、documentが持つstyleSheetsというプロパティを使います。前述の通りCSSStyleSheetはstyle要素やlink要素によって作られますから、それはdocumentに(HTML要素を通じて)結びついていると考えられます。このstyleSheetsプロパティは、そのdocumentに結びついたCSSStyleSheetの一覧です。使い方はNodeListなどと同様です。すなわち、lengthはその数を表し、またitemメソッドや[0]のようなインデックスアクセスにより個々のCSSStyleSheetを取得できます。例えば、最初のCSSStyleSheetを取得するには、document.styleSheets.item(0)またはdocument.styleSheets[0]とします。

もう1つの方法は、style要素やlink要素から対応するCSSStyleSheetを得る方法です。こちらのほうが、どのCSSStyleSheetを得たいのか明確になるのでいいかもしれません。それには、style要素またはlink要素のsheetプロパティを使います。次の例ではconsole.logでCSSStyleSheetが表示されます。


<!doctype html>
<html>
  <head>
    <title>test</title>
    <style type="text/css" id="mysheet">
      body {
          background-color: aqua;
      }
      div {
          background-color: white;
      }
      p {
          background-color: yellow;
      }
    </style>
  </head>
  <body>
    <p>testtest</p>
    <script>
      var style = document.getElementById('mysheet');
      console.log(style.sheet);
    </script>
  </body>
</html>

CSSStyleSheetのプロパティ

さて、この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というプロパティです。その名の通り、ノードが入っています。どんなノードかというと、そのCSSStyleSheetのもととなるノードで、今回の場合、このCSSStyleSheetはstyle要素によって作られたから、style要素のノードが入っています。言うなれば先に紹介したsheetの逆向きができるプロパティです。

残りはあとで紹介します。

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メソッドを持っています。

つまり、例えばsheet.cssRules[0]のようにしてCSSRuleを取得します。ただし、sheetはCSSStyleSheetとします。さて、上のサンプルの場合、最初(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>

のようにしたら、以下のような文字列が表示されるはずです。


body { background-color: aqua; }

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

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

なお、cssTextは読み取り専用です。これに新しい文字列を入れることでCSSRuleを書き換えることはできません。

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[0].cssRules[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)(またはcssrule.style[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」なら携帯端末などの限られた表示領域を持つもの用…などと決められています。

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

また、最近は@media (max-width: 600px){ … }のような指定で、PCとスマートフォンでCSSを切り替えるようなことも行われています。

さて、@mediaの中には複数のルールを書くことができます。よって、CSSStyleSheetと同様にcssRulesプロパティを持っています。これは前述のCSSImportRuleと違い、CSSStyleSheetを介さないので注意してください。

また、screenの部分を得るために、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であることがわかります。

しかし、数値を比較すると分かりにくいので、定数プロパティというものが用意されています。これらはCSSRuleのプロパティとしてあらかじめ用意されていて、決まった値が入っています。

具体的に、1から順にSTYLE_RULE,CHARSET_RULE,IMPORT_RULE,MEDIA_RULE,FONT_FACE_RULE,PAGE_RULEという名前の定数があります。上と対応していますね。これらは、CSSRuleのプロパティとして参照することができます。つまり、変数aaaがあるCSSRuleであるとき、aaa.STYLE_RULEには1が入っています。他の定数も同様です。これを用いて、aaa.type == aaa.STYLE_RULEとすることでaaaがCSSSTyleRuleであるかどうか調べることができます。見た目にも、それがCSSStyleRuleであることを確かめているのが分かりやすいと思います。これがaaa.type == 1であると、やっていることは同じですが、上と比べて分かりにくいですね。

今回は構造を知って参照するだけでしたが、次回はCSSRule自体の追加や削除などの操作を解説します。