エイプリルフール企画などでレガシーブラウザ対応する場合に覚えておきたい最初期のDHTMLブラウザのJavaScriptの罠たちを回避する
関数定義 → 関数宣言(2024/04/04)
執筆時点で未解決だった、Gecko 0.8.0以下での括弧の下の関数の問題は es2-postprocessor 0.12.0で解決しています。(2023/01/04 追記)
この記事は、JavaScript Advent Calendar 2022の3日目です。2日目の記事は martinheidegger さんによる「JavaScript を使って DNS データ受け取る。」になります。
1. はじめに
本記事を一般化して書くと、エイプリルフール企画で0年代前半風の Web サイトを制作する案件が来て「見た目はバイブスでてますけど、コレ、当時のブラウザで動くんスか?」と恐ろしい方向に話が進んだ際に、気配をフェードアウトする暇でこの記事のことを思い出してください。
当時のブラウザを取り出して閲覧してくれるユーザーの手元でバッチリ動いてくれたなら、ハートキャッチですね。
因みに『空の軌跡』のエイプリルフール企画については、Internet Exproler 3.0や Netscape Navigator 3.0で閲覧した報告のツイートを目にした記憶があるので、油断なりません。(*1)
さて、0年代前年は DHTML ブラウザが出揃い、後の Ajax(2005年~)の開花を準備します。
Microsoft が IE4 に向けて書いた文書によると DHTML はダイナミックコンテンツ、ダイナミックスタイル、CSS-P などがその構成技術です。これらに加えて、要素とテキストレンジの計測が正常に実装されているなら、理論上は、新しい CSS の実装を待たずともどんなレイアウトでも可能です。つまり DHTML さえ健全ならどんな表現でも可能なわけです。(*2)
そんな素晴らしい能力を秘めたレガシー DHTML ブラウザですが、いざそれらに向けて開発を始めると、不可解なバグと貧弱な開発者ツールに泣くことになります。エラーコンソールがあれば御の字という世界へようこそ。
本記事では未実装とバグの中から印象的なものを3つご紹介します。どれも JavaScript を書き換えるポストプロセッサーを用意してエレガントめに迂回できた問題たちです。
2. IE5 以下に未実装のビルトインオブジェクトとメソッドを補う
最初期の DHTML ブラウザ向け開発を始めたらまず入手したいのが、IE5(1999年3月18日)からサポートする jQuery クローン「kQuery」を開発していた ofk 氏による polyfill 集です。ECMAScript 3未満の IE 4~5 を ES3 のちょい手前くらいまで引き上げる事ができます。
僕の手でいくつかのバグを修正し、他の足りない組み込みオブジェクトとメソッドの polyfill を加えた es2-to-es3 を公開しています。自動で polyfill を埋め込む gulp プラグインにもしていて npm からインストール出来ます。
3. Opera 7.23以下はラベル付きブロックで構文エラー
Opera 7.23(7.20は2003年9月23日に公開)以下は、ラベル付きブロックを非サポートで、7.50(2004年5月12日)以降でサポートします。
JavaScript でラベル付きブロックを使うコーディングは寡聞にしてあまり見かけないようですが、僕のケースでは Closure Compiler で ADVANCED コンパイルをすると遭遇しました。「ラベル付きブロックさえなければ動くのに…!」という状況は結構口惜しいですね。
そんなラベル付きブロックを書き換えるツール es2-postprocessor を公開しているのでもう大丈夫です。
3.1. ラベル付きブロックを do~while
または即時実行関数に書き換える
こんな感じに書き換えます。
a: {
break a;
}
// ↓
do{
break; // <= break a;
} while(false);
// ---------
a: {
b: {
break b;
break a;
}
break a;
}
// ↓
(function(){
do{
break; // <= break b;
return; // <= break a;
} while(false);
return; // <= break a;
})();
この書き換えを自動で、あらゆるパターンで漏れなく実施することは、僕の手にはあまりました。そこで、エラーを投げて書き換えを諦める場合があります。但し、僕の使用状況では Closure Compiler の出力するラベル付きブロックは簡素な為、書き替えできなかったことは今のところありません。
それにしても、Opera 7.xは可愛くて良いです。7.0(2003年1月28日)は若干遅めの登場にしては実装状況がやや遅れ気味な気がしますが、可愛いので良しです。
4. 優等生の Gecko 0.8.0以下のややこしめのバグ
Netscape Navigator 6(Gecko 0.6)は2000年の11月14日にリリースされたブラウザで IE 5.5(2000年7月17日公開)が同期です。この時点で Object.prototype.__defineGetter__
, Object.prototype.__defineSetter__
を実装していて頼もしいです。一方の CSS とペイント周りでは、問題がやや多めに感じます。
今回紹介するのは Gecko ~0.8.0で遭遇する JavaScript 実装のバグです。0.8.1では解決したことを確認しました。何年か前に初めて遭遇した時は、訳が分からず頭が白くなりましたが、この11月に条件を特定して解決するところまで漕ぎつけました。(執筆中に発生条件が即時実行関数に留まらないことが判明しました。これへの対処は後日にします。es2-postprocessor 0.12.0 以降で解決しています。2024/01/04 追記)
4.1. 即時実行関数が絡むバグ
先祖スコープのオブジェクトを参照すると常に undefined
になっているバグです。但し、グローバルオブジェクト(window
のプロパティ)では問題が起きません。即時実行関数などの括弧で囲まれた無名関数の中からの参照でだけ発生します。そして、これらの関数と同じスコープに関数宣言が存在する場合は発生しません。
と、文章にするとややこしいですね。テストページでご確認いただけます。
この問題も es2-postprocessor に、次のように即時実行関数と括弧で囲まれた(無名)関数を関数宣言に書き換えて回避するコードを追加して対処しました。
function parentScope(){
var a = {};
(function(){
alert(typeof a); // "undefined"
})();
};
// ↓
function parentScope(){
var a = {};
function b(){
alert(typeof a); // "object"
};
b();
b = false;
};
5. さいごに
これで0年代風の見た目に留まらず、対応ブラウザも0年代の Web 開発をする下準備が出来ました。
さて、現在では Visual Studio Code などの優れたツールを使って Web 開発が行えます。現代のツールと知見を駆使してチューニングされた Web コンテンツをレガシーブラウザに読み込ませたら、当時には引き出せなかったパワーを引き出すことが出来るかもしれませんね!(*3)
「この Canvas ゲームですけど、Java アプレットってので出来るんじゃないっスか?」
「… … 、、、」
(function(){
obj.filters.alpha.opacity -= 5;
0 < obj.filters.alpha.opacity && setTimeout(arguments.callee, 50);
})();
5.1. 注釈
Netscape 4.xまでは日本語 UTF-8 に非対応のようです。公開サーバ側で Shift-JIS などで HTML を返す必要があると思います。そして、仮にこのような実装がされていても Web アーカイブには記録されません。
- やっぱり、
Canvas
とWebAudio
とGamePad API
とService Worker
とIndexedDB
は欲しいです。 - という事象は確認していません。特に Opera 7.xの DHTML は、激しくモッサリです。
5.2. ご参考リンク
- 「令和にも残そう、懐かしい90年代WEBデザイン。さよなら平成。」
- 「レトロブームが、どのようにwebデザインに取り入れられているか」
「ニュートロ(Newtro)」とは「現在or未来(New)」と「過去(Retro)」を融合させた「新しい過去」を意味する韓国で生まれた新造語です。
- 「懐かしのインターネット。AppleにGoogle、Amazonなど1999年のサイトデザインはこんな感じだった。」
90年代は、ウェブのグラフィックデザインの可能性が模索された時代でした。当時、ウェブデザイナーの大多数は印刷物のデザインしか手がけたことがありませんでした。だからウェブサイトをどのようにデザインするのか、その方法が試行錯誤されていたのです。