IT戦記

プログラミング、起業などについて書いているプログラマーのブログです😚

document.createStyleSheet で動的に CSS を生成

今まで

var element = document.createElement('style');
document.getElementsByTagName('head')[0].appendChild(element);
if (ie) {
    var sheet = element.styleSheet;
}
else {
    var sheet = element.sheet;
}

みたいにやっていた。
ところが

dojo のソースを眺めていたら

IE には createStyleSheet という関数があるみたい。

createStyleSheet を使うとこうなる。

if (ie) {
    var sheet = document.createStyleSheet();
}
else {
    var element = document.createElement('style');
    document.getElementsByTagName('head')[0].appendChild(element);
    var sheet = element.sheet;
}

あまり代わり映えはしないけど。無駄な要素を挿入しなくてもいいので、こっちのほうが軽いかな。
IE は多数派なので、直しておこうっと。

Safari で CSSStyleSheet オブジェクトを生成する方法

今まで Safari では出来ないと思っていました

var element = document.createElement('style');
document.getElementsByTagName('head')[0].appendChild(element);
var sheet = element.sheet;
alert(sheet); // null

こんな感じで null になってしまうのです。。。
しかし!

さっき出来る方法が分かりました!

var element = document.createElement('style');

element.appendChild(document.createTextNode('')); // この行を追加

document.getElementsByTagName('head')[0].appendChild(element);
var sheet = element.sheet;

alert(sheet); // [object CSSStyleSheet]

このように TextNode を追加することで sheet オブジェクトが取得できるようになりました。

あー。どうして今まで試さなかったんだろう!

でも、分かってスッキリ!

Opera の CSSStyleSheet.insertRule はちょっとバグってる

以下のコード

var element = document.createElement('style');
element.appendChild(document.createTextNode(''));
document.getElementsByTagName('head')[0].appendChild(element);
var sheet = element.sheet;

// 以下のように CSSStyleSheet の先頭に二つのルールを挿入していく
sheet.insertRule('html body { background: gray }', 0); // 0 番目にこのルールを挿入
sheet.insertRule('html body { background: red }', 0);  // 0 番目にこのルールを挿入

このようにするとルールの順番は html body { background: gray } のほうが下にくるので、
カスケーディング順位(CSS のルールの優先順位)は html body{ background: red } より html body{ background: gray } のほうが上位になるはずである。
ということはこれを実行すると、body 要素の背景は gray になるはずである。

しかし Opera では

背景は red になってしまう。
これは、 OperainsertRule の第二引数に関わらず、ルールをルールリストの最後に挿入してしまうためである。

ブックマークレット化したので、実際にやってみてください。(IE は全くの独自仕様なのでこのブックマークレットはうごきません。あしからず)

javascript:var element=document.createElement('style');element.appendChild(document.createTextNode(''));document.getElementsByTagName('head')[0].appendChild(element);var sheet = element.sheet;sheet.insertRule('html body { background: gray }', 0);sheet.insertRule('html body { background: red }', 0);void(0);

まとめ

現状 insertRule を使う場合は、以下のようにルールをルールリストの最後に追加しなければブラウザ間の整合性が保てなくなってしまう。

sheet.insertRule('html body { background: black }', sheet.cssRules.length);

まあ、 IE 独自の addRule も最後にしか挿入できないし、どっちにしろそうなんだけどね(ぇ

IE で removeChild した要素はどこへいくか?

普通のブラウザだと

parent.removeChild(child);
alert(child.parentNode); // null

なぜか、 IE だと DocumentFragment が生成されている。

parent.removeChild(child);
alert(child.parentNode);          // [object]
alert(child.parentNode.nodeName); // #document-fragment

な、なぜ?

変なブラウザだなー