JavaScript: getElementById()メソッドを使わずにid属性値で要素を参照する

Twitterのタイムラインで、つぎのような情報に接しました。このふるまいについての日本語の説明が、検索しても見当たらなかったので、調べたことを簡単にまとめます。

<blockquote class="twitter-tweet" data-lang="ja"><p lang="ja" dir="ltr">&lt;div id=&quot;foo&quot;&gt;ラーメン&lt;/div&gt;<br>の要素をJavaScriptで取得したいとき、<br>getElementById()を使わなくてもfooだけ指定すればいい。<br><br>例： foo.innerHTML = &quot;うどん&quot;<a href="https://t.co/D8SgIbzxVo">https://t.co/D8SgIbzxVo</a><a href="https://twitter.com/hashtag/JavaScript?src=hash&amp;ref_src=twsrc%5Etfw">#JavaScript</a> <a href="https://t.co/lJjEwYHcR1">pic.twitter.com/lJjEwYHcR1</a></p>&mdash; 鹿野壮@ICS (@tonkotsuboy_com) <a href="https://twitter.com/tonkotsuboy_com/status/993808648596897792?ref_src=twsrc%5Etfw">2018年5月8日</a></blockquote>
<script async src="https://platform.twitter.com/widgets.js" charset="utf-8"></script>

#要素をid属性で参照してみる
まずは、論より証拠で試してみましょう。要素の`id`属性値をあたかも変数のように参照して、テキストを加えます。すると、たしかに空の要素にテキストが差し込まれるのです。

```html
<body>
<div id="element"></div>
<script>
element.textContent = '参照できた!!';
</script>
</body>
```

ただし、JavaScriptコードで`id`属性値と同じ識別子(変数や関数)を定めれば、そちらが優先です。つぎのコードでは、新たにつくって加えられる子要素(`<h1>`)に、テキストが与えられます。

```javascript
const element = document.createElement('h1');
document.getElementById('element').appendChild(element);
element.textContent = '参照できた!!';
```

また、`id`属性で直に要素を参照するやり方は、[Internet Explorer 8では使えない](https://coderwall.com/p/umiruq/javascript-getelementbyid-not-required)ようです。

#id属性値はドキュメントのグローバルなプロパティとして扱われる
要素の`id`属性値はドキュメントのグローバルな名前空間(`Window`オブジェクト)のプロパティとして扱われ、その要素が参照されます(HTML 5.2仕様§6.3.3.「[Named access on the Window object](https://www.w3.org/TR/html5/browsers.html#named-access-on-the-window-object)」後掲邦訳参照)。けれど、前述のとおり、JavaScriptコードの識別子の方が優先されました。さらに、グローバルプロパティの座を争う識別子は、ほかにもあるのです(たとえば、要素の`name`属性)。

そのためHTML 5.2仕様は、`id`属性から要素を参照するには、`document.getElementById()`メソッドの使用が望ましいとしています(§6.3.3.)。興味深いふるまいではあるものの、豆知識にとどめておくのがよさそうです。

####§6.3.3. Windowオブジェクトへの名前による参照(HTML 5.2)
> **window[name]**
指定された要素または要素の集合を返します。
一般に、これに依存したコードは脆弱になります。 どのidがこのAPIにマッピングされることになるかは、ときとともに変わります。たとえば、Webプラットフォームに新しい機能が追加された場合などです。 代わりに、`document.getElementById（）`や`document.querySelector（）`をお使いください。
(筆者邦訳)

#別要素のname属性に同名の値を与えてみる
前項でお話は終わってはいます。ただ、要素によっては`name`属性値がやはりグローバルで参照できます。一応、それも試しておきましょう。

`name`属性がグローバルで参照される要素のひとつは`<form>`です。前掲の`<body>`要素にこれを加え、`name`属性値をあえてもうひとつの要素の`id`属性値と同じにします。

```html
<div id="element"></div>
<form name="element"></form>
```

`console.log()`メソッドでこの識別子を参照すると、Chromeのコンソールにはつぎのように出力されました。要素がふたつとも含まれているようです。

> HTMLCollection(2) [div#element, form, element: div#element]

試しに以下のJavaScriptコードを試すと、ふたつの要素にテキストが加えられました(ECMAScript 2015の新しい`Array.from()`メソッドの使い方については「[ECMAScript 6のArrayに関わる構文を試す](https://qiita.com/FumioNonaka/items/4ff7b392bbd8232a00ba#arrayfrom%E3%83%A1%E3%82%BD%E3%83%83%E3%83%89)」をお読みください)。

> DIVが参照できた!!
> FORMが参照できた!!

```javascript
Array.from(element).forEach((element) =>
    element.textContent = element.nodeName + 'が参照できた!!');
```

なお、筆者のmacOS環境では、Safariは同じ結果を示したものの、Firefoxでは`id`属性の備わる要素のみが参照されました。HTML 5.2仕様§6.3.3.にも注意されているとおり、属性値を直に参照するのは危ないので、お勧めできないということでしょう。

参考:「[Why is document.getElementById not needed?](https://stackoverflow.com/questions/25931810/why-is-document-getelementbyid-not-needed)」

[追記: 2018年5月9日]「別要素のname属性に同名の値を与えてみる」の項とHTML 5.2仕様§6.3.3.邦訳を追加。