tabindex
属性を JavaScript 側から設定する時の注意これは完全にバグといえる仕様なのだが、IE6 と IE7 では JavaScript(IE だから本来は JScript)側から setAttribute
などを使って tabindex
属性を設定してやる時、属性名を "tabIndex"
にしてやらないと認識しないことを知った("I" がキャメルケースになっている)。WAI-ARIA(2008-08-06版 WD 邦訳)絡みで色々といじっていた時に発見。因みに IE8 RC1 では修正されている。
次の JavaScript コード断片は、IE で tabindex
属性を設定する時の方法を説明したものである。
// ターゲット要素 var elem = document.getElementById("test"); elem.setAttribute("tabindex", "0"); // IE6, 7 は駄目、他 UA は認識 elem.setAttribute("tabIndex", "0"); // IE6, 7 は認識、他 UA は駄目 elem.tabIndex = 0; // tabindex 属性サポート UA 全てで認識
あまりにも酷い仕様なのだが仕方ない。一番手っ取り早い解決策は tabIndex
プロパティを経由して値を設定してやることか。わざわざ UA 判別で "tabindex"
と "tabIndex"
を切り替えてやるよりかはずっとお手軽。W3C DOM 2 でも定義されているし。
それにしてもこの辺の IE 仕様には困ったものだ。大体、class
属性値の件と同じパターンなのだ[1]。要するに「JavaScript 側からプロパティ経由で設定する時のプロパティ名を使わなければならない」ということ。設計した人間は、当時こそ「これで解決じゃん」と思っていたのだろうが……手抜きの仕方が間違っている……。プログラマの三大美徳「無精」「傲慢」「怠惰」が全て勘違いされた意味で使われてしまった典型例だろう。
あー、いい加減 IE6 とか滅びないかなー。IE7 も JScript 側はほとんど IE6 から変化していないし……。IE8 も色々不安要素を孕んでるし……。JavaScript/JScript の変革は、IE9 以降にしか起こり得ないだろう。本気でそう思う憂鬱な今日この頃。
class
属性値を JavaScript 側から setAttribute
などで設定する時、 "class"
ではなく "className"
を属性名として与えなければならないという有名な話。プロパティ経由で設定する時は className
プロパティを使う。Adobe Flash 10.0.22.87 がリリースされた。様々な修正が入っているようだ。重要なセキュリティフィックスも入っているし、アップデート必須。
早速テスト目的で配布されている、スタンドアロンインストーラのアーカイブをダウンロード。バージョン10のアーカイブもしっかり最新版になっていた。アンインストーラで ActiveX 版と NPAPI 版を同時にアンインストールしてから、アーカイブ中の flashplayer10r22_87_win.exe
(NPAPI 版)と flashplayer10r22_87_winax.exe
(ActiveX 版)をインストール。
今ニコ動で「春閣下は無慈悲なシチリアの女王」シリーズや「アイドルたちとクトゥルフ神話世界を!」シリーズを観ていたりするのだが、特に問題は出ていない。
あと今日はさよなら絶望放送 DJCD 第五巻が届いた。本放送とあわせて、これから聴く。
いきなりでびっくりしたが、Safari 4 Beta がリリースされた(プレスリリース邦訳)。Windows 版も勿論ある。というわけで早速インストールしてみた。
まあスクリーンショットを見てもらえるとわかるように、UI が大幅に変更されている。Google Chrome の影響をモロに受けているのか、Google Chrome がこの UI を参考にしたのか。Safari 4 の Alpha 版ってどうなってたんだろ。個人的に残念なのはタブまわり。Windows XP のクラシックテーマだとめちゃくちゃ小さい上、非常に判別しにくい。OS ネイティブテーマをベースにするようになったのにもびっくりだ(遅
Safari 4 Beta (Windows) の UA 文字列は次の通り。
Mozilla/5.0 (Windows; U; Windows NT 5.1; ja-JP) AppleWebKit/528.16 (KHTML, like Gecko) Version/4.0 Safari/528.16
WebKit Nightly と同様、WebKit が528系になっている。ということは WAI-ARIA(2008-08-06版邦訳)にも対応したバージョンになっているのだろう。実際に Mozilla のタブインタフェイスデモで試してみたが、動作はしているようだ。明日 NVDA でテストしてみる予定。
ついに Safari 4 の Beta が始動した。Opera 10 は Alpha 1 以後、Snapshot Build がちょいちょいと出ている。Firefox 3.1 も、Mozilla Flux によると Beta 3 まで Blocker バグ9個という所まできているらしい。Google Chrome もバージョン2系を Dev チャンネルで配信しているし、Internet Explorer 8 も feedback の流れをみる限りなかのひとがバグ潰しに躍起になっているところだ。
ますます加速してきた開発状況。ちょっと目を離したら、その隙に追いていかれるよなぁ、これ……。
情報処理学会 (IPSJ) が「情報処理技術遺産」なるものを策定した。世界遺産、とまではいかないが、まあ国宝の情報処理関連技術版。プレスリリースを読むと、なかなかツボに入るセレクションが揃っている。以下、リストからいくつか抜粋。
この中で特に感慨深いのは FACOM128B。FUJITSU のサイトにあるように、128Bは沼津工場に存在している。これを学生時代、学校が近かったこともあり見学にいったことがあるのだ。大型のリレーが大量に並ぶ様子は、まさに圧巻のひとこと。それがガシャガシャと音をたてながら(機械稼動部分が非常に多い)計算を行うのだ! 半分スチームパンク。実に素晴らしい光景であった。ボタンがいちいちレトロなコンソールに、パンチテープのリーダ、そしてタイプライタの如きキーボード。あの光景は計算機を愛する者にとっての原風景になりうるだろう。シリコン文化万歳。
この情報処理技術遺産、これからも継続していってもらいたい。そのうち、ハードウェアのみならず、ソフトウェアにも目を向けてもらえると嬉しい。日本人が開発したソフトウェアか……。GNU の歴史を紐解けば、Mule や SKK などがサッと脳裏に浮かぶ。いつかそのアーカイブに収蔵される日がくるのだろうか?
クトゥルカ[1]もやはり生きているものと、少なくとも私自身は考えている。リリースの以前から彼女を庇護していた笑みの割れ目に戻って、生きつづけるにちがいないのだ。彼女の呪われた都も、ふたたび網底に沈んだ。二月の嵐のあと、火狐号がその網上を無事にレンダリングできたのがその証拠だ。しかし、現実では彼女の信徒たちが、今夜もまた、人里離れた深夜の自室内で、偶像の在りし姿をめぐって、吠え立て、踊り狂い、投稿を繰り返している。目下のところ、クトゥルカは網底の暗黒の深淵に捕えられたかたちで身を潜めている。そうでなければ、現在のこの現実は恐怖と狂乱の世界と代わって、われわれ人類は泣き叫んでいるはずである。(中略)そして私の遺言実行者に、私の死後、この記録を発見したときは、俗人どもの目に触れぬよう色々な意味で慎重に処理してしまうことを依頼しておく[2]。
クトゥルカの呼び声
というわけで色々な意味でクトゥルカ様を讃えてみるテスト。元ネタは勿論『クトゥルフの呼び声』(H・P・ラヴクラフト 創元推理文庫『ラヴクラフト全集 2』所収)。
入門はまず「讃えよ!海産物(主にタコを)」を歌ってみた【アッ子P】が良いだろう。クトゥルカもといクトゥルフ[3]のもたらす名状しがたき恐怖が見事に表現された作品である。
そして、親愛なるクトゥルカ信徒の一人が素晴らしいクトゥルカ神話史を記述してくださった。その暗黒の歴史を垣間みたければ、このまとめは必読であろう。
That is not dead which can eternal lie, And with strange aeons even death may die.
Necronomicon
ふと疑問に思った。当然ながらクラスベースの場合は記述法が存在している。……だが、プロトタイプベースの場合はどうなのだろう? 例えば ECMAScript のプロトタイプチェーンを [[Prototype]]
内部プロパティと prototype
プロパティを交えつつうまいこと図示したい時に、共通のコンセンサスがあった方がいいとは思う。
UML はあまり使っていないので何もデカいことは言えないが、リファレンス的な情報を求めてググってみても書籍ばかりがヒットする。なんじゃそりゃ。
本とか買うしかないのかなぁ……。どっかで立ち読みしてから考えよう。
随分と前にアカウントだけはとっておいたのだが、ふと本格的に Twitter で呟いてみようと思いたった。それが昨日のハナシ。というわけで taku_eof としてぶつくさ言ってみている。
……面白いなぁ、これ。何というか、愚痴るには丁度いいツール。イロイロと独り言やら愚痴やらを書いていると、思いのほかスッキリする。
最近つとに思うのだが、私はどちらかといえば「常に乗り遅れてくる」タイプのようだ。最新の情報を蒐集しようとしても、どこかワンテンポ遅れているような感じ。αじゃなくてβ、みたいな。それはそれで別にいいか。
class
属性値における空白文字と JavaScript 1.5 の正規表現における \s
の範囲は違う先日引っかかったのでメモ。HTML 5[1] において、class
属性値として与えることができる値は skip whitespace される。skip whitespace とは space characters の連続によって区切ることだ。space characters は次の Unicode 文字から成る集合として厳密に定義されている。
対して JavaScript ではどうなっているか。Mozilla Developer Center の記述によると、JavaScript 1.5 の正規表現内における \s
は、次の Unicode 文字から成る集合として定義されている(強調は前述のリストとの差異)[2]。
この差異の何が問題になるのだろうか? 実はこの差異が class
属性値の解析に多大な影響を及ぼすのである。ひいては getElementsByClassName
メソッドの挙動にも影響する。
前述の仕様に従えば、class
属性値に指定された値は次の文字列で区切られているといえる。Unicode 値の前に記述したのは正規表現内で表現する方法である。
" "
U+0020 SPACE"\t"
U+0009 CHARACTER TABULATION"\n"
U+000A LINE FEED"\f"
U+000C FORM FEED"\r"
U+000D CARRIAGE RETURNつまり、class
属性値を split
する時は、次のような正規表現を使わなければならないことになる。
var COND_SPC = /[ \t\f\r\n]+/;
次の JavaScript コード断片のようにして値を取り出せるというわけだ。
var elem = document.getElementById("test"); var classNames = (elem.className || "").split(COND_SPC);
変数 classNames
は Array
オブジェクトになる。そこに split
された文字列が格納されている。
次の表は、各文字列に対して正規表現 /[ \t\f\r\n]+/
を使った場合に split
される値を一覧にしたものである。
与文字列 | 出力配列 | 配列長 |
---|---|---|
"a b" |
["a", "b"] |
2 |
"a b" |
["a b"] |
1 |
"a b" |
["a", " b"] |
2 |
まず2番目の例に着目してほしい。ここでは U+3000 すなわち全角空白を区切りとして用いている。ところが、HTML 5 仕様に基づくならば、U+3000 は space characters の一員ではない。つまり split
は行われず配列長も1のままである。
次に、3番目の例に着目してほしい。ここでは a
と b
の間に U+0020 U+3000 の並びで半角空白と全角空白が並んでいる。よって、この例では U+0020 を区切りとして用いている。だから配列長も2になる。しかし、出力配列にある b
の前に全角空白が存在したままになっている。よって b
という文字列が存在するものとして処理される。
これは JavaScript ライブラリを作成する上で盲点になっているようだ。jQuery や Prototype といったライブラリでは、特に IE6/IE7 用に class
属性値を解析しようとしている部分で space characters として \s
を用いてしまっている。前述したように、\s
に含まれる空白文字列と HTML 5 で定義されている space characters は異なっている(厳密には space characters が \s
の部分集合になっている)。つまり、HTML/XHTML 文書をマークアップした作成者が、誤って全角空白 U+3000 を区切りに使ってしまっていた場合、解析結果が HTML 5 の意図とは異なるものになってしまっていることになる。
この挙動は getElementsByClassName
をネイティブ実装していない UA に対して実装してやる場合に、とても重大な差異を生むことになる。挙動が異なってしまうのだ。
次の表は、各文字列に対して正規表現 /\s+/
を使った場合に split
される値を一覧にしたものである。
与文字列 | 出力配列 | 配列長 |
---|---|---|
"a b" |
["a", "b"] |
2 |
"a b" |
["a", "b"] |
2 |
"a b" |
["a", "b"] |
2 |
明らかに前述した /[ \t\f\r\n]+/
を使った場合とは結果が異なるのがわかる。
例えば、Prototype 1.6.0.3 で定義されている document.getElementsByClassName
を使おうとした場合、ネイティブ実装されている Firefox 3.0.x/Safari 3.x[3] と、IE6/IE7 の結果が異なってしまう。
英語圏の人々にしてみれば「なんじゃそりゃ!」という話になるのだろう。私だって「なんじゃそりゃ!」という感じだった。だが HTML 5 では space characters に類似の概念として White_Space characters というものも定義されている。つまり、英語圏以外の言語もきちんと考えている。つまり、仕様のバグという可能性は考えにくい。
まあ要するに「空白を意味する文字列の定義には気をつけろ!」ということでひとつ。
\s
は、仕様書 15.10, 7.2, 7.3 によれば、ほとんど JavaScript 1.5 のものと同じである。唯一の相違は U+2000~U+200b と U+3000 が明記されておらず、Other category "Zs"とのみ記述されている点である。Unicode カテゴリ Zs は space separator を表現する文字の集合のようだ。手許に資料がない上に Web 上の資料がとても探しにくいので断言はできないが、どうやら JavaScript 1.5 は Unicode カテゴリ Zs の「一部」をサポートしている、というスタンスのように見受けられる。
\s
ないしそれに類似した space characters を使っているらしく、HTML 5 の仕様からみれば不正な挙動をとっている。U+3000 を space characters の一員と解釈してしまっているので、\s
を使った場合と同様の結果を得ることになってしまっている。予防接種を受けたにも関わらずこの有様だよ。ウイルスはソ連A型だとか。発病したと自覚してからすぐに病院へいったお陰か、現在リレンザ(ザナミビル)を服用中(48時間以内に服用を開始しないと効果がない)。会社には当然出勤してはならない。というわけで自宅療養中。
発症時は咳と頭痛がひどかったものの、現在は薬が効いているおかげか沈静化。ただ、くしゃみと鼻水が酷くなった。熱は一時39度近くまで上昇したが、現在は37度5分くらい。解熱剤を飲んで睡眠をとり続けている。
混合ワクチンじゃなかったのか予防接種……? ワクチン摂取していても発症することがあるというが、それでも症状は控えめになるという。今回の症状は以前発症した時と比較してほぼ同等の苦しみだった。もし混合ではなかったら、健康保険組合(か会社?)に文句言ってやりたい……。
そろそろツラくなってきたのでもう寝る。ああ……だるい……。
今日は Opera の新しい ECMAScript/JavaScript エンジン Carakan の公開情報の要約「後編」。内容は「自動オブジェクト分類」と「パフォーマンス」について。プロパティアクセスにキャッシュを導入することで高速化を実現したようだ。パフォーマンスについては以下略。ただ、先日要約した「ネイティブコード生成」が無効のままで SunSpider ベンチマークをとっている点には注意しておきたい。
以下、不完全な要約。私のヒドイ英語力によって、色々と解釈を間違えている可能性が高いので注意。特に「自動オブジェクト分類」は難しかったので無茶苦茶になっているかもしれない……。
Carakan では、ECMAScript オブジェクトの改良が行われたという。具体的には、プロパティ機構にキャッシュが導入されるようになったらしい。
このキャッシュは、同一のプロトタイプを持ち、ほぼ同一のプロパティを持つオブジェクトに対して効果を発揮するという。この時、プロパティ数は多ければ多いほど効果が現れるそうだ。
つまり、次のようなコードに対し、そのプロパティとプロパティ値を共有するようになるということだろう。
// alpha, beta ともにプロトタイプは Object プロトタイプオブジェクト var alpha = { first : "A", second : "B", third : "C" }; var beta = { first : "A", second : "B", third : "D" };
前述したコードの場合、first
と second
が内部的に同一の値を参照するようになるらしい。そして、alpha.first
を参照した場合、その結果がキャッシュされる。次に beta.first
を参照した場合、alpha.first
を参照した際に保存されたキャッシュが用いられることで、「連想配列の検索」という操作を丸ごと省略できる……ということだろうか。
多分、両者で異なる値をセットした場合は「値の」共有をやめ、「プロパティ名の」共有(キャッシュ)は残しておくのだろう。
Carakan を使用した場合、Presto 2.2 (Opera 10 Alpha) の Futhark を使用した場合と比べ、SunSpider ベンチマークでおよそ2.5倍高速になっていたという(但し、ネイティブコード生成は行っていない)。
Opera はたくさんの異なったハードウェアアーキテクチャに移植されるので、このようなクロスプラットフォーム改良がとても重要である。
Carakan のネイティブコード生成は、まだ全体テストの準備が整っていない。だが、いくつかの個別のベンチマークテストでは、既に5~50倍速く動作しているそうだ。
パフォーマンスの部分に注目。5~50倍というのは「個々の」ベンチマークテストの結果でしかない。マスメディアが騒いでいるような、大仰なものではないということだ。これがスクリプトエンジン全体に適用され、より詳細なベンチマークがとられた場合は、この値より下がることだってありうる。
まあ要するに、報道ってやつはいつもどこか情報が欠けているから鵜呑みにしちゃいけないよ、ということで。
それにしても英語は苦手だ……。接続詞がどの単語にかかっているかがゼンゼンわからん。仕様書なんかは定型文だから辞書と睨めっこすればどうとでもなるが、こういう Blog の、要するに生の英語(?)だともうお手上げ。
プログラミング言語やマークアップ言語だとすんなり入れるのに、自然言語ってやつはどうしてこんなにわかりにくいんだおろう……。
今日は Opera の新しい ECMAScript/JavaScript エンジン Carakan の公開情報の要約「中編」。内容は「ネイティブコード生成」について。ネイティブコードの生成が実装されつつあるという話だが、どうやら正規表現エンジンとも連携している模様。
以下、不完全な要約。私のヒドイ英語力によって、色々と解釈を間違えている可能性が高いので注意。
新エンジンのバイトコード命令セットは、高速なバイトコード実行エンジンの実装を可能にするという。ただ、それだけではバイトコードインタプリタ内で整数演算を実行するループなどといった、簡単な ECMAScript コードを実行するのに伴う重大なオーバーヘッドは解決できないらしい。
バイトコード命令セットだけでは解決できないようなオーバーヘッドを除去するために、ECMAScript プログラムや関数の全体や一部をネイティブコードにするネイティブコードコンパイラを実装したそうだ。
このネイティブコードコンパイラは、次の要素がベースになっているという。
前述したレジスタアロケータは、独立したアーキテクチャ上にデザインされており、最も複雑な判定を行うコードを生成するためのフロントエンドを作成する。
特にネイティブコード変換に適合した ECMAScript コードでは、ネイティブコードコンパイラが生成したネイティブコードが、一見だれかが手で書いたようなアセンブリコードみたいになるらしい。そして、すべてのレジスタを保つような努力を行うようになっているという。
ネイティブコードコンパイラの 32bit/64bit x86 アーキテクチャ用のバックエンドは、既に実装されているという。また、ARM などといった他の CPU アーキテクチャ用も実装がはじまっているらしい。
加えて、このネイティブコードコンパイラは簡単な正規表現によるマッチを実行するネイティブコードも生成するのだとか。これは長い文字列でシンプルな正規表現によるマッチを使う場合、性能を大いに向上させるという。つまり、十分に長い文字列で正規表現を使用する際、String.prototype.indexOf
よりも、正規表現のほうが高速に検索を行えるようになるということだ。
但し、短い文字列では正規表現のほうがオーバーヘッドがかかるという。正規表現用ネイティブコード生成時間 + 正規表現による検索時間 > 他の方法による検索時間
になるためらしい。
また、複雑な正規表現ではネイティブコード生成も複雑になるため、これまたオーバーヘッドがかかるという。正規表現エンジンが既にかなり高速なため、正規表現用ネイティブコード生成時間 + 正規表現による検索時間 > (ネイティブコードを使わない)正規表現による検索時間
になるためらしい。
基となる正規表現エンジンは新たに開発されたものだが、これは Presto 2.2 (Opera 10)で既にデビューしている。この正規表現エンジンは、基本的には典型的なバックトラッキング正規表現エンジンである。だが、冗長なバックトラッキングを回避することが可能ないくつかのトリックが仕込まれているそうだ。また、特定のパターンを持った正規表現には、厳しいパフォーマンス問題が存在している。ところがこの正規表現エンジンは、このパフォーマンス問題をいつでも回避するようになっているという。
最近の JavaScript プログラムでは正規表現で長い文字列を扱うことが多くなってきているからなぁ(XMLHttpRequest
で取得したテキストファイルなど)。これは朗報。
昨日は夜に呑み会があって、帰宅したのが午前様になってしまった……。とても楽しかったからいいんだけれども。
明日は「自動オブジェクト分類」と「パフォーマンス」について要約する。
Opera が新しい ECMAScript/JavaScript エンジンの情報を公開した。コードネームは "Carakan"(発音は「チャラカン」)[1]。Choose Opera 日本支部には概略が掲載されている。マイコミジャーナルの速報も出た。
これから今日・明日・明後日にわけて、前述した Opera の発表記事 (Blog) に掲載された内容の不完全な要約を載せていくつもりである。私の英語力はそれはもうヒドイので、色々と解釈を間違えている可能性が高いので注意。
今日の要約は「レジスタベース バイトコード」について。
現行の ECMAScript エンジンは、「スタックベース バイトコード命令セット」を使用しているという。
スタックベース バイトコード命令セットは、スタック上にある値をベースにする。いくつかの命令が単純にスタック上に値を push し、他の命令がスタック上の値を再配列する。
コンパクトなバイトコードプログラムを(値として)与えてやる(push 操作)と、それを基にしたバイトコードを生成(再配列)しやすい……ということなのだろうか?
Carakan ではレジスタベース バイトコード命令セットを使うことになるという。これは動的な大きさで作成されたスタックのかわりに、レジスタ (registers)[2] と呼ばれる固定サイズのデータブロックを使うそうだ。
スタックでは、命令がスタックの先頭にある値だけを頼りにして、スタック全体のデータをコピーする。
対してレジスタは、あらゆる命令がどのようなレジスタにもアクセスできるため、(レジスタ全体をスキャンする命令が必要でないために)より少ない命令でレジスタ内のデータへアクセスできる。また、レジスタ内の必要なデータのみがコピーされるので、スタックベースの場合より少ないデータをコピーするだけですむ。
いかんなぁ……。もっとインタプリタやコンパイラに関する知識が欲しいところ。高専時代に同じ研究室でコンパイラに関する研究をしていた同輩がいたことを思い出す。研究室の蔵書には貴重な『コンパイラ I』『コンパイラ II』(A. V. エイホ、R. セシィ、J. D. ウルマン サイエンス社)が揃っていたというのに。特に後者は版元品切か絶版。少しでも読んでおきたかった。
明日は「ネイティブコード生成」について要約する。
margin
/padding
/border
を使うには hasLayout
を true
にするIE5.01 をゴニョゴニョすることなぞ、最早滅多にないことだ。だが、時としてこの過去の遺物と対決しなければならないこともある。
そんな IE 5.01 には有名なバグ(というか仕様)がある。「インライン要素で margin
/padding
/border
が使えない」というものだ。これは、以外な方法で「ある程度の」解決を図ることができる。「対象インライン要素の hasLayout
を true
にすればよい」というものだ。
この方法、勿論のことだが万能ではない。実際に得られるレンダリング結果は、細やかな微調整を施してやらないと使いものにならない。なので、当然ながら非推奨であり、のっぴきならない事情がある場合の最終手段として使うべきだ。
閑話休題、次の XHTML 断片が存在することを前提にして話を進める。
<ul> <li class="first">[<span><code>hasLayout</code> が <code>true</code> の場合</span>]</li> <li class="last">[<span><code>hasLayout</code> が <code>false</code> の場合</span>]</li> </ul>
前述の XHTML 断片に、次の CSS 断片が与えられたとする。
/** * IE5.01 専用 * 条件コメントなどで他の UA には適用されないように! */ ul li.first { height : 0; /* hasLayout を true に */ } ul li span { /* 本来なら適用されないプロパティ群 */ margin : 0 0.5em; padding : 0 0.5em; border : 1px solid #f00; } ul li.first span { height : 0; /* hasLayout を true に */ line-height : normal; /* 祖先要素の値を継承する問題の回避 */ vertical-align : top; /* 上下位置がズレるため補正 */ }
IE5.01 では、次の図のようなレンダリング結果を得られる。
前述してきた hack を使うにあたっては、色々と条件がつきまとう。次に列挙したのはその条件である。私が見つけていないだけで、他の条件があるかもしれない。
hasLayout
を true
にしたインライン要素の親要素でも hasLayout
を true
にしないと、レイアウト崩壊する場合があるhasLayout
を true
にしたインライン要素に vertical-align
を指定しないと、上下位置がズレる場合があるhasLayout
を true
にしたインライン要素は祖先要素の line-height
値を継承してリーディングを確保してしまうため、リセットする必要がある何にしろ、IE5.01 なぞを考えなくてすむような状態が、一番精神衛生的にはよろしいことなのだが……。そうもいかない世界も確かに存在するのが、この世の不条理な側面である。
Object.clone
が消えた現在 ECMAScript 3.1 仕様は策定の真っ最中であり、WD が公開され、ちょくちょく更新されている。ECMA 262 3rd Edition (ECMAScript 3) 仕様(邦訳)と読み比べたりしているのだが、つい最近になって ECMAScript 3.1 になってから新たに追加されたはずの Object.clone
メソッドが仕様から消えているのに気づいた。
WD 中にある Revision History によると、削除されたのは Draft as of 20 Oct 2008 (PDF) が公開されたタイミング。随分と前のことだった。Draft as of 13 Oct 2008 (PDF) までは確かに存在している。
Revision History には 15.2.3.6 - removed this section (this was about Object.clone), and renumbered subsequent sections
としか書かれておらず、何故削除されたかまでは不明。ミーティングの議事録を読んでもこれといった記述を見つけることができなかった。ググっても同様。私の検索能力がヘタレなだけで、どこかに理由が書かれているかもしれない。情報求む。
Object.clone
の詳細についてはヒマができたら書きたいなぁ。
はてなブックマークのコメントで色々と指摘を頂いたので、先日の記事に対する補遺を書いてみた。
確かに、coliss で紹介されていた方法(元ネタその1とその2)は「ネタ」の意味合いを含んでいるかもしれない。実際、私も最初はネタとして捉えた。
とはいえ、「もし使うのであれば注意したほうがいいよ」という注釈を書いておいた方がよかったのも確かである。先日の記事は「注釈」を目的の1つとして書いた。
基本的には「機能の有無」で判定・対応すべきなのは確かだ。しかし、「機能の有無」だけでは判定できない場合が存在するのである。
幸いにも os0x氏が「JavaScript によるブラウザ判別の実際」としてまとまった記事を書いてくださった。参考にしてほしい。
補足(もしくは蛇足)として、実際に「機能の有無」だけでは判定できない場合について例示する。
まず、WebKit では document.styleSheets
の NodeList に Alternate StyleSheets が含まれない。これは「機能の有無」では判定不可能である。よって、「ブラウザ判別」を使用して処理を切り分ける必要が出てくる[1]。
他にも、Opera のフルページズームがとる挙動を挙げることができる。Opera はフルページズームを行う際、あらゆる Computed Style 値が再計算されない。つまり、getComputedStyle
で取得できるすべての値、currentWidth
、currentHeight
、offsetWidth
、offsetHeight
といった値はことごとく「ズーム前と同じ」のまま、ということだ。これでは「ズームされたか否か」が一切判定できない。これもまた、「機能の有無」では対応できないパターンのひとつである。Opera の場合だけは特殊な処理を行う必要がある。
window.execScript
は Google Chrome にもある & id="opera"
な要素が存在し、かつスクリプトを load
イベント後に実行した場合はどうする?Google Chrome で確かめてみたらホントに window.execScript
があった。これにはちょっとびっくり(しかも JavaScript による実装)。というわけで、ActiveXObject
の有無を判定したほうがよさげ。
また、id="opera"
でノードアクセスが行われるパターンがあるというのも、確かに納得。この方法はまったく頭の中からヌケていた。指摘に大感謝。ひとつ条件を追加することで、より確実さを高めることにした。
勿論、これらの予防措置に加えて、script
要素を head
要素内に定義し、load
イベントが発行される前に判別用コードを実行しておくと、より better である。
次の表は、「プロパティの有無」を使ったブラウザ判別例の改訂版である(差異を強調してある)。
UA | コード |
---|---|
IE | undefined !== window.ActiveXObject |
Firefox | undefined !== window.Components |
Safari | undefined !== window.defaultstatus |
Google Chrome | |
Opera | undefined !== window.opera && "function" === typeof window.opera.version |
window.opera.version
は Function
オブジェクトなので、typeof
で判定したほうがよいだろうと考えた。定義されていないなら "undefined"
が返るためである。
改めて指摘されると、確かに禁止
するほどでもないか……と思えるようになってきた。
というわけで、先日のUA 文字列による判定は論外である。禁止といってもいい
という表現はやめて、次のように再定義してみたい。「UA 文字列による判定にも問題はある。使用するならば、十分に注意する必要がある」。
UA 文字列を用いたブラウザ判別については、Mozilla Developer Center にある navigator.userAgent と Developing Cross Browser/Cross Platform Pages で「非推奨である理由」が述べられている。
先日の記事で、既に回答を書いてある。
そもそも、こんなまどろっこしい真似はせずに、どのブラウザ (UA) でも同じコードで同じ挙動が得られればいいのだが……。それはあくまでも理想であって、現実は非常に泥臭い。泣けてくるなぁ。
JavaScript でブラウザ判別を行う方法が coliss で紹介(IE 用・Firefox/Safari 用)されていたのだが、どうもこの方法には疑問がある。その原因は、言語仕様の盲点を使っているためだった。それに気づかせてくれたのは Days on the Moon の記事である。
Caution 補遺を追記した。本エントリと併せて参照。
結論からいえば、ブラウザ判別を行う必要に迫られたなら、「仕様の盲点」ではなく、「プロパティの有無」を使った方がまだマシだ、ということになる。
仕様の盲点による判別は、言ってしまえば後々にはバグとして修正されてしまう可能性を強く秘めている。現に、前述した Days on the Moon の記事によれば、Firefox 用の判別法は Firefox 3.1 では使えなくなってしまうという。こんなに不安定な方法はとてもじゃないが使いようがない。
次の表は、coliss で紹介されていた「仕様の盲点」による方法の一覧である。
UA | コード |
---|---|
IE | '\v' == 'v' |
Firefox | /a/[-1] == 'a' |
Safari | /a/.__proto__ == '//' |
Opera | なし |
特に IE でのエスケープが効いていない問題は、後にバグとして修正される可能性を秘めている。
これに対し、プロパティの有無を利用するブラウザ判別は、仕様の盲点同様「不安定」であることに変わりはないが、それでもプロパティの存在自体はバグではないために、まだマシといえる。
次の表は、私の独断で掲載する「プロパティの有無」による方法の一覧である。
UA | コード |
---|---|
IE | undefined !== window.execScript |
Firefox | undefined !== window.Components |
Safari | undefined !== window.defaultstatus |
Google Chrome | |
Opera | undefined !== window.opera |
それぞれのプロパティについて、軽く説明しておく。
window.execScript
IE のみ。詳細は MSDN の記事を参照。要は eval
の多言語対応版。
IE6、IE7、IE8 RC1 で定義済。
window.Components
Firefox (Gecko) のみ。詳細は MDC の記事を参照。XPCOM へのエントリポイント。XPCOM は Gecko だけのものなので、必然的に Gecko 専用のプロパティになる。
Firefox 2.x (Gecko 1.8.1.x)、Firefox 3.0.x (Gecko 1.9.0.x)、Firefox 3.1 Beta 2 (Gecko 1.9.1b2)、Minefield (Gecko 1.9.2a1pre) で定義済。
window.defaultstatus
window.defaultStatus
の s が小文字版。なぜか Safari と Google Chrome (WebKit) ではこれが定義されている。
Safari 2.x、Safari 3.x で定義済。Nightly Build では r40471 まで確認、定義済。
Google Chrome 2.0.159.0 で定義済。
window.opera
名前からもわかるように、Opera のみが定義しているプロパティ。
Opera 9.2x、Opera 9.5x、Opera 9.6x、Opera 10.0 Alpha 1 で定義済。
注意したいのは、ここで挙げた「プロパティの有無」による方法はあくまでも「仕様の盲点」よりかはマシ (better) な方法だが、最良 (best) の方法ではない、ということだ。これから先、もっと良い方法が出現するかもしれないし、しないかもしれない。その「不安定さ」を知っておかなければ、後に困ったことになるのは使っている側自身なのである。
因みに、UA 文字列による判定は論外である。禁止といってもいい。なぜなら UA 文字列は「閲覧者側が」容易に偽装できるからだ。これほど不安定な方法はない。
プロパティだって定義されたらおしまいじゃないか、と言われそうだが、少なくとも window
オブジェクトといったネイティブオブジェクトに対して undefined
かどうかをチェックすれば、偽装の危険性を減らせる。また、前述したプロパティはどれもスクリプト側が定義する必要性の低いものばかりだ。勿論、これらの前提はかなり性善説に基づいている。世のライブラリにはネイティブオブジェクトに対する無茶苦茶なプロパティ追加を行うものもある。だが、そこまで考えたらキリがない。
ともかく、「仕様の盲点」は不安定すぎるから使わないほうがいいんじゃない? という話。