Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

[インデックス 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の varlet/const (lint警告関連)

このコミットが行われた2012年時点では、JavaScriptの変数宣言は主に var キーワードが使われていました。letconst はES2015 (ES6) で導入されたもので、ブロックスコープを持つため、変数のスコープに関する問題を減らし、より安全なコーディングを可能にします。

このコミットで「lint警告の解消」とあるのは、おそらく当時のJavaScriptのベストプラクティスや、使用されていたlintツール(JSLintやJSHintなど)のルールに則って、var の使用方法やその他のコーディングスタイルに関する警告を修正したことを指していると考えられます。例えば、変数の再宣言や、意図しないグローバル変数化などを避けるための修正が含まれている可能性があります。

4. ===== (lint警告関連)

JavaScriptにおける等価演算子には == (抽象等価演算子) と === (厳密等価演算子) があります。

  • ==: 比較する値の型が異なる場合、型変換を行ってから比較します。これにより、予期せぬ結果を招くことがあります(例: "" == 0true)。
  • ===: 比較する値の型と値の両方が同じである場合にのみ true を返します。型変換は行いません。

一般的に、厳密な比較を行う === の使用が推奨されており、多くのlintツールは == の使用に対して警告を発します。このコミットで search.value != ""search.value !== "" に変更されているのは、このlint警告を解消し、より厳密な比較を行うための修正です。

技術的詳細

このコミットの技術的な核心は、IE8で不足していた getElementsByClassName メソッドの代替実装を提供することと、既存のコードベースの品質を向上させるためのlint警告の解消です。

getElementsByClassName の代替実装

IE8は document.getElementsByClassName をサポートしていませんでしたが、document.getElementsByTagName('*') を使ってすべての要素を取得し、それらの要素の className プロパティをチェックすることで、同様の機能を実現できます。

コミットで追加された getElementsByClassName 関数は以下のロジックで動作します。

  1. ネイティブサポートの確認: まず base.getElementsByClassName が存在するかどうかを確認します。もし存在すれば、そのネイティブメソッドをそのまま使用します。これは、現代のブラウザではネイティブメソッドが最も効率的であるためです。
  2. フォールバック実装: ネイティブメソッドが存在しない場合(IE8のような古いブラウザの場合)、以下の手順でクラス名に一致する要素を探します。
    • base.getElementsByTagName('*') を使用して、指定された base 要素内のすべての子孫要素を取得します。
    • 取得したすべての要素をループで処理し、各要素の className プロパティが引数で渡された clazz(クラス名)と一致するかどうかを確認します。
    • 一致する要素が見つかった場合、それを foundElements 配列に追加します。
    • 最終的に、foundElements 配列を返します。

この実装により、IE8でも getElementsByClassName を使用する既存のコードがエラーなく動作するようになります。

lint警告の解消

コミットメッセージにある「remove some lint warnings」は、以下の具体的な変更によって実現されています。

  1. 厳密等価演算子 !== の使用:
    • if (search.value != "")if (search.value !== "") に変更されました。これは、前述の通り、JavaScriptの型変換による予期せぬ挙動を避けるために、厳密な比較を行う !== (または ===) を使用するというベストプラクティスに従ったものです。
  2. 変数のスコープと再宣言の修正:
    • godocs_bindExamples 関数内で、ループ変数 ifor (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 ファイルに対して以下の変更が行われました。

  1. godocs_onload 関数の移動:
    • ファイルの先頭にあった bindEvent(window, 'load', godocs_onload);godocs_onload 関数全体が、ファイルの末尾に移動されました。
  2. godocs_bindSearchEvents 内の比較演算子の変更:
    • if (search.value != "")if (search.value !== "") に変更されました。
  3. godocs_nodeToText 関数の移動:
    • godocs_nodeToText 関数が、godocs_generateTOC 関数の下から godocs_bindSearchEvents 関数の下に移動されました。
  4. getElementsByClassName ポリフィルの追加:
    • 新しい関数 getElementsByClassName が追加されました。
  5. godocs_bindExamples 内の getElementsByClassName の使用と変数 i の再宣言の修正:
    • document.getElementsByClassName の呼び出しが、新しく定義された getElementsByClassName 関数に置き換えられました。
    • for (var i = 0; i < links.length; i++)var が削除され、for (i = 0; i < links.length; i++) となりました。
  6. godocs_bindExampleToggle 内の getElementsByClassName の使用:
    • eg.getElementsByClassName の呼び出しが、新しく定義された getElementsByClassName 関数に置き換えられました。
  7. godocs_bindExampleToggle 内のイベントハンドラの修正:
    • bindEvent の第3引数に直接無名関数を渡すのではなく、callback という変数に格納してから渡す形に変更されました。
  8. 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 要素内のすべての子孫要素を取得します。
  • 取得した elementsfor...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 という変数に格納されてから渡される形に変更されています。これは、コードの可読性を向上させるためのリファクタリングと考えられます。
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 ステートメントでもブロックを使用するというコーディングスタイルに合わせたもので、可読性と保守性を向上させます。

関連リンク

参考にした情報源リンク