javascriptでスタイルを追加する方法

注：用語があやふやです

## 経緯
ディスプレイのサイズから たくさんある要素のサイズを決めたくて，
*全ての要素に対してスタイルを動的に設定* するのではなく，
*cssセレクタ に対して設定するスタイルを動的に決定* したい
と考え，方法を探した．
(なんとなく後者の方が*軽そう*に思えたのが理由だが，実際のところどちらがいいのかは知らない．誰か教えて)

## 結論
1. javascriptによる style要素の追加／操作により，可能
2. css mediaクエリも可能
3. head要素に style要素を追加すれば良い

## チェック環境
1. Firefox43.0
2. Google Chrome 47.0
3. IE 9, IE 11

## 参考にしたページ
1. [Dr.ウーパのコンピュータ備忘録 より](http://upa-pc.blogspot.jp/2014/06/javascript-css-add-style.html)
2. [David Walsh Blog より](https://davidwalsh.name/add-rules-stylesheets)
3. 他

## コードサンプル
```js:cssController.js
var CSSController = CSSController || {};
(function(namespace){
    // ルールを追加する
    namespace.addRule = function(selector, styleName, value, media)
    {
        var sheet = setSheet(media);

        if (sheet.addRule) { // IE
            sheet.addRule(selector, styleName + ":" + value, sheet.rules.length);
        } else if (sheet.insertRule) { // ^IE
            var rule = selector + "{" + styleName + ":" + value + "}";
            var index = sheet.cssRules.length;
            sheet.insertRule(rule, index);
        }
    };

    // メディアクエリのテキストをキーとして，シートを管理している
    var sheets = {};
    function setSheet(media)
    {
        if (!media) {media = 0;}
        if (!sheets[media]) {
            var style = document.createElement("style");
            if (media) {
                style.setAttribute("media", media); // mediaクエリをセット
            }
            document.getElementsByTagName("head")[0].appendChild(style);
            sheets[media] = style.sheet;
        }
        return sheets[media];
    }

    function getSheet(media)
    {
        if (!media) {media = 0;}
        return sheets[media] || null;
    }
})(CSSController);

/* サンプルコード */
// jQuery を利用
$(function(){
    var wid = $(window).width();
    var media = "only screen"; // スクリーン上での表示にのみ適用
    if (wid < 500) {
        // 表示領域の横幅が500px未満なら .target の横幅を 100% に設定
        wid = '100%';
    } else {
        // 表示領域の横幅(=wid)が500px以上なら .target の横幅を (wid - 100)px に設定
        wid = (wid - 100) + 'px';
    }
    CSSController.addRule('.target', 'width', wid, media);
});
```

## 結論までの紆余曲折
試してダメだった方法などを書いてるので，興味なければ読まなくてどうぞ．
実行テストはFirefox と Firebug で行ってます．

### document.styleSheets を操作する

```js:failed1.js
var sheets = document.styleSheets;
for (var i = 0; i < sheets.length; i++) {
    var sheet = sheets[i];
    var rules = sheet.cssRules;
    for (var j = 0; j< rules.length; j++) { // ここでエラー
        var rule = rules[j];
        // ルールを操作
    }
}
```
javascriptでスタイルシートを操作する方法をweb検索して，最初に見つけたのがこの方法．
~~古い方法らしく，最近のブラウザは セキュリティ上の理由から sheet.cssRules に対して操作を行うことができない．~~
（何か勘違いがあったようです。できました）

### head要素に style要素を追加して，そこにルールを追加する(メディアクエリ非対応)
[参考にしたページ](#参考にしたページ)の 1 で見つけた方法．
最近のブラウザでも問題なく動作するが，メディアクエリを使いたかったのでどうすればいいかわからず困った．
`insertRule()` の引数となるルールは文字列で渡していたので，試しに

```js:failed2.js
var rule = "@media " + media + "{"
    + selector + "{" + styleName + ":" + value + "}"
    + "}";
sheet.insertRule(rule, index);
```
と いうようにやってみたけれど，メディアクエリの条件に関わらず，常にスタイルが反映されてしまった．
Firebugで console出力したルールオブジェクトを .cssファイルでメディアクエリを使った場合と読み比べてみたけれど，どう違うのかよくわからず，よくわからないまま *この方法だけじゃダメ* と結論した．

### メディアクエリの設定
その後 [参考にしたページ](#参考にしたページ)の 2 にて，メディアクエリの設定方法を見つけた．
メディアクエリの設定方法は，
**ルールではなくスタイル要素に対して media属性として メディアクエリを設定する**
というもの．
html中でstyle要素として設定する方法からすると当然の方法だけれど，スタイルは.cssファイルでしか書いてこなかったから思い至らなかった．

### 完成
以上を合わせて，sample.js の完成．