online comment (オンラインコメント)
uupaa.js のコメントは、多くの場合オンラインコメントとして記述しています。
function gc(abort) { if (abort) { // [IE6][IE7][FIX] xhr.abort throw exception // http://twitter.com/uupaa/status/26953945895 try { xhr && xhr.abort && xhr.abort(); } catch (err) {} } watchdogTimer && (clearTimeout(watchdogTimer), watchdogTimer = 0); xhr = null; //{@mb _gecko && uueventdetach(win, "beforeunload", ng); // [Gecko] //}@mb }
のように、URL( http://twitter.com/uupaa/status/26953945895 )を埋め込んでいます。
http://twitter.com/uupaa/status/26953945895 の中身は次のようになっており、コードの意味と背景が分かるような記述になっています。
IE6, IE7 では xhr.about に触れることが許されないケースがあるので( xhr.about にタッチするだけで例外発生 ) try 〜 catch で囲っとくべきだったか
なぜオンラインコメントなのか
ライブラリやフレームワークのコードはどういった環境や文字コードで利用されるか分からないため、ASCIIコード以外の文字コードは原則的に利用できません。
ASCIIコード以外の文字を使うと、読めないだけではなく、実行時エラーを発生させるおそれがあるからです。
オンラインコメントのメリット
オンラインコメントは、次のような使い方ができます。
- 日本語のコメントや、より詳細なコメントを望んでいるユーザのために利用できる
- 資料,画像,映像を活用した説明が可能になる
- 設計思想など、長くなりそうな場合は、短い英文とURLだけを埋め込み、ブログやツイッターに読み物の形で説明する
IE9RC で document が const になり再定義が不可能になった
IE9RC の挙動を調べています。
IE9RC では @amachang さんの
/*@cc_on var doc = document; eval('var document = doc'); @*/
が封印されてる
残念ながら、 @amachang さんの発案した方法がIE9で封印されてしまったようです。
IE9RC Release note http://msdn.microsoft.com/en-US/ie/ff468705.aspx
window.document が const になったため、再代入するとエラーになります。
http://jsdo.it/uupaa/Wq0w で試せます。
回避案
(function(document) { // ローカルスコープ doc.createElement("div"); })(document);
こんな感じにするといいかもしれません。ローカルスコープ内では document にアクセスすると、同じような効果が得られます。
ついでにナビ子記法の導入もおすすめです → http://handsout.jp/slide/1883
ナビ子記法のスライドに書いていない事
あまりメリットを書きすぎると胡散臭いだろうなと思い、スライドには書いてないことがあったりします。
ナビ子記法ならさらに高速化
var lib = {}: (function(doc) { // スコープ内部 lib.api1 = libapi1; // function lib.api2 = libapi2; // function lib.api3 = libapi3; // function })(docuemnt);
スコープ内部では、 lib.api1() として関数を呼び出さずに libapi1() として呼び出すと lib.xxx() 呼び出しに伴うコスト(ドット演算子の名前解決コスト)をカットできます。
ナビ子記法なら、コード圧縮率も向上
さらにコードの圧縮時(Minify)に、lib.api1() といったコードは x.api1() と8文字以下には圧縮できませんが、libapi1() と記述していると x() や xx() のように3〜4文字に圧縮されます。
lib.api1() は圧縮しても、せいぜい x.api1() にしかならない libapi1() を圧縮すると、 x() や xx() に圧縮される
ナビ子記法ならECMAScriptとJScriptの解釈の違いも発生しなくなる
IE6〜IE8のJScriptでは名前付きの匿名関数のスコープの扱いが ECMAScript-262 5th Edition や他のブラウザの実装とは異なるため、JScriptべったりなコードを書くとトラブルが発生する場合があります。
var fn = function fnName() {}; if (typeof fnName === "function") { alert( "maybe legacy JScript(IE6, IE7, IE8)" ); if (fn !== fnName) { alert( "??" ); } } else { alert( "ECMAScript standard" ); }
- WebKit, Gecko, Opera, IE9 なら "ECMAScript standard" が表示されます。
- IE6, IE7, IE8 なら、"maybe legacy JScript(IE6, IE7, IE8)" と "??" が表示されます。
- fn と fnName は何かが違うんでしょうね…
IE6〜IE8 において typeof fn は "function" ですが、ECMAScript 準拠であれば typeof fn は "undefined" になります。
ナビ子記法は、一石七鳥ぐらいある気がするけど
あんまりメリットばかりPUSHすると胡散臭くなるから自重モード
おまけ
uupaa.js では、uu.ready からコールバックされた時の第二引数が document オブジェクトなので、
uu.ready(function(uu, doc) { alert( doc.documentMode ); });
最初からナビ子記法のメリットをおいしくいただけます。
おまけのおまけ
第一引数には uupaa.js のルートオブジェクト(uu)が渡されるのですが、これを uu ではなく $ で受け取ると、ちょっとしたデジャブ感を楽しむ事ができます。
uu.ready(function($, doc) { $(".hoge").click(function() { }); });
#ちょっとだけですが
innerHTML = "" まとめ
HTML5をサポートしていないIE6〜IE8で、node.innerHTML = "
- innerHTML の前に document.createElement() が実行されていない
- node が DOM Tree に参加していない(オンザフライ)
これらを回避するには、IE6〜IE8で以下のようします。
document.createElement("section"); // HTML5 Shiv function build(fragment) { // @param HTMLDocumentFragmentString: "<nav>...</nav>" // @return DocumentFragment: var div = document.createElement("div"); div.style.display = "none"; document.body.appendChild(div); // DOM Tree に参加してから div.innerHTML = fragment; // innerHTML を実行する var rv = document.createDocumentFragment(); // DocumentFragment に移し替え while (div.firstChild) { rv.appendChild(div.firstChild); } document.body.removeChild(div); // DOM Tree から除去 return rv; // DocumentFragment を返す } var documentFragment = build("<section>...</section>"); document.body.appendChild(documentFragment);
innerHTML の代わりに cloneNode() を使った場合は、生成される node の tagName と CSSセレクタでヒットする要素名にズレが発生しスタイルが適用されなくなります。
var div = document.createElement("div"); var section = document.createElement("section"); document.body.appendChild(div); div.appendChild(section); section.appendChild(document.createTextNode("...")); var clonedNode = section.cloneNode(true); alert( clonedNode.outerHTML ); // "<:section>...</:section>" document.body.appendChild(clonedNode);
この時、clonedNode は document.getElementsByTagName("section") で検索できるが、 section { color: red } は適用されない状態になります。
CSSを適用するには以下のように、要素名の前に \: を追加したスタイルも設定します。
section, \:section { color: red; }
innershiv
jQuery ユーザの場合は、オンザフライで、$("div").append("section") のようにすると失敗してしまいます。
これを回避するには、HTML 5 innerShiv を使い、
$("div").append(innerShiv("<section>...</section>"));
のようにします。
innerShiv はDocumentFragmentを返す関数です。
内部では、DOM Tree に参加した状態で、innerHTML を行い、jQuery側で不具合が発生しないように回避します。
$("div").append("section") できないらしいので色々調べてみた
追記: jQ的には解決していませんが、素の JavaScript を使った回避方法がわかりました。IE8以下では、オンザフライで作成した要素に対して innerHTML すると謎要素が作成されてしまうといった現象がでるため、一度要素片を、DOMツリー( body とかね ) にぶらさげてから innerHTML すると回避できました。
つまり、オンザフライな↓ではだめで… var div = document.createElement("div"); // 作る div.innerHTML = "<section>ほげー</section>"; // 突っ込む → ι(´Д`υ) ↓のようにすると謎の挙動を回避できます。 var div = document.createElement("div"); // 作る document.body.appendChild(div); // あてがう div.innerHTML = "<section>いけたー</section>"; // 突っ込む → ヽ(・ω・)ゝ createDocumentFragment() を使っても、オンザフライなノードに突っ込むと、同様に謎の挙動が発生します。 node.cloneNode() は、nodeがオンザフライかどうかに関わらず、謎の <:section> ノードを生成するため、 CSSを適用したい場合は、 section { ... } と記述するだけだとだめで、 section, \:section { ... } と併記する必要があります。 \:section を併記すると IE8 でスタイルが適用されます(IE6, IE7 では適用されません)。
文章で書くとよくわからないと思いますがコードにすると↓こんな感じです。
<!DOCTYPE html><html lang="ja"><head><meta charset="utf-8" /> <title></title></head><body> <style> section { color:blue;outline:2px solid blue } \:section { color:pink;outline:2px solid pink } /* IE8で<:section>に適用される, IE6, IE7には適用されない */ \\:section { color:red; outline:2px solid red } /* IE6, IE7, IE8 には適用されない */ </style> <script> document.createElement("section"); // HTML5 siv var div = document.createElement("div"); // 作る div.innerHTML = "<section>ほげー</section>"; document.body.appendChild(div); // あてがう → ι(´Д`υ) var div2 = document.createElement("div"); // 作る document.body.appendChild(div2); // あてがう div2.innerHTML = "<section>いけたー</section>"; // ヽ(・ω・)ゝ var div3 = div2.cloneNode(true); document.body.appendChild(div3); alert(div3.outerHTML); // ※ // ※ // IE9 では <div><section>いけたー</section></div> // IE8 では <DIV><:section>いけたー</:section></DIV> // IE7 では <DIV><:section>いけたー</:section></DIV> // IE6 では <DIV><:section>いけたー</:section></DIV> // IE9(IE7互換mode) では <DIV><section>いけたー</section></DIV> // IE9(IE8互換mode) では <DIV><section>いけたー</section></DIV> </script> </body></html>
IE9 の IE7,IE8 互換モードは実際の IE7,IE8 と細かな挙動が異なるのです、いっぱいあって説明する時間も気力もないのですが、ひとつ言えることは、「HTML5 に対応しました。(IE9 の互換モードで)確認もしました(ドヤ)」する人は、もれなく罠にはまるといいよ!
ここまで追記
ここから本文
@hokaccha さんのツイートを見て気になったので、
$('div').append('
hoge '); とかしたときIEでstyleが効かなくなる件jQuery1.5になったら対応してくれるかなと思ったけど、試してみたらダメだった。
IE6〜IE8 で HTML5 で追加された新要素(この場合は section) を jQuery で追加するとスタイルが適用されない問題があるらしい。
jQuery で試してみた
jQuery の最新版(1.5)で http://jsdo.it/uupaa/createHTML5ElementByjQuery 試してみた。
<script> var css = "div{border-top:1px solid blue}"; document.createElement("section"); // HTML5 shiv $(function() { $("head").append("<style>" + css + "</style>"); $("div").append("<section>section</section>"); }); </script> <style> section, \:section { display:block;border-top:1px solid red } </style> <div>div</div> <div>div</div>
uupaa.js で試してみた
uupaa.js の最新版で http://jsdo.it/uupaa/createHTML5ElementByuu 試してみた。
<script> var css = "div{border-top:1px solid blue}"; uu.ready(function() { uu.head(uu.style(css)); uu("div").add(uu.section("section")); }); </script> <style> section, \:section { display:block;border-top:1px solid red } </style> <div>div</div> <div>div</div>
IEのinnerHTMLとcloneNodeの不具合を調査している時に、
IE6〜IE8で(
… ).cloneNode(true)で生成せした要素にスタイルが当たらなくなるのは、 クローンで作成したノードがチラ見だとにみえて実は<:section>要素になっているため。 http://twitter.com/#!/uupaa/status/34544812854091776
- -
jsからは
のようにみえて、実はouterHTMLすると<:section>なので、CSSが当らないし、IEは仲間を呼んだ。爆弾岩が現れた。IEはぱるぷんてを唱えた。みたいなDOMツリーになる http://twitter.com/#!/uupaa/status/34545606823256064
- -
cloneNode()せずにNodeの属性を正確に素早くコピーする方法はない。cloneNodeすると
が<:section>になる。outerHTMLに" hoge "突っ込むとDOMツリーの親子関係ぶっこわれ3つのノードができる。手詰まりhttp://twitter.com/#!/uupaa/status/34555491514322944
- -
IEで
.cloneNode() した要素にCSSを当てるには \:section { display:block;border-top:1px solid red } のように \:section な感じで先頭にバクスラでエスケープいれればOK
と色々と無駄知識が手に入ったので、
このデモでは、CSSに section と一緒に \:section を併記し、ライブラリ内部でcloneNodeされている要素にもスタイルが適用されるようにしてあります(結局逃げてますが)。
実行結果
IE8 + jQuery
IE8 + uupaa.js
jQueryの場合、画像のように DOM ツリー構造がそもそも壊れちゃってて、
<div> 文字列 <section> 文字列 </section> </div>
のように、本来は2つあれば十分なはずの div 要素の子要素が4つもある。
この状態だとスタイルも適用されなかったんだけど…
どうせ、皆さんが使っているjQuery使えば対策済みですから大丈夫ですよ
HTML5の新要素をinnerHTMLで生成できないバグを回避する - latest log http://htn.to/9wkfZg
ってブクマが今朝のエントリに書かれてたのが気になる。
何か特別な方法があるのかもしれないんだけど、jQユーザではないのでよくわからず。
uupaa.js でも、コードを一部入れ替えると、jQueryのように壊れたDOMツリーが生成されてしまう。
// uu("div").add(uu.section("section")); // cloneNode でノードをノードとしてコピーする uu("div").add("<section>section</section>"); // innerHTML でノードを文字列としてコピーする
uu.section() でノードを作る場合は、内部で cloneNode が走るので被害を最小限にできるんだけど、
uu.node("
今のところ、innerHTML で親子関係が崩れる問題をどうにかする良い方法は見つかってない。IE6〜IE8のバグ由来の仕様という認識。
お願い
uupaa.jsで、HTML5で追加された要素を作成する時は、uu.add("
その際に、CSS には \:section や \:aside など cloneNode で生成される特殊な要素に対するスタイルも併記してください。
よろしくお願いします。
CSSの\:sectionの話については、こちらをどうぞ http://jsdo.it/uupaa/IEcloneNodeBug (HTML5の新要素をcloneNodeするとCSSが適用されないバグを対策する)
HTML5の新要素をinnerHTMLで生成できないバグを回避する
IE6〜IE8のinnerHTMLには問題が多く、そのひとつに、HTML5の新要素を食わせると悪夢のようなDOMツリーを生成するというものがあります。
var div = document.createElement("div"); div.innerHTML = "<section>section</section>"; document.body.appendChild(div);
上記のコードを実行すると、body以下にはこのような構造ができてしまいます。
<body> <div> section </SECTION> </div> </body>
どうやら、innerHTML に与える文字列を div でラップし、div要素の前に何か一つ以上のダミーの文字列を設定しておき、あとでアンラップすれば上手くいくようです。
http://jsdo.it/uupaa/IEInnerHTMLBug で試せます。
window.onload = function() { document.createElement("section"); // HTML5 siv var div = document.createElement("div"); div.innerHTML = "<section>section</section>"; alert( div.innerHTML ); // "section</SECTION>" in IE6〜IE8 // 対策するには、 // ダミーの<div>でラップし、あとでdivを除去します if (document.uniqueID) { // is IE var div = document.createElement("div"); div.innerHTML = "*<div>" + "<section>section</section>" + "</div>"; alert( "wrapped div.innerHTML = \n" + div.innerHTML ); div = div.lastChild; // unwrap alert( "unwraped div.innerHTML = \n" + div.innerHTML ); } };
RegExp#test + parseInt vs RegExp#exec + plus operator
CSSValue な "12em" や "123px" から 単位がpx の場合に 123 という数値を素早く取り出すには、
のどちらが効率的なのか気になったのでベンチマークとってみました。
RegExp#test() + parseInt() |
RegExp#exec() + parseInt() |
RegExp#exec() + plus operator |
loops | |
IE9pp7 | 861 | 2134 | 2072 | 100 |
---|---|---|---|---|
IE8 | 410 | 917 | 860 | 10 |
GC9 | 410 | 564 | 445 | 100 |
Op11 | 670 | 803 | 726 | 100 |
Fx40β10 | 337 | 1488 | 1566 | 100 |
iPhone3GS (iOS4.2) |
138 | 333 | 344 | 1 |
HTC Desire HD (OS2.2) |
48 | 78 | 52 | 1 |
RegExp#test() してから parseInt すると良いようです。
jsdo.it で試せます http://jsdo.it/uupaa/6LQq
<!DOCTYPE html><html lang="ja"><head><meta charset="utf-8" /> <title>bench: RegExp#test + parseInt vs RegExp#exec + plus operator</title> <style>th,td { padding: 5px; text-align: right }</style> <script src="bench.js"></script><script> /* RegExp#test() + parseInt() vs RegExp#exec() + plus operator */ window.onload = function() { document.body.innerHTML += "<p>" + navigator.userAgent + "</p>Running..."; var loop = getLoop(); // --------------------------------------------------------- var rex1 = /\d+px$/, rex2 = /(\d+)px$/; function _test(idx) { var patt = idx + "px"; if (rex1.test(patt)) { return parseInt(patt); } return idx; } function _exec(idx) { var patt = idx + "px"; var match = rex2.exec(patt); if (match) { return parseInt(match[1]); } return idx; } function _exec2(idx) { var patt = idx + "px"; var match = rex2.exec(patt); if (match) { return +match[1]; } return idx; } var arg; job(5, loop, ["RegExp#test() + parseInt()", _test, "RegExp#exec() + parseInt()", _exec, "RegExp#exec() + plus operator", _exec2, "RegExp#exec() + parseInt()", _exec]); } </script></head><body></body></html>
地獄のJavaScript2
(ε・◇・)з 戦いはこれからだ!
http://jsdo.it/uupaa/hellsjs2 で動かせるよ
// forked from uupaa's // "地獄のJavaScript (Symbolic JavaScript)" // http://jsdo.it/uupaa/hellsjs uu.ready("window", function(uu, doc) { uu.mix(window, uu); uu.config.log.rollup = 1000; // logロールアップ回避 // use: $=_-+~[]{}()*/<!\;" $ = -~[]; // 1 $$ = $+$; // 2 $$$ = $$+$; // 3 $$$$ = $$$+$; // 4 $$$$$ = $$$$+$; // 5 $$$$$$ = $$$<<$; // 6 $$$$$$$ = $$$$$$+$; // 7 $$$$$$$$ = $$<<$$; // 8 $$$$$$$$$ = $$$*$$$; // 9 _ = +[]; // 0 __ = // "constructor" (""+{})[$$$$$] + // c (""+{})[$] + // o (""+$/_)[$] + // n (""+![])[$$$] + // s (""+!![])[_] + // t (""+!![])[$] + // r (""+[][[]])[_] + // u (""+{})[$$$$$] + // c (""+!![])[_] + // t (""+{})[$] + // o (""+!![])[$]; // r ___ = // "return " (""+!![])[$] + // r (""+!![])[$$$] + // e (""+!![])[_] + // t (""+[][[]])[_] + // u (""+!![])[$] + // r (""+$/_)[$] + // n " "; ____ = // "self" (""+![])[$$$] + // s (""+!![])[$$$] + // e (""+![])[$$] + // l (""+![])[_]; // f _____ = // "proto" (""+/ /[__])[$$$$$$$*$$] + // p (""+!![])[$] + // r (""+{})[$] + // o (""+!![])[_] + // t (""+{})[$]; // o ______ = // "type" (""+!![])[_] + // t (""+$/_)[$$$$$$$] + // y (""+/ /[__])[$$$$$$$*$$] + // p (""+!![])[$$$]; // e $_ = ""[__][__]; // new Function $$_ = $_((___+____))(); // new Function("return self")() -> DOMWindow /* 8 = $$<<$$; 9 = $$$$$$$$$; 10 = $$$$$<<$; 20 = $$$$$<<$$; new Function = "".constructor.constructor "function RegExp() { [native code] }" 25 = / /[__]; "function Number() { [native code] }" = (0)[__]; undefined = [][0]; true = !![]; // or !+[] false = ![]; // or !!+[] NaN = -{}; // or +{} Infinity = 1/0; "[object Object]" = (""+{}) -1 = ~[]; 0 = +[]; // or -[] // or +"" -2147483648 = -1 << -1; 2147483647 = ~(-1 << -1); */ log('"a" = @', (""+![])[$] ); log('"b" = @', (""+{})[$$] ); log('"c" = @', (""+{})[$$$$$] ); log('"d" = @', (""+[][[]])[$$] ); log('"e" = @', (""+!![])[$$$] ); log('"f" = @', (""+![])[_] ); log('"g" = @', (""+/ /[__])[$$$$$*$$+$] ); // 11 log('"i" = @', (""+$/_)[$$$] ); log('"j" = @', (""+{})[$$$] ); log('"l" = @', (""+![])[$$] ); log('"m" = @', (""+(_)[__])[$$$$$*$$+$] ); // 11 log('"n" = @', (""+$/_)[$] ); log('"o" = @', (""+{})[$] ); log('"p" = @', (""+/ /[__])[$$$$$$$*$$] ); // 14 log('"r" = @', (""+!![])[$] ); log('"s" = @', (""+![])[$$$] ); log('"t" = @', (""+!![])[_] ); log('"u" = @', (""+[][[]])[_] ); log('"v" = @', (""+/ /[__])[$$$$$$*$$$$+$] ); // 25 log('"x" = @', (""+/ /[__])[$$$*$$$$+$] ); // 13 log('"y" = @', (""+$/_)[$$$$$$$] ); log('"E" = @', (""+/ /[__])[$$$*$$$$] ); // 12 log('"I" = @', (""+$/_)[_] ); log('"N" = @', (""+(+{}))[_] ); log('"O" = @', (""+{})[$$<<$$] ); // 8 log('"R" = @', (""+/ /[__])[$$$*$$$] ); // 9 });