ID を持つ DOM 要素は、JavaScript でグローバル変数としてアクセスできることをご存知ですか? ずっと前からあるものの XNUMX つですが、実際に掘り下げたのは初めてです。
これが初めての場合は、気を引き締めてください。 HTML の要素に ID を追加するだけで、実際の動作を確認できます。
通常、次を使用して新しい変数を定義します querySelector("#cool")
or getElementById("cool")
その要素を選択するには:
var el = querySelector("#cool");
しかし、実際にはすでにアクセスできます #cool
その厳格なモラルなしで:
だから、 id
—または name
属性、さらに言えば — HTML では、JavaScript を使用してアクセスできます。 window[ELEMENT_ID]
. 繰り返しますが、これは正確には「新しい」ものではありませんが、実際に目にすることはめったにありません。
ご想像のとおり、名前付き参照を使用してグローバル スコープにアクセスすることは、最善のアイデアではありません。 これを「地球規模の汚染者」と呼ぶ人もいます。 その理由について説明しますが、まず…
コンテキスト
このアプローチは HTML 仕様で概説されている、「上の名前付きアクセス」として説明されています Window
物体。"
この機能を最初に実装したのは Internet Explorer でした。 他のすべてのブラウザも同様に追加しました。 Gecko は、標準モードで直接サポートしていない当時唯一のブラウザであり、代わりに実験的な機能にすることを選択しました。 導入には全く躊躇がありましたが、 ブラウザの互換性という名目で前進 (Gecko はしようとさえしました WebKitを説得する 標準モードから移動するため)、最終的に Firefox 14 で標準モードになりました。
あまり知られていないかもしれないことの XNUMX つは、生成されたグローバルが Web ページを壊さないようにするために、ブラウザーがいくつかの予防措置を講じる必要があったことです (成功の程度はさまざまです)。 その対策の一つが…
可変シャドウイング
おそらく、この機能の最も興味深い部分は、名前付き要素の参照がそうでないことです。 既存のグローバル変数をシャドウする. したがって、DOM 要素に id
すでにグローバルとして定義されている場合、既存のものをオーバーライドしません。 例えば:
window.foo = "bar";
I won't override window.foo
console.log(window.foo); // Prints "bar"
また、その逆も同様です。
I will be overridden :(
window.foo = "bar";
console.log(window.foo); // Prints "bar"
次のような危険なオーバーライドを無効にするため、この動作は不可欠です。
を無効にすることで競合が発生します。 alert
API。 この保護技術は、あなたが私のような人なら、これについて初めて学ぶ理由である可能性が非常に高い.
名前付きグローバルに対するケース
先ほど、グローバルな名前付き要素を参照として使用することは最善のアイデアではないかもしれないと述べました。 それにはたくさんの理由がありますが、 TJ VanToll は彼のブログでうまくカバーしています ここで要約します。
- DOM が変更されると、参照も変更されます。 それは本当に「もろい」ものを作ります(仕様の用語 そのために) HTML と JavaScript の間の関心の分離が多すぎるかもしれないコード。
- 偶発的な参照はあまりにも簡単です。 単純なタイプミスが、名前付きグローバルを参照することになり、予期しない結果をもたらす可能性があります。
- ブラウザでは実装方法が異なります。 たとえば、アンカーにアクセスできる必要があります
id
—例— ただし、一部のブラウザー (つまり、Safari と Firefox) は
ReferenceError
コンソールで。 - あなたの思い通りにならないかもしれません。 仕様によると、DOM に同じ名前の要素のインスタンスが複数ある場合、たとえば、
— ブラウザは
HTMLCollection
インスタンスの配列で。 ただし、Firefox は最初のインスタンスのみを返します。 また、 スペックによると の XNUMX つのインスタンスを使用する必要があります。id
とにかく要素のツリーで。 しかし、そうすることで、ページの動作やそのようなものが停止することはありません。 - たぶん、パフォーマンスコストがありますか? つまり、ブラウザはその参照リストを作成して維持する必要があります。 何人かの人々がテストを実行しました この StackOverflow スレッドで、名前付きグローバルが実際にあった場所 XNUMX 回のテストでより高いパフォーマンスを発揮 および 最近のテストではパフォーマンスが低下.
その他の考慮事項
名前付きグローバルの使用に対する批判を片付けて、とにかくそれらを使用するとしましょう。 大丈夫だよー。 しかし、あなたがそうするように考慮したいことがいくつかあります。
ポリフィル
エッジケースのように聞こえるかもしれませんが、これらのタイプのグローバル チェックは、ポリフィルの一般的なセットアップ要件です。 new を使用して Cookie を設定する次の例を確認してください。 CookieStore
API、まだサポートしていないブラウザーでポリフィルします。
// Polyfill the CookieStore API if not yet implemented.
// https://developer.mozilla.org/en-US/docs/Web/API/CookieStore
if (!window.cookieStore) {
window.cookieStore = myCookieStorePolyfill;
}
cookieStore.set("foo", "bar");
このコードは Chrome では問題なく動作しますが、Safari では次のエラーがスローされます。
TypeError: cookieStore.set is not a function
Safari は、 CookieStore
この記事の執筆時点での API。 その結果、ポリフィルは適用されません。 img
要素 ID は、 cookieStore
グローバル
JavaScript API の更新
状況をひっくり返して、ブラウザーの JavaScript エンジンを更新すると、名前付き要素のグローバル参照が壊れる可能性があるという別の問題を見つけることができます。
例:
window.BarcodeDetector.focus();
そのスクリプトは入力要素への参照を取得し、呼び出します focus()
その上で。 正しく動作します。 それでも、方法はわかりません 長い それは働き続けます。
入力要素を参照するために使用しているグローバル変数は、ブラウザがサポートを開始するとすぐに機能しなくなります。 BarcodeDetector
API。 その時点で、 window.BarcodeDetector
global は入力要素への参照ではなくなり、 .focus()
をスローしますwindow.BarcodeDetector.focus
は関数ではありません」エラー。
おまけ: すべての名前付き要素がグローバル参照を生成するわけではありません
面白いことを聞きたいですか? さらにひどいことに、名前に文字しか含まれていない場合にのみ、名前付き要素にグローバル変数としてアクセスできます。 ブラウザは、特殊文字と数字を含む ID を持つ要素のグローバル参照を作成しません。 hello-world
および item1
.
まとめ
ここにたどり着いた方法を要約しましょう。
- すべての主要なブラウザーは、各 DOM 要素へのグローバル参照を自動的に作成します。
id
(または、場合によっては、name
属性)。 - グローバル参照を介してこれらの要素にアクセスすることは、信頼性が低く、潜在的に危険です。 使用する
querySelector
orgetElementById
を代わりにお使いください。 - グローバル参照は自動的に生成されるため、コードに副作用が生じる場合があります。 これは、
id
あなたが本当にそれを必要としない限り、属性。
結局のところ、JavaScript で名前付きグローバルを使用しないことはおそらく良い考えです。 それがどのように「壊れやすい」コードにつながるかについて、以前に仕様を引用しましたが、ここに要点を強調するための全文があります。
原則として、これに依存するとコードが脆弱になります。 たとえば、Web プラットフォームに新しい機能が追加されると、どの ID が最終的にこの API にマッピングされるかが変わる可能性があります。 これの代わりに、
document.getElementById()
ordocument.querySelector()
.
HTML 仕様自体が、この機能を避けることを推奨しているという事実は、それ自体が物語っていると思います。