JavaScript で getElementById を使わなくても id 属性で要素を参照できてしまう件について
こんにちは、じゅんじゅんです。先日、社内の勉強会で JavaScript の DOM について発表を行いました。社内での発表とはいえとても緊張しました(笑)。説明やスライドなど課題がたくさん見つかったので、回数をこなして上達したいと思います。
DOM 操作についての練習問題を作成しているとき、書き間違いから偶然 getElementById
で取得をしていないにも関わらず id 属性で要素を参照できてしまうことに気づきました。今回はなぜこのようなことが起こるのかについてお話しします。
実際のコード
実際のコードがこちらです。
<div id="element"></div>
<script>
element.textContent = 'こんにちは、じゅんじゅんです';
</script>
これだけでしっかり「こんにちは、じゅんじゅんです」というテキストが表示されます。 getElementById
などのメソッドを使わず、直接 id 属性から textContent
プロパティを変更できてしまいました。
この現象を説明するためには window オブジェクトについてお話ししておく必要があります。
window オブジェクトについて
ブラウザ上の情報は全てオブジェクトとして扱うことができ、総称してブラウザオブジェクトと呼びます。全てのブラウザオブジェクトの親となるものが window オブジェクトです。言わばブラウザそのものです。console.log(window)
とするか、デベロッパーツールの Console で window
と打つことで以下のように確認できます。
JavaScript に元々入っているメソッド・プロパティがずらっと並んでいます。よく使う alert
メソッドや document
プロパティ (これが window
に読み込まれたものが document
オブジェクト) もありますね。
id 属性で要素が参照できる理由
以上を踏まえて、表題の件の理由を説明します。 HTML で id 属性が指定されている場合、自動で id の属性値と同名のプロパティが window オブジェクトに追加されます。そして、最初に紹介したコードで説明すると、
<script>
element.textContent = 'こんにちは、じゅんじゅんです'
</script>
の element
のように id 属性が直接指定された場合、その id 属性を持つ要素である
<div id="element"></div>
が参照されることになります。
window のプロパティやメソッドを記述する場合、本来頭につけるべき window
が省略できるので、 id 属性から直接要素を参照しているように見えたのですね。きちんと書くとこうなります。
<script>
window.element.textContent = 'こんにちは、じゅんじゅんです';
</script>
極力使わない方がいい
一見、 getElementById
などの要素を取得するメソッドを省略できるのでこちらの方が楽に見えますが、この書き方はあまりお勧めされていません。HTML Standard の §7.3.3 Named access on the Window object に、「一般的にこの書き方に依存したコードは脆弱なので、代わりに document.getElementById
やdocument.querySelector
を使ってください」という記述があります。
唐突ですが、ここでグローバル変数を宣言してみます。 let
や const
を記述しないことでグローバル変数になります。
<script>
global = 'グローバルです';
</script>
もう一度 window オブジェクトを確認してみると、今宣言した global
という変数が window オブジェクトのプロパティとして登録されています。
ブラウザ内では window オブジェクトがグローバルオブジェクトなので、実はグローバル変数、グローバル関数というものは window オブジェクトのプロパティ、メソッドのことなのです。 window プロパティとして追加された id 属性はグローバル変数になるので、名前の衝突が起こりやすくなります。例えば、以下のように先に同名の window プロパティを宣言していると、後から id 属性を設定しても window プロパティに追加されず、要素の参照ができません。
<script>
window.element = '';
</script>
<div id="element"></div>
<script>
element.textContent = 'こんにちは、じゅんじゅんです';
</script>
この場合、下の script
タグ中の element
は div
タグを参照できていないので textContent
プロパティを呼ぶことができず、画面には何も表示されません。
また、 JavaScript 上で同名の要素を定めている場合、そちらが優先して参照されます。以下では querySelector
メソッドを使用して element
という変数に代入した h1
要素にテキストが追加されています (点線の下) 。
<div id="element"></div>
<p>---------------------------</p>
<h1></h1>
<script>
const element = document.querySelector('h1');
element.textContent = 'こんにちは、じゅんじゅんです';
</script>
このように非常に不安定な状態となります。コード自体は短くはなりますがそもそも正しい動作をしなくなる可能性が高くなるので、使用するメリットは少ないと思います。きちんと getElementById
を使うようにしましょう。
感想
偶然見つけた現象でしたが、調べてみるといろいろと新しい発見がありました。引き続きアンテナを立てつついろんなところから知識を吸収していきます。