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(重み付け) については次回