Идентификаторы именованных элементов могут использоваться как глобальные данные JavaScript PlatoBlockchain. Вертикальный поиск. Ай.

На идентификаторы именованных элементов можно ссылаться как на глобальные переменные JavaScript

Знаете ли вы, что элементы DOM с идентификаторами доступны в JavaScript как глобальные переменные? Это одна из тех вещей, которые существовали всегда, но я действительно вникаю в нее впервые.

Если вы впервые слышите об этом, приготовьтесь! Мы можем увидеть это в действии, просто добавив идентификатор к элементу в HTML:

Обычно мы определяем новую переменную, используя querySelector("#cool") or getElementById("cool") чтобы выбрать этот элемент:

var el = querySelector("#cool");

Но на самом деле у нас уже есть доступ к #cool без этой канитель:

Итак, любой id - или же name атрибут, если на то пошло — в HTML можно получить доступ в JavaScript, используя window[ELEMENT_ID]. Опять же, это не совсем «новинка», но это действительно редкость.

Как вы можете догадаться, доступ к глобальной области видимости с помощью именованных ссылок — не лучшая идея. Некоторые люди стали называть это «загрязнителем глобального масштаба». Мы разберемся, почему это так, но сначала…

В некотором контексте

Этот подход указано в спецификации HTML, где он описывается как «именованный доступ на Window объект."

Internet Explorer был первым, кто реализовал эту функцию. Все остальные браузеры также добавили его. В то время Gecko был единственным браузером, который не поддерживал его напрямую в стандартном режиме, вместо этого решив сделать его экспериментальной функцией. Были сомнения, внедрять ли его вообще, но это продвинулись вперед во имя совместимости браузера (Геккон даже пытался убедить вебкит чтобы вывести его из стандартного режима) и в конечном итоге перешли в стандартный режим в Firefox 14.

Одна вещь, которая может быть не очень хорошо известна, заключается в том, что браузеры должны были принять несколько мер предосторожности — с разной степенью успеха — чтобы гарантировать, что сгенерированные глобальные переменные не нарушат работу веб-страницы. Одной из таких мер является…

Переменное затенение

Вероятно, самая интересная часть этой функции заключается в том, что ссылки на именованные элементы не затенить существующие глобальные переменные. Итак, если элемент 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 возвращает только первый экземпляр. Затем снова, в спецификации говорится мы должны использовать один экземпляр id в любом случае в дереве элементов. Но это не остановит работу страницы или что-то в этом роде.

  • Может быть, есть стоимость производительности? Я имею в виду, что браузер должен составить этот список ссылок и поддерживать его. Пара человек провела тесты в этой ветке StackOverflow, где на самом деле были именованные глобальные переменные более производительный в одном тесте и менее производительный в более позднем тесте.

Дополнительные соображения

Допустим, мы отбросим критику против использования именованных глобальных переменных и все равно используем их. Все хорошо. Но есть некоторые вещи, которые вы, возможно, захотите рассмотреть, как вы это делаете.

Полифиллы

Как бы банально это ни звучало, эти типы глобальных проверок являются типичным требованием настройки для полифиллов. Посмотрите следующий пример, где мы устанавливаем файл 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 идентификатор элемента создает глобальную переменную, которая конфликтует с cookieStore глобальный характер.

Обновления JavaScript API

Мы можем изменить ситуацию и найти еще одну проблему, когда обновления движка JavaScript браузера могут нарушить глобальные ссылки именованного элемента.

Например:


  
  
    window.BarcodeDetector.focus();
  

Этот скрипт получает ссылку на элемент ввода и вызывает focus() в теме. Это работает правильно. Тем не менее, мы не знаем, как длинной он будет продолжать работать.

Видите ли, глобальная переменная, которую мы используем для ссылки на элемент ввода, перестанет работать, как только браузеры начнут поддерживать BarcodeDetector API. В этот момент window.BarcodeDetector global больше не будет ссылкой на элемент ввода и .focus() бросит "window.BarcodeDetector.focus не является функцией».

Бонус: не все именованные элементы генерируют глобальные ссылки

Хотите услышать что-нибудь смешное? Чтобы усугубить ситуацию, именованные элементы доступны как глобальные переменные только в том случае, если имена не содержат ничего, кроме буквы. Браузеры не будут создавать глобальную ссылку для элемента с идентификатором, который содержит специальные символы и числа, например hello-world и item1.

Заключение

Подытожим, как мы сюда попали:

  • Все основные браузеры автоматически создают глобальные ссылки на каждый элемент DOM с id (или, в некоторых случаях, name атрибут).
  • Доступ к этим элементам через их глобальные ссылки ненадежен и потенциально опасен. Использовать querySelector or getElementById .
  • Поскольку глобальные ссылки генерируются автоматически, они могут иметь некоторые побочные эффекты в вашем коде. Это хорошая причина, чтобы не использовать id атрибут, если он вам действительно не нужен.

В конце концов, вероятно, лучше избегать использования именованных глобальных переменных в JavaScript. Ранее я цитировал спецификацию о том, как это приводит к «ломкому» коду, но вот полный текст, чтобы донести мысль:

Как правило, полагаясь на это, вы получаете ненадежный код. Какие идентификаторы в конечном итоге сопоставляются с этим API, могут меняться со временем, например, по мере добавления новых функций в веб-платформу. Вместо этого используйте document.getElementById() or document.querySelector().

Я думаю, что тот факт, что сама спецификация HTML рекомендует держаться подальше от этой функции, говорит сам за себя.

Отметка времени:

Больше от CSS хитрости