print
メディアへの暫定対応昔々に対応したものの、その後のアップデートでいつの間にか意味消失していた print
メディア専用スタイルを復元させた。しかしあくまでも暫定。かなり適当。またぼちぼち改良しようと思っている。
明日は COMIC1 か……。友人に誘われたし、とりあえず行くとしましょうか。
/**/
形式しかサポートしないRhino を利用した高い圧縮性能を誇る YUI Compressor。これ、何気に IE の条件付きコンパイルをサポートしており、きちんと判定してコード中に残してくれる。しかし、完全にはサポートしていないようだ。コメントが複数行形式 /**/
の場合のみのサポートだからである(2.3.5 で確認)。
実のところ、条件付きコンパイルは1行形式のコメント //
でも書くことができる。次のコード断片は、同一の条件付きコンパイルを /**/
と //
の両形式で記述した例である。
/* 複数行形式の場合 */ /*@cc_on @if (@_jscript) var ie = true; @else @*/ var ie = false; /*@end @*/ // 1行形式の場合 //@cc_on @if (@_jscript) var ie = true; @else var ie = false; //@end
YUI Compressor は、1行形式のコメントを無視して削除対象にしてしまう。よって、YUI Compressor で圧縮しようとしているコードに条件付きコメントが含まれる場合は、必ず複数行形式のコメントで記述しなければならない。削除されてしまっては元も子もないからだ。
そもそも条件付きコメントは JScript の独自仕様であり、はっきり言って嫌らしいことこの上ない。できれば使いたくなどないが、使った方が労力とコードが省ける場合というのも往々にして存在する。絶対に積極的な利用をするべきではないが、必要悪として最低限利用する分には問題も少なくなるだろう。
そういえば、JScript Blog にガベージコレクタの記事が出てるな……。読んでおこう。
querySelectorAll
も、getElementsByClassName
の完全なる代替にはなりえない?Selectors API 仕様の最新 Editor's Draft を読み返していて「あーそうか」と改めて思ったのは、getElementsByClassName
が返す NodeList
オブジェクトと querySelectorAll
の返す NodeList
オブジェクトが似て非なるものだということ[1]だった。前者は動的に内容が変化するが、後者は静的である。ここで、少しばかり先日のネタの結論を修正。
getElementsByClassName
を実装してやるには、Selectors API(特に querySelectorAll
)を利用するのがベターここで、「他の方法」とは XPath による方法と線形探索による方法のことである。
さて、現時点での Selectors API 最新 Editor's Draft には次のような記述がある(強調筆者)。
The
NodeList
object returned by thequerySelectorAll()
methods must be static, not live. ([DOM3Core], section 1.1.1) Subsequent changes to the structure of the underlying document must not be reflected in theNodeList
object. This means that the object will instead contain a list of matchingElement
nodes that were in the document at the time the list was created.Selectors API
要は DOM Level 3 Core で定義されている NodeList
とは微妙に違い、「静的でなければならない(= live でない)」らしい。
DOM Level 3 Core には「live」の定義と NodeList
の説明がある。
The DOM also specifies a
NodeList
interface to handle ordered lists ofNodes
, such as the children of aNode
, or the elements returned by theElement.getElementsByTagNameNS(namespaceURI, localName)
method, and also aNamedNodeMap
interface to handle unordered sets of nodes referenced by their name attribute, such as the attributes of anElement
.NodeList
andNamedNodeMap
objects in the DOM are live; that is, changes to the underlying document structure are reflected in all relevantNodeList
andNamedNodeMap
objects. For example, if a DOM user gets aNodeList
object containing the children of anElement
, then subsequently adds more children to that element (or removes children, or modifies them), those changes are automatically reflected in theNodeList
, without further action on the user's part. Likewise, changes to aNode
in the tree are reflected in all references to thatNode
inNodeList
andNamedNodeMap
objects.Document Object Model Core
つまり、文書に対して何らかの操作が成されてノードが増減した場合、NodeList
のアイテムは動的に変化する。念のため HTML 5 仕様から getElementsByClassName
の NodeList
に関する記述を引いてみよう(強調筆者)。
The
getElementsByClassName(classNames)
method takes a string that contains an unordered set of unique space-separated tokens representing classes. When called, the method must return a liveNodeList
object containing all the elements in the document that have all the classes specified in that argument, having obtained the classes by splitting a string on spaces. If there are no tokens specified in the argument, then the method must return an emptyNodeList
.HTML 5
getElementsByClassName
は、通常の live な NodeList
オブジェクトを返さなければならない。
ところが、である。XPath による方法、線形探索による方法、そして querySelectorAll
による方法は、全て「live でない」何らかの Array
ないし NodeList
オブジェクトを返してしまう。XPath と線形探索は、その性質上どうしても Array
オブジェクトを返さざるを得ないし、querySelectorAll
は前述の通り「live でない」NodeList
オブジェクトを返す。
結果的に、どの代替手段でも完全な getElementsByClassName
は実装できないのである。
とはいえ、この仕様の相違にさえ目を瞑れば、XPath、線形探索、そして querySelectorAll
は十分に代替品として利用できる。動的に要素を増減させることが多い昨今の JavaScript 事情を考えれば、どこかで矛盾は出てくるし、現に出ているだろうが、何より大事なのは代替品が最低限必要な機能を持っているか否かである。
うーむ……。この絶妙な差異、どう考えても潜在的なバグの温床のような気がしてならない。どちらが悪い、というのではなくて、この差異をプログラマの側が意識していないと何か厄介なことが起きる可能性があるという話だ。得てしてそういうバグほど、デバッグしにくいのである。
でもこの話、どこかで誰かが既にしてるかもしれない。そうすると、私はある意味で車輪の再発明みたいなことをしているわけだが……気がつけたからいいや。
何だか色々心配になってきたな。自分が書いたコード、あとで見直してみよう。
前々から気になっていた点をいくつか修正。詳細は Updatelog に記述した。おおまかに言えば「CSS ファイル数の削減」と「ディレクトリ名の変更」を行った。
「CSS ファイル数の削減」は、前々からやろうとしていたことの1つ。以前は用途毎に分割して style.css
をヘッダファイル代わりに利用、インポートしていた。だが容量も大してない上に IE6 や IE7 用の CSS 群とミラーリングさせる時、どうしてもファイル数が多いと手間ばかりかかって仕方がない。構成も煩雑になり、必要な HTTP コネクションも多くなる。非常に微々たる問題としてファイル容量の増加もあるが、それはまあどうでもいい(微小すぎるため)。ともかく、現時点では利点より欠点の方が目立つ形になってしまったため、最小数のファイルにまとめる形式をとるようにした。
実はこの作業途中でむかーしに作成した印刷用 CSS がいつしか無効になっていたことが判明したのだが、これの対応はまた後々。今回、デザインそのものの対応は小さな変更をひとつだけ行うにとどめた。
「ディレクトリ名の変更」は、img
ディレクトリを media
ディレクトリにリネームしただけ。これは全置換用の正規表現を書く時間だけでおしまい。
マークアップはまだいいとして、スタイルはかなりガタがきてるなぁ。一応メンテしている分寿命も延びてはいるが……。ぼちぼち考えるだけ考えとくか。IE8 Beta 1 のこともあるし。ここいらで、そろそろ IE6 とはオサラバしたいし。
18時間ぐらい寝てしまった。こんなに睡眠とったのは久々だ。おかげで何もできず、何もする気にならず、久しぶりの過睡眠でズキズキする頭を抱えるしかない。
休日なのでフィードの更新も少なめ。ヤケクソでもうひと眠りしようかしらん。
getElementsByClassName
実装には Selectors API の querySelectorAll
を使いたい激しく久しぶりにサイトいじった……。さて、IE8 Beta 1 には getElementsByClassName
が実装されていない。この有用なメソッドはしばしば JavaScript ライブラリに収録されている。「Prototype JavaScript framework」が有名な例だろう。そして、Mozilla Firefox 3 や Apple Safari 3、Opera 9.5 Beta といった最近の UA は、getElementsByClassName
をネイティブサポートしていたりする。HTML5 でも定義されている。だが、Firefox 2 や Safari 2、Opera 9.2x、そして勿論 IE6、IE7、IE8 Beta 1 はネイティブでは実装していない。
結論から言ってしまえば「IE8 Beta 1 で getElementsByClassName
を実装してやるには、Selectors API(特に querySelectorAll
)を利用するのがベター」となる。
話を元に戻す。では、数多の JavaScript ライブラリは getElementsByClassName
をどのように実装しているのか? 答えのひとつは XPath だ。document.evaluate
がサポートされている UA では、@class
(class
属性値の値一致)を用いて getElementsByClassName
を実現している。Firefox 2、Safari 2、Opera 9.2x は document.evaluate
すなわち JavaScript からの XPath 利用をサポートしている。よって、モダンブラウザと呼ばれることもある UA のほとんどでは、パフォーマンス面でネイティブ実装には叶わないものの、そこそこ高速に動作してくれる getElementsByClassName
が利用できる。
では、XPath が利用できない場合――IE6 や IE7――はどうするのか? 答えは簡単で、とてもいやらしい。線形探索を利用するのである。つまり、document.getElementsByTagname("*")
や document.all
を利用してドキュメント内にある全ての要素を取得し、先頭から末尾までひとつひとつの要素の class
属性値をチェックしていくという意味だ。これは当然ながら一番頭の悪い方法になる。
ネイティブ getElementsByClassName
や XPath による実装も、内部実装は線形探索になっているかもしれない。しかし、それらはレンダリングエンジンの開発言語で書かれているだろう。C や C++ といった言語の方が、JavaScript より高速かつ少資源で動作しやすいのは明らかである。つまり、現状では ネイティブ < XPath < 線形探索
の順にパフォーマンスが悪くなる。
IE8 Beta 1 ではどうだろう。ネイティブ getElementsByClassName
も XPath もサポートしていない。では線形探索しか道はないのか?
そんなことはない。ここで Selectors API、具体的には querySelectorAll
メソッドの登場である。querySelectorAll
の詳細な解説は W3C の仕様や IT 戦記のエントリに任せるとして、以降では簡単に実装例を紹介するに留める。
次のコード断片は、Selectors API を利用した getElementsByClassName
の実装例である(他方法の実装は省略)。
var FOO = {}; // 適当な疑似名前空間 FOO.getElementsByClassName = function(elem, className) { return elem.querySelectorAll("."+className); };
実にシンプルだ。そして勿論、線形探索より高速に動作する。これは実際にテストしてもらった方がよいだろう。
、、、 の4種を用意した(利用できない場合は押下できない)。ボタン押下でアラートダイアログが出現し、1000 回の実行に何ミリ秒かかったかを表示する。IE8 Beta 1 では差が顕著に現れるだろう。
じきに、Selectors API を getElementsByClassName
に利用したライブラリも登場するかもしれない。
data/2008-04-19/test.js
に記述した。謎つぶし文@謎経由で、Sage 1.9.1 リリースを知る。3.0 Beta 5 用なので、完全にテスタ用。人柱希望者以外は入れないほうがいいだろう。Places はそれほどまでにやりにくいということか。
Trunk Nightly の 2008040606 に入れて使ってみた。インタフェイスは同じだが、挙動の面で Firefox 2 用の Sage 1.8 系とはかなり違っている。シングルクリックで Live Bookmark は開いてしまうし、一覧も出ない。おまけに、Places 上に Sage Feeds という特殊なフォルダを作成されてしまう。酷いことに、これは削除できないのだ。現状は、まだ常用に耐えうるレベルに達していないといえる。
それでも開発が続行されていたことに拍手喝采を送りたい。今常用しているフィードリーダは Brief だが、CVS HEAD でもその挙動はまだまだ遅い。もう少し様子をみることにする。
木曜日のことだったが。……買ったのは Lenovo ThinkPad X61 の NA75C36。同梱 OS は Windows XP。そして、今現在 Windows の基本的なカスタムは完了したので、Linux をインストールすべく準備中。ディストリビューションは今のところ Vine Linux にする予定。……主記憶と CPU が自宅のデスクトップより大容量 & 高性能ってどういうことやねん。いかん。早いとこ更なる貯蓄を決行しなければ。
Linux のインストールだが、とりあえず USB メモリを用いる方法が楽そうだ。Vine のインストールパッケージは CD-R に収納できる容量(700MB 未満)に抑えられているので、自宅にある2GB の USB メモリ(というより microSD)に簡単に入れられる。やり方は、そのものズバリ ThinkPad X シリーズ用に書かれている「Vine on X40」が参考になる。ブートローダは GRUB にしたいなー。あとで試そ。
あとは IBM 部品センター[1]に US キーボードを注文すれば完璧。勿論 42T3467 (NMB) の予定。それまでは HHK と併用して誤魔化す。もう Winodows の方はレジストリ設定変えちゃったし。いや、元に戻して再起動すればいいだけの話なんだが。
さて、これからコツコツと Vine Linux 環境を整えましょう。しばらくいじってなかったから……。shell は何にしようかなぁ。ここは Zsh にしちゃおうかしらん。いや、昔から使い慣れている Bash も捨てがたい。C Shell 系は好みじゃないから論外。ああ、GNU Emacs も CVS HEAD にしなきゃ。先月の26日に 22.2 が正式にリリースされたことだし。
03-5445-0365
。補修用部品の購入方法・部品番号一覧があったりする。まとめた方に感謝。Mozilla Firefox 3 Beta 5 がリリースされた。いつもの通り、開発者プレビュー版であるため一般ユーザの利用は推奨されない。
次からはついに RC か……。どうなることやら。
そして、Opera 9.27 もリリースされた。日本の公式サイトでは、現時点 (22:03) で配布が始まっていない。本家からダウンロードした方が無難だろう。デフォルトで International 版(ローカライズされているバージョン)がダウンロードできる。
驚いたことに、「WebKit 開発者の一部よ、恥を知れ! - Opera の非公開テストビルドと WebKit Nightly r31342 が Acid3 をクリア」は様々な人に読んでいただけたようで、その内容に対する指摘を色々と頂戴することができた。その一部は既に該当記事にも反映したが、全てを追記するには改めて書き起こす必要があると考えた。それが、今回の内容である。
因みに「WebKit 開発者の一部よ、恥を知れ!」の後、追加情報として「WebKit の Acid3 クリア用の修正 Changeset 31322 がバックアウトされる」という後日談を書いた。以降の内容は、前述した後日談の閲覧を前提として記述する。
私が現時点で把握できている指摘は、全て「WebKit 開発者の一部よ、恥を知れ!」に対するものである。そして、その殆どは「Safari Part41」でのものだ。以後、引用を交えつつ内容を検証する。
まず、レス895 (ID:JCWAmUkB0 氏) から引用。
- 895 :名称未設定:2008/04/02(水) 00:51:11 ID:JCWAmUkB0
- >>868
このサイトさ、Operaが100点取った!って宣言したビルドの点数が
実は99点だったってことに「まったく言及していない」のはなぜだろう。
>http://ln.hixie.ch/?start=1206578003&count=1
> This presumably means Opera is now at 99/100... the race continues!
100点取ったのはその後のビルドなんだよね。
そもそもWebKitチームから指摘されたAcid3の不具合が修正されたこと
さえきちんと書いておらず、ものすごく恣意的だと思うんだが。Safari Part41
特に Hickson 氏の Blog エントリについては完全に見落としていた。情報提供ありがとうございました。これでは、ものすごく恣意的だと思う
と受けとめられても仕方ない。特に、WebKitチームから指摘されたAcid3の不具合が修正されたことさえきちんと書いて
いなかったことは不覚の極みである。
但し、Operaが100点取った!って宣言したビルドの点数が実は99点だった
というのは This presumably means Opera is now at 99/100...
を踏まえてのことだと考えられるが、presumably が「おそらく」という意味なので、Hickson 氏も推測しているだけと考えられる点は指摘できるだろう。100点取ったのはその後のビルド
という指摘はソースがわからないため、検証することができなかった。
次に、レス896 (ID:JCWAmUkB0 氏、レス895と同一 ID) から引用。
- 896 :名称未設定:2008/04/02(水) 00:54:40 ID:JCWAmUkB0
- もう一ついうと、
Webkitのblogに書いてあったけど、
サブピクセルレンダリングのパッチを適用せずとも100点取れていたって
ことにも言及していない。
Acit3がReferenceImageとのPixel-to-Pixelでの一致を要求する時点で
サブピクセルレンダラを標準実装する環境とあわないのは自明の理。
きちんと英文読めてるのかな?Safari Part41
サブピクセルレンダリングのパッチを適用せずとも100点取れていたってことにも言及していない
点についても、完全に見落としていた。Webkitのblogに書いてあった
というのは「Surfin' Safari」のエントリ「WebKit achieves Acid3 100/100 in public build」のことだと考えられる。確かに、サブピクセルレンダリングの件が Acid3 の点数には影響しない現象であることは確かであり、この点を明確にしなかった私の文章に落ち度があった。
Acit3がReferenceImageとのPixel-to-Pixelでの一致を要求する時点でサブピクセルレンダラを標準実装する環境とあわないのは自明の理
というのも、なるほどもっともな話である。
きちんと英文読めてるのかな?
については、以前にも脚注でも言及したように、私の英語力は酷いものであり、英文が読めていなかった。これは自他共に認める事実であると思う[1]。
最後に、レス900 (ID:5E+KvCxK0 氏) から引用(脚注筆者)。
- 900 :名称未設定:2008/04/02(水) 01:07:15 ID:5E+KvCxK0
- >>895
> このサイトさ、Operaが100点取った!って宣言したビルドの点数が
> 実は99点だったってことに「まったく言及していない」のはなぜだろう。
…
> そもそもWebKitチームから指摘されたAcid3の不具合が修正されたこと
> さえきちんと書いておらず、ものすごく恣意的だと思うんだが。
モダンブラウザに興味があるのに、HyattとAppleやMozillaとの関係も
知らないってのは、ちょっと珍しい人だよね。あとWeb標準にこだわりながら
ユーザビリティ的には×××つけたくなるような疑似inline-frame仕立ての
サイトデザインもようわからん[2]。
でもま、このスレでの誤訳の指摘が反映されてるあたり、書けば聞く耳
持ってくれる人ではあるんじゃないかなと。Safari Part41
モダンブラウザに興味があるのに、HyattとAppleやMozillaとの関係も知らないってのは、ちょっと珍しい人だよね
という意見には素直に「その通りでした」としか言いようがない。Hyatt 氏のことについて知ったのも、2008年3月29日になってからである。
さて、これからの言及は言い訳じみて捉えられる可能性があるため、それを私自身も自覚しているという前提で話を進める。
「WebKit 開発者の一部よ、恥を知れ!」で、私は先に結論を述べた。今回、その結論を修正し、本当に言いたかったことをより詳細に言及する。
まず、「WebKit 開発者の一部よ、恥を知れ!」の結論を強調位置も含め次のように修正する。「WebKit 開発者は Acid3 をリファレンスレンダリングとのピクセル完全一致レベルでクリアすることのみに特化した、汎用性のない、バグの温床となりうるコードを実装した」。
以降、前述した結論の本意について記述する。
私が一番問題視したのは、あくまでも「汎用性のない、バグの温床となりうるコードを実装した」点である。つまり「Changeset 31322 のようなコードがレビューを通過し、チェックインされてしまったことが問題である」と言いたかったのだ。むしろ、Acid3 をクリアしているか否か[3]など、どうでもよかったのである。結果として、パッチの意図、Acid3 への影響、翻訳、人物に対する知識といったトピックが生まれた。
パッチの意図、Acid3 への影響、翻訳、人物に対する知識などは、Changeset 31322 のコードが持つ忌まわしい本質とは切り離して考えるべきである。私が論じようとしたのは後者、即ち「Changeset 31322 のコードが持つ忌まわしい本質」についてであった[4]。
不明瞭な文章、説明・調査不足、無知などが重なり、結果 Opera 優遇のような印象を与え、「WebKit を攻撃している」と受けとめられる可能性がある文章を記述したことは、私にとっての恥であり、WebKit を愛している人々からの指摘を受けて当然ともいえる。
ここに意図の明瞭化と情報の加筆訂正を行い、関係者並びに指摘を頂戴した方々に、心よりお詫びと感謝を申し上げる次第である。