IDs de elementos nomeados podem ser referenciados como JavaScript Globals PlatoBlockchain Data Intelligence. Pesquisa vertical. Ai.

IDs de elementos nomeados podem ser referenciados como JavaScript Globals

Você sabia que os elementos DOM com IDs são acessíveis em JavaScript como variáveis ​​globais? É uma daquelas coisas que existe desde sempre, mas estou realmente investigando isso pela primeira vez.

Se esta é a primeira vez que você ouve falar sobre isso, prepare-se! Podemos ver isso em ação simplesmente adicionando um ID a um elemento em HTML:

Normalmente, definiríamos uma nova variável usando querySelector("#cool") or getElementById("cool") para selecionar esse elemento:

var el = querySelector("#cool");

Mas na verdade já temos acesso a #cool sem aquele rigamorale:

Então, qualquer id - ou name atributo, aliás - no HTML pode ser acessado em JavaScript usando window[ELEMENT_ID]. Novamente, isso não é exatamente “novo”, mas é realmente incomum de ver.

Como você pode imaginar, acessar o escopo global com referências nomeadas não é uma boa ideia. Algumas pessoas passaram a chamar isso de “poluidor de âmbito global”. Veremos por que isso acontece, mas primeiro…

Algum contexto

Essa abordagem é descrito na especificação HTML, onde é descrito como “acesso nomeado no Window objeto."

O Internet Explorer foi o primeiro a implementar o recurso. Todos os outros navegadores também o adicionaram. Gecko era o único navegador na época que não o suportava diretamente no modo padrão, optando por torná-lo um recurso experimental. Houve hesitação em implementá-lo, mas avançou em nome da compatibilidade do navegador (Gecko até tentou convencer o WebKit para movê-lo para fora do modo padrão) e finalmente chegou ao modo padrão no Firefox 14.

Uma coisa que pode não ser bem conhecida é que os navegadores tiveram que implementar algumas medidas de precaução – com graus variados de sucesso – para garantir que os globais gerados não quebrassem a página da web. Uma dessas medidas é…

Sombreamento variável

Provavelmente a parte mais interessante deste recurso é que as referências de elementos nomeados não sombrear variáveis ​​globais existentes. Então, se um elemento DOM tiver um id que já está definido como global, não substituirá o existente. Por exemplo:


  
    window.foo = "bar";
  


  
I won't override window.foo
console.log(window.foo); // Prints "bar"

E o oposto também é verdadeiro:

I will be overridden :(
window.foo = "bar"; console.log(window.foo); // Prints "bar"

Este comportamento é essencial porque anula substituições perigosas, como

, o que de outra forma criaria um conflito ao invalidar o alert API. Essa técnica de proteção pode muito bem ser a razão pela qual você – se você é como eu – está aprendendo sobre isso pela primeira vez.

O caso contra globais nomeados

Anteriormente, eu disse que usar elementos nomeados globais como referências pode não ser a melhor ideia. Existem muitas razões para isso, que TJ VanToll cobriu muito bem em seu blog e vou resumir aqui:

  • Se o DOM mudar, a referência também mudará. Isso torna alguns realmente “frágeis” (o termo da especificação para isso) código onde a separação de interesses entre HTML e JavaScript pode ser demais.
  • Referências acidentais são fáceis demais. Um simples erro de digitação pode muito bem acabar fazendo referência a um nome global e gerar resultados inesperados.
  • Ele é implementado de forma diferente nos navegadores. Por exemplo, deveríamos ser capazes de acessar uma âncora com um id - por exemplo - mas alguns navegadores (nomeadamente Safari e Firefox) retornam um ReferenceError no console.
  • Pode não retornar o que você pensa. De acordo com a especificação, quando há múltiplas instâncias do mesmo elemento nomeado no DOM — digamos, duas instâncias de

    — o navegador deve retornar um HTMLCollection com uma matriz de instâncias. O Firefox, entretanto, retorna apenas a primeira instância. Então de novo, a especificação diz devemos usar uma instância de um id na árvore de um elemento de qualquer maneira. Mas fazer isso não impedirá o funcionamento de uma página ou algo parecido.

  • Talvez haja um custo de desempenho? Quero dizer, o navegador precisa fazer essa lista de referências e mantê-la. Algumas pessoas fizeram testes neste tópico StackOverflow, onde os globais nomeados eram na verdade mais desempenho em um teste e menos desempenho em um teste mais recente.

Considerações adicionais

Digamos que descartamos as críticas contra o uso de globais nomeados e os usamos de qualquer maneira. É tudo de bom. Mas há algumas coisas que você pode querer considerar ao fazer isso.

Polifills

Por mais extremo que possa parecer, esses tipos de verificações globais são um requisito típico de configuração para polyfills. Confira o exemplo a seguir, onde definimos um cookie usando o novo CookieStore API, preenchendo-o em navegadores que ainda não o suportam:


  
  
    // 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");
  

Este código funciona perfeitamente no Chrome, mas gera o seguinte erro no Safari:

TypeError: cookieStore.set is not a function

O Safari não tem suporte para o CookieStore API no momento desta redação. Como resultado, o polyfill não é aplicado porque o img ID do elemento cria uma variável global que entra em conflito com o cookieStore global.

Atualizações da API JavaScript

Podemos inverter a situação e encontrar outro problema em que as atualizações no mecanismo JavaScript do navegador podem quebrar as referências globais de um elemento nomeado.

Por exemplo:


  
  
    window.BarcodeDetector.focus();
  

Esse script pega uma referência ao elemento input e invoca focus() nele. Funciona corretamente. Ainda assim, não sabemos como longo ele continuará funcionando.

Veja, a variável global que estamos usando para referenciar o elemento input irá parar de funcionar assim que os navegadores começarem a suportar o BarcodeDetector API. Nesse ponto, o window.BarcodeDetector global não será mais uma referência ao elemento de entrada e .focus() vai lançar um “window.BarcodeDetector.focus não é um erro de função”.

Bônus: nem todos os elementos nomeados geram referências globais

Queres ouvir uma coisa engraçada? Para piorar ainda mais a situação, os elementos nomeados são acessíveis como variáveis ​​globais apenas se os nomes contiverem apenas letras. Os navegadores não criarão uma referência global para um elemento com um ID que contenha caracteres especiais e números, como hello-world e item1.

Conclusão

Vamos resumir como chegamos aqui:

  • Todos os principais navegadores criam automaticamente referências globais para cada elemento DOM com um id (ou, em alguns casos, um name atributo).
  • Acessar esses elementos através de suas referências globais não é confiável e é potencialmente perigoso. Usar querySelector or getElementById ao invés.
  • Como as referências globais são geradas automaticamente, elas podem ter alguns efeitos colaterais no seu código. Essa é uma boa razão para evitar usar o id atributo, a menos que você realmente precise dele.

No final das contas, provavelmente é uma boa ideia evitar o uso de globais nomeados em JavaScript. Eu citei a especificação anteriormente sobre como isso leva a um código “frágil”, mas aqui está o texto completo para deixar claro:

Como regra geral, confiar nisso levará a um código frágil. Quais IDs acabam mapeados para esta API podem variar ao longo do tempo, à medida que novos recursos são adicionados à plataforma web, por exemplo. Em vez disso, use document.getElementById() or document.querySelector().

Acho que o fato de a própria especificação HTML recomendar ficar longe desse recurso fala por si.

Carimbo de hora:

Mais de Truques CSS