uhyohyo.net

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

五章第三回 CSSの操作

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

前回は、CSSがどのような構造をしているのか説明しました。今回は、その構造をいじることについて解説します。

CSSRuleの追加と削除

まず、CSSRuleを子に持つCSSStyleSheetやCSSMediaRuleに、CSSRuleそのものを追加したり、あるいは削除したりする方法を解説します。

CSSStyleSheet(やCSSMediaRule)は、insertRuledeleteRuleという2つのメソッドを持っています。その名の通り、insertRuleは新しいCSSRuleを追加、deleteRuleは今あるCSSRuleを除去するメソッドです。

insertRule

まずinsertRuleの使い方から見ていきます。insertRuleは2つの引数を持ち、ひとつめの引数は追加するルール、ふたつめの引数は追加する位置です。これは何番目かを数値で指定するもので、ルールがその位置に追加されます。最初は0番目だから、たとえば0を指定すると一番先頭に追加されるということです。

ひとつめの引数のルールはどのような形で指定するかですが、appendChildなどのときはノードのオブジェクトを作成してそれを引数にしました。今回同じように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 stylesheet = document.styleSheets.item(0);

      stylesheet.insertRule("body { background-color:#00ff00; }", stylesheet.cssRules.length);
    </script>
  </body>
</html>

という感じです。insertRuleの第一引数には、body { background-color:#00ff00; }という、CSSのルールが文字列としてそのまま渡されています。これを渡すと、JavaScript側が解釈してCSSRuleをつくって追加してくれます。今回の場合、CSSStyleRuleが作られたことになりますね。

全体的に確認しておくと、変数stylesheetには、最初の行でCSSStyleSheetが代入されています。また、insertRuleの第二引数はstylesheet.cssRules.lengthが渡されています。cssRulesはそのCSSStyleSheetが持つCSSRuleの一覧で、lengthはその個数ということになります。だから、例えばlengthが3のとき、CSSRuleは0番目から2番目まであるということです。同様にlengthが5なら4番目まで、10なら9番目まであることになります。つまり、(length-1)番目までCSSRuleがあることになります。したがって、length番目に追加すると、それが一番最後に追加されます。この書き方は、一番最後に追加するための書き方だったのです。

さて、実行してみると、新しいCSSRuleが追加されて、新しいのに従って背景が緑に変わりましたね。今回学んだ方法で、ページ全体に自由なスタイルを適用することができます。

また、すでにbodyの背景色を指定する


body {
  background-color: aqua;
}

というのがありますが(これは0番目にあたりますね)、これとの兼ね合いも多少解説します。CSSでは、このように重複する指定があった場合、後ろにあるほうが優先されます。(ただし、セレクタによって優先度が変わるためそれとの兼ね合いもあります。今回の場合はセレクタはどちらもbodyで同じ順位です。)今回の場合、もともとあったのは0番、JavaScriptで追加したのは3番になるから、追加したほうが優先されます。

deleteRule

deleteRuleは、CSSRuleを1つ削除するメソッドです。引数は1つで、insertRuleと同じく数値です。その番号の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 stylesheet = document.styleSheets.item(0);

      stylesheet.deleteRule(0);
    </script>
  </body>
</html>

このコードでは、0番目、つまり最初の

body {
  background-color: aqua;
}

を削除していることになります。だから、bodyの背景色の指定がなくなって、背景はデフォルトの白(大抵の場合)になります。

CSSStyleSheetの追加と削除

上では、もともとあるCSSStyleSheetに新しいルールなどを追加するものでした。しかし、JavaScriptで新しいスタイルなどを作りたい場合、その文書にもともとCSSStyleSheetが(style要素やlink要素が)存在していない場合がありえます。そこで、CSSStyleSheetそのものを新しく作る必要がでてきます。

実は、DOMではCSSStyleSheetを直接作って追加する方法は提供されていません。そこで、どうするかというと、style要素を新しく作ってDOMに追加します。style要素やlink要素とCSSStyleSheetは一対一で対応しているので、こうすることで間接的に新しいCSSStyleSheetをつくったことになります。

前回見たように、style要素のsheetというプロパティが対応するCSSStyleSheetを表しています。できたばかりのCSSStyleSheetは空っぽなので、insertRuleで追加していくことになります。

つまり、例えばもともとstyle要素がない状態からp要素の背景を黄色くしたい場合は次のようにします。


<!doctype html>
<html>
  <head>
    <title>test</title>
  </head>
  <body>
    <p>なんとかなんとか</p>
    <p>なんとかかんとか</p>
    <p>かんとかかんとか</p>

    <script type="text/javascript">
      var newStyle = document.createElement('style');
      newStyle.type = "text/css";

      document.getElementsByTagName('head')[0].appendChild(newStyle);
      newStyle.sheet.insertRule("p { background-color:yellow; }",0);
    </script>
  </body>
</html>

できたstyle要素のノードのプロパティtypeに設定しているのは、style要素のtype属性に相当するものです。<style type="text/css">の部分ですね。昔はこのtype属性は必須でしたが、HTML5では必須でなくなりましたので、この行はなくても構いません。

その次は、document.getElementsByTagName('head')[0]にappendChildでstyle要素を追加しています。これは、0番目のhead要素ということです。head要素は文書中に1個しかありませんね。これで、正しくstyle要素が追加されました。

ちなみに、文書中のhead要素を取得するにはdocument.headとする方法もあります。ただし、これは比較的新しい方法なので、非対応ブラウザを心配するなら今回のような方法を使いましょう。

style要素を追加できたら、insertRuleでいよいよ具体的なルールを追加します。今回insertRuleの第二引数が0ですが、もともと空っぽなので0番目しか追加する場所がありませんね。

CSSStyleSheetの削除

CSSStyleSheetを削除するときも、同様に対応する要素を削除します。

つまり、こんな感じです。


<!doctype html>
<html>
  <head>
    <title>test</title>
    <style type="text/css">
      body {
        background-color: aqua;
      }
    </style>
  </head>
  <body>
    <p>testtest</p>
      <script type="text/javascript">
        var stylesheet = document.styleSheets[0];

        var ele = stylesheet.ownerNode;
        ele.parentNode.removeChild(ele);
    </script>
  </body>
</html>

変数stylesheetはひとつあるstyle要素のCSSStyleSheetです。

変数eleに代入しているのはstylesheetに対応するstyle要素ですね。

その次の行でremoveChildでそのstyle要素を除去しています。removeChildは二章第四回で出てきました。引数で指定した子ノードを除去するというものです。

つまり、つまり、eleを子ノードとして持つノード、つまりeleの親ノードに対してremoveChildを実行する必要があります。eleの親ノードは、つまりele.parentNodeです。

このサンプルを実行してみると、スタイルシートがなくなったことがわかります。

以上でCSSの構造に関する話はおしまいです。特に今回は大した内容が無いように思えますが、CSSの構造をいじることができればCSSに関する大抵のことはできます。機会があればぜひ試してみてください。