[インデックス 12728] ファイルの概要
このコミットは、Go言語のドキュメント表示に使用されるJavaScriptファイル doc/godocs.js
におけるInternet Explorer 8 (IE8) で発生していたエラーを修正するものです。具体的には、IE8がネイティブでサポートしていない getElementsByClassName
メソッドの実装を追加し、コードのlint警告をいくつか解消しています。
コミット
commit 782feeb765b392e0a69ff35cc57c4b92cd5796ea
Author: Yasuhiro Matsumoto <mattn.jp@gmail.com>
Date: Fri Mar 23 12:23:53 2012 +1100
doc/godoc.js: fix error on IE8.
* implement simple getElementsByClassName for IE8.
* remove some lint warnings.
Fixes #3318.
R=golang-dev, adg
CC=golang-dev
https://golang.org/cl/5881054
GitHub上でのコミットページへのリンク
https://github.com/golang/go/commit/782feeb765b392e0a69ff35cc57c4b92cd5796ea
元コミット内容
doc/godoc.js: fix error on IE8.
* implement simple getElementsByClassName for IE8.
* remove some lint warnings.
Fixes #3318.
R=golang-dev, adg
CC=golang-dev
https://golang.org/cl/5881054
変更の背景
このコミットの主な背景は、Internet Explorer 8 (IE8) においてGo言語のドキュメント(godoc)が正しく表示されない、または機能しないという問題が存在したことです。具体的には、IE8が標準で document.getElementsByClassName
メソッドをサポートしていなかったため、このメソッドに依存するJavaScriptコードがエラーを引き起こしていました。
コミットメッセージにある Fixes #3318
は、この問題がGoプロジェクトのIssueトラッカーで報告されていたことを示しています。Issue #3318は「godoc.js: IE8 compatibility」というタイトルで、IE8でのgodocの表示に関する問題を指摘していました。この修正は、古いブラウザであるIE8のユーザーにもgodocが利用できるようにするための互換性向上を目的としています。
また、コードのlint警告の解消も行われており、これはコード品質の維持と改善の一環として実施されました。
前提知識の解説
1. getElementsByClassName
メソッド
getElementsByClassName
は、指定されたクラス名を持つすべての子要素のリストを、NodeList
オブジェクトとして返す Document
インターフェースのメソッドです。これはHTML要素をJavaScriptで選択する際によく使われる便利な機能です。
- 現代のブラウザ: Chrome, Firefox, Safari, Edgeなどの現代のブラウザでは、このメソッドは標準でサポートされており、広く利用されています。
- Internet Explorer 8 (IE8): IE8は2009年にリリースされたブラウザであり、HTML5やCSS3の多くの機能、そして一部のDOM操作メソッド(
getElementsByClassName
を含む)をネイティブでサポートしていませんでした。そのため、IE8でこれらの機能を使用するには、ポリフィル(polyfill)と呼ばれる互換性レイヤーや、代替の実装が必要となります。
2. ポリフィル (Polyfill)
ポリフィルとは、新しいWeb標準の機能をサポートしていない古いブラウザに対して、その機能を提供するためのJavaScriptコードのことです。このコミットで行われている getElementsByClassName
の実装は、まさにポリフィルの典型的な例と言えます。これにより、IE8のような古いブラウザでも、新しいブラウザと同じように getElementsByClassName
を使用するコードが動作するようになります。
3. JavaScriptの var
と let
/const
(lint警告関連)
このコミットが行われた2012年時点では、JavaScriptの変数宣言は主に var
キーワードが使われていました。let
と const
はES2015 (ES6) で導入されたもので、ブロックスコープを持つため、変数のスコープに関する問題を減らし、より安全なコーディングを可能にします。
このコミットで「lint警告の解消」とあるのは、おそらく当時のJavaScriptのベストプラクティスや、使用されていたlintツール(JSLintやJSHintなど)のルールに則って、var
の使用方法やその他のコーディングスタイルに関する警告を修正したことを指していると考えられます。例えば、変数の再宣言や、意図しないグローバル変数化などを避けるための修正が含まれている可能性があります。
4. ===
と ==
(lint警告関連)
JavaScriptにおける等価演算子には ==
(抽象等価演算子) と ===
(厳密等価演算子) があります。
==
: 比較する値の型が異なる場合、型変換を行ってから比較します。これにより、予期せぬ結果を招くことがあります(例:"" == 0
はtrue
)。===
: 比較する値の型と値の両方が同じである場合にのみtrue
を返します。型変換は行いません。
一般的に、厳密な比較を行う ===
の使用が推奨されており、多くのlintツールは ==
の使用に対して警告を発します。このコミットで search.value != ""
が search.value !== ""
に変更されているのは、このlint警告を解消し、より厳密な比較を行うための修正です。
技術的詳細
このコミットの技術的な核心は、IE8で不足していた getElementsByClassName
メソッドの代替実装を提供することと、既存のコードベースの品質を向上させるためのlint警告の解消です。
getElementsByClassName
の代替実装
IE8は document.getElementsByClassName
をサポートしていませんでしたが、document.getElementsByTagName('*')
を使ってすべての要素を取得し、それらの要素の className
プロパティをチェックすることで、同様の機能を実現できます。
コミットで追加された getElementsByClassName
関数は以下のロジックで動作します。
- ネイティブサポートの確認: まず
base.getElementsByClassName
が存在するかどうかを確認します。もし存在すれば、そのネイティブメソッドをそのまま使用します。これは、現代のブラウザではネイティブメソッドが最も効率的であるためです。 - フォールバック実装: ネイティブメソッドが存在しない場合(IE8のような古いブラウザの場合)、以下の手順でクラス名に一致する要素を探します。
base.getElementsByTagName('*')
を使用して、指定されたbase
要素内のすべての子孫要素を取得します。- 取得したすべての要素をループで処理し、各要素の
className
プロパティが引数で渡されたclazz
(クラス名)と一致するかどうかを確認します。 - 一致する要素が見つかった場合、それを
foundElements
配列に追加します。 - 最終的に、
foundElements
配列を返します。
この実装により、IE8でも getElementsByClassName
を使用する既存のコードがエラーなく動作するようになります。
lint警告の解消
コミットメッセージにある「remove some lint warnings」は、以下の具体的な変更によって実現されています。
- 厳密等価演算子
!==
の使用:if (search.value != "")
がif (search.value !== "")
に変更されました。これは、前述の通り、JavaScriptの型変換による予期せぬ挙動を避けるために、厳密な比較を行う!==
(または===
) を使用するというベストプラクティスに従ったものです。
- 変数のスコープと再宣言の修正:
godocs_bindExamples
関数内で、ループ変数i
がfor (var i = 0; i < links.length; i++)
のように再宣言されていました。JavaScriptのvar
は関数スコープを持つため、同じ関数内でvar i
を複数回宣言してもエラーにはなりませんが、lintツールによっては警告を発することがあります。このコミットでは、2つ目のループのvar
が削除され、単にfor (i = 0; i < links.length; i++)
となっています。これにより、i
が関数スコープ内で一度だけ宣言され、再利用される形となり、lint警告が解消されます。
コードの再配置
godocs_onload
関数とその関連する bindEvent(window, 'load', godocs_onload);
の呼び出しが、ファイルの先頭から末尾に移動されています。これは機能的な変更ではありませんが、コードの可読性や論理的な順序を改善するためのリファクタリングの一環と考えられます。一般的に、関数は定義されてから呼び出されるのが自然な流れであり、JavaScriptでは関数宣言の巻き上げ(hoisting)があるためどこに定義しても動作しますが、コードの構造をより明確にするためにこのような配置変更が行われることがあります。
コアとなるコードの変更箇所
doc/godocs.js
ファイルに対して以下の変更が行われました。
godocs_onload
関数の移動:- ファイルの先頭にあった
bindEvent(window, 'load', godocs_onload);
とgodocs_onload
関数全体が、ファイルの末尾に移動されました。
- ファイルの先頭にあった
godocs_bindSearchEvents
内の比較演算子の変更:if (search.value != "")
がif (search.value !== "")
に変更されました。
godocs_nodeToText
関数の移動:godocs_nodeToText
関数が、godocs_generateTOC
関数の下からgodocs_bindSearchEvents
関数の下に移動されました。
getElementsByClassName
ポリフィルの追加:- 新しい関数
getElementsByClassName
が追加されました。
- 新しい関数
godocs_bindExamples
内のgetElementsByClassName
の使用と変数i
の再宣言の修正:document.getElementsByClassName
の呼び出しが、新しく定義されたgetElementsByClassName
関数に置き換えられました。for (var i = 0; i < links.length; i++)
のvar
が削除され、for (i = 0; i < links.length; i++)
となりました。
godocs_bindExampleToggle
内のgetElementsByClassName
の使用:eg.getElementsByClassName
の呼び出しが、新しく定義されたgetElementsByClassName
関数に置き換えられました。
godocs_bindExampleToggle
内のイベントハンドラの修正:bindEvent
の第3引数に直接無名関数を渡すのではなく、callback
という変数に格納してから渡す形に変更されました。
godocs_bindExampleLink
内の条件分岐の修正:if (i < 0)
の後に{ return; }
が追加され、単一行のステートメントでもブロックを使用する形になりました。
コアとなるコードの解説
getElementsByClassName
関数
function getElementsByClassName(base, clazz) {
if (base.getElementsByClassName) {
return base.getElementsByClassName(clazz);
}
var elements = base.getElementsByTagName('*'), foundElements = [];
for (var n in elements) {
if (clazz == elements[n].className) {
foundElements.push(elements[n]);
}
}
return foundElements;
}
この関数は、IE8のような getElementsByClassName
をネイティブでサポートしないブラウザのためのポリフィルです。
base.getElementsByClassName
が存在する場合(現代のブラウザ)、そのネイティブメソッドを直接呼び出して結果を返します。- 存在しない場合、
base.getElementsByTagName('*')
を使ってbase
要素内のすべての子孫要素を取得します。 - 取得した
elements
をfor...in
ループで反復処理し、各要素のclassName
プロパティが引数clazz
と一致するかどうかを確認します。 - 一致する要素は
foundElements
配列に追加され、最終的にこの配列が返されます。
godocs_bindExamples
関数
function godocs_bindExamples() {
var examples = getElementsByClassName(document, "example");
for (var i = 0; i < examples.length; i++) {
godocs_bindExampleToggle(examples[i]);
}
var links = getElementsByClassName(document, "exampleLink");
for (i = 0; i < links.length; i++) { // ここで var i が削除された
godocs_bindExampleLink(links[i]);
}
}
この関数は、ドキュメント内の「example」クラスを持つ要素と「exampleLink」クラスを持つ要素にイベントハンドラをバインドします。
- 変更点として、
document.getElementsByClassName
の代わりに、新しく定義されたgetElementsByClassName
関数が使用されています。これにより、IE8でもこれらの要素が正しく取得されるようになります。 - 2つ目の
for
ループでvar i
が削除されています。これは、関数スコープ内でi
が既に宣言されているため、再宣言の警告を避けるためのlint修正です。
godocs_bindExampleToggle
関数
function godocs_bindExampleToggle(eg) {
var heading = getElementsByClassName(eg, "exampleHeading");
var callback = function() {
if (eg.className == "example") {
eg.className = "exampleVisible";
} else {
eg.className = "example";
}
};
for (var i = 0; i < heading.length; i++) {
bindEvent(heading[i], "click", callback);
}
}
この関数は、例のセクションのヘッディングにクリックイベントをバインドし、クリックされると例の表示/非表示を切り替えます。
- ここでも
eg.getElementsByClassName
の代わりに、新しく定義されたgetElementsByClassName
関数が使用されています。 - イベントハンドラが無名関数として直接
bindEvent
に渡されるのではなく、callback
という変数に格納されてから渡される形に変更されています。これは、コードの可読性を向上させるためのリファクタリングと考えられます。
godocs_bindExampleLink
関数
function godocs_bindExampleLink(l) {
var prefix = "example_";
bindEvent(l, "click", function() {
var i = l.href.indexOf("#"+prefix);
if (i < 0) { // ここに { return; } が追加された
return;
}
var id = prefix + l.href.slice(i+1+prefix.length);
var eg = document.getElementById(id);
eg.className = "exampleVisible";
});
}
この関数は、例へのリンクにクリックイベントをバインドし、クリックされると対応する例のセクションを表示します。
if (i < 0)
の後に{ return; }
が追加されました。これは、単一行のif
ステートメントでもブロックを使用するというコーディングスタイルに合わせたもので、可読性と保守性を向上させます。
関連リンク
- Go Issue #3318: https://code.google.com/p/go/issues/detail?id=3318 (古いGoogle Codeのリンクですが、当時のIssueトラッカーのURLです)
- Go Change-Id:
I782feeb765b392e0a69ff35cc57c4b92cd5796ea
(GerritのChange-Id)
参考にした情報源リンク
- MDN Web Docs: Document.getElementsByClassName()
- MDN Web Docs: Strict equality (===) - JavaScript | MDN
- MDN Web Docs: var - JavaScript | MDN
- MDN Web Docs: let - JavaScript | MDN
- MDN Web Docs: const - JavaScript | MDN
- Polyfill - Wikipedia
- Google Code Archive - golang/go - Issue 3318: godoc.js: IE8 compatibility (Issue #3318の詳細)
- Gerrit Code Review - golang/go - 5881054: doc/godoc.js: fix error on IE8. (GerritのChange-Idページ)