JavaScriptでCSSパーサーを書くための情報を収集中(2日目)
はい、JavaScript で クロスブラウザな CSSパーサー の2日目です。
今日は、<link rel="stylesheet" type="text/css" href="a.css" /> の読み込みや @import url(...) への対応にトライしてみます。
これが HTML です。
ロードが終わると、CSSの情報をかき集めて表示します。
<!DOCTYPE html><html><head><title></title>
<link rel="stylesheet" type="text/css" href="a.css" />
<style type="text/css"><!--
@import url('e.css');
:digit {
color: pink;
}
/* hoge
* hoge
* hoge */
--></style>
</head>
<body>
<script src="uuMeta.js"></script> // ブラウザ判別など
<script src="uuCSSParser.js"></script> // 主役
<script src="uuURL.js"></script> // URLハンドラ
<script src="uuAjax.js"></script> // uuAjax.sync() 同期読込機能を利用
<script>
window.onload = function() {
alert(uuCSSParser.parse()); // かき集めて表示
};
</script>
</body></html>折角なので、CSS ファイルの中でさらに、CSSファイルを読み込むようにしてあります。
最終的には a.css, b.css, c.css, d.css, e.css の5つのファイルが読み込まれます。
a.css
@import "b.css"; @import "d.css"; /* a.css */ a { background-color: #aaa; }
b.css
@import url("c.css"); /* b.css */ b { text-align: center; } b { background-color: #bbb; }
c.css
/* c.css */ canvas { background-color: #ccc; }
d.css
/* d.css */ div { background-color: #ddd; }
e.css
/* e.css */ pre { background-color: #eee; }
uuCSSParser.js です。
今は、CSSファイルをかき集めたり、コメントを除去するぐらいのことしかやっていません。
(function() {
var _cssparser, // inner namespace
_ie = document.uniqueID,
MEMENTO = "uuCSSParserMemento";
_cssparser = {
parse: function() {
// IEなら、style 要素のテキスト情報(生のCSS)が失われないように、
// element.uCSSParserMemento に退避(固着)しておく。
// なぜこの処理が必要かは、昨日の日記参照
_ie && _cssparser._memento();
var rv = _cssparser._collect();
// strip comment
return rv.replace(/<!\-\-|\-\->/g, ""). // <!-- ... -->
replace(/\/\*[^*]*\*+([^\/][^*]*\*+)*\//g, ""). // /* ... */
replace(/\s*[\r\n]+/g, "\n"); // ___[CRLF]
},
_memento: function() {
var node = document.getElementsByTagName("style"),
i = 0, iz = node.length;
for (; i < iz; ++i) {
node[i][MEMENTO] = node[i].innerHTML;
}
},
_collect: function() {
var rv = [], node = document.styleSheets, i = 0, iz = node.length,
key = _ie ? "owningElement" : "ownerNode",
key2 = _ie ? MEMENTO : "textContent",
rex = /(?:^|[\r\n]+|\s+)@import\s*(?:url)?[\("']+\s*([\w\.\/\+\-]+)\s*["'\)]+\s*([\w]+)?\s*;/g;
// @import url(...) を見つけたら再帰的に読み込む
// CSSの仕様(というよりブラウザの実装かな?)では、@規則はルールの前に記述することになっているが、
// 現在は CSS の途中に @import を見つけても include してしまう。
function load(css) {
return css.replace(rex, function(m, url, media) {
return load(uuAjax.sync(url));
});
}
for (; i < iz; ++i) {
if (!node[i].disabled) { // style.disabled = true なら無視する
if (node[i].href && // link には URLが設定されている
!/\.html?$/.test(node[i].href)) { // Firefox2.0 は style要素にページのURLが設定されてしまうので(バグ)
// 拡張子が .htm または .html なら対象外としている
// 拡張子を持たない HTML ページで問題になりそうなので見直しが必要かも
// <link href="..."> を読み込む
rv.push(load(uuAjax.sync(node[i].href)));
} else {
// <style>...</style> を読み込む。
// IE なら退避しておいた element.uCSSParserMemento から読み込む
rv.push(load(node[i][key][key2])); //
}
}
}
return rv.join(""); // 読み込んだCSSファイルの内容を結合して返す
}
};
window.uuCSSParser = _cssparser; // export
})(); // uuCSSParser scope実行するとこうなります
canvas { background-color: #ccc; } b { text-align: center; } b { background-color: #bbb; } div { background-color: #ddd; } a { background-color: #aaa; } pre { background-color: #eee; } :digit { color: pink; }
!important と specificity(重み付け) については次回