7 maneiras de selecionar elementos com Javascript sem querySelectorAll, querySelector e getElementById
Tags: queryselector, queryselectorall, javascript, getelementbyid
Published: 2023-01-27 01:08:44

## Além dos já conhecidos querySelectorAll, querySelector e getElementById, existem essas 7 alternativas para você selecionar elementos na tela usando Javascript

---
 
## Shortcut
 
Use os atributos `document.head`, `document.body`, `document.links`, `document.images`, `document.forms` e `document.scripts`.
 
Eles lhe dão acesso direto a alguns elementos.

---
 
Ahhh, manipular a DOM, uma das magias do Javascript...
 
Nessa situação, é muito comum você usar os métodos `querySelector`, `querySelectorAll` e `getElementById`.
 
Eles são como varas de pesca, que selecionam elementos na tela. E a isca é o argumento que você informa, isso pode ser um ID, classe, atributo ou um seletor excêntrico do CSS.
 
É importante lembrar que essas alternativas não são milagrosas. Elas não substituem totalmente esses métodos.
 
Vou falar como eles funcionam, outros métodos conhecidos, e também alternativas a eles.
 
Mas antes, tenho uma pergunta:
 
## Por que usar alternativas ao querySelectorAll, querySelector e getElementById?
 
Performance!
 
Esse é o principal motivo.
 
Antes de explicar isso, é importante entender como esses 3 métodos funcionam. Todos eles recebem uma string como argumento e retornam um nodelist (não uma array) com os elementos (ou um elemento único) que atendem aos requisitos.
 
### querySelectorAll
 
Use esse método para pegar vários elementos da DOM.
 
Ele recebe uma string em formato de seletor CSS e retorna uma nodelist com esses elementos.
 
A linha `document.querySelectorAll('.product-image')` busca por todos os elementos com a classe `product-image`.
 
Logo, quanto maior a DOM, maior o tempo de execução de cada uso desse método.
 
Como curiosidade, na data de hoje (14/01/2023), a página inicial do g1.globo.com tem 4.672 itens na DOM.
 
Se você executasse 3 vezes um `document.querySelectorAll`, haveria mais de 10 mil verificações de elementos.
 
![Amostra da quantidade de elementos HTML na home do G1: 4672](https://www.datocms-assets.com/64896/1673702173-nodelist_g1.png)
 
### querySelector
 
Use esse método para pegar um elemento da DOM.
 
Esse é bem parecido com o anterior, com uma única diferença: ele retorna apenas o primeiro elemento que atende aos requisitos.
 
Ao encontrá-lo, esse método pára a sua execução. Assim, ele pode ser mais rápido do que o `querySelectorAll`. Tudo depende se esse elemento está no início ou no final da DOM.
 
### getElementById
 
Igual ao anterior, porém seleciona elementos apenas pelo ID.
 
Dos 3 métodos analisados, esse é o mais rápido, conforme você pode acompanhar nos links no final desse post.
 
Uma curiosidade é que todos os elementos com um ID são acessíveis através do objeto global `window`:
 
![mostrar como todo elemento html com id é acessível pelo objeto global window](https://www.datocms-assets.com/64896/1673704342-id.png)
 
Talvez por esse motivo o `getElementById` seja mais rápido. Pois em vez de escanear toda a DOM, ele pega os elementos direto desse objeto.
 
## Alternativas ao querySelectorAll, querySelector e getElementById
 
### A cabeça da página: document.head
 
Essa propriedade retorna o elemento `<head>` da sua página.
 
Esse elemento agrupa as metatags como:
 
- `<meta>`
- `<link>`
- `<script>`
- `<title>`
- `<style>`
 
Caso você precise buscar alguma delas, pode usar `document.head.querySeletor('meta')` por exemplo.
 
Mas por que não usar diretamente `document.querySeletor('meta')`?
 
Mais abaixo eu falarei sobre isso.
 
### O corpo da página: document.body
 
Essa propriedade retorna o elemento `<body>` da sua página.
 
Um caso de uso muito comum é bloquear o scroll do site quando um modal é aberto. Nessa situação, o `<body>` precisa da propriedade CSS `overflow:hidden`:
 
```javascript
function openModal() {
  document.body.style.overflow = 'hidden';
  // other expressions to open modal
  // ...
}

function closeModal() {
  document.body.style.overflow = '';
  // other expressions to close modal
  // ...
}
```
 
### Os links da página: document.links
 
Essa propriedade retorna um HTML collection com todas as tags `<a>` da página.
 
Muito sites configuram os links que apontam para outros domínios para abrirem em uma nova aba. Isso é feito no HTML através do atributo `target="_blank"`.
 
Se por algum motivo, você só pode fazer isso com Javascript, use o `document.links` para isso:
 
```javascript
const allLinks = Array.from(document.links);

/*
O Array.from() serve para transformar
um HTML collection em uma array.

Assim você pode usar métodos como:

- forEach
- map
- filter
- reduce
- etc
*/

allLinks.forEach(link => {
  if (!link.href.includes('mydomain')) {
    link.target = '_blank';
    // Ou
    link.setAttribute('target', '_blank');
  }
});
```
 
### As imagens da página: document.images
 
Essa propriedade retorna um HTML collection com todas as tags `<img>` da página.
 
Um caso de uso muito comum é quando você precisa aplicar o efeito de lazyload nas imagens.
 
Entenda [o que é lazyload](https://demenezes.dev/posts/atributo-loading-lazy/#o-que-e-lazyload?) e [como criar esse efeito compatível com todos os navegadores](https://demenezes.dev/posts/como-fazer-lazyload/).
 
Spoiler: é ótimo para performance, SEO e consumo de banda de banda de internet.
 
Assim, o acesso a essas imagens fica mais rápido:
 
```javascript
function setLazyload(imagesList) {
  // Essa função recebe a lista de
  // imagens para aplicar o lazy load
}

const allImages = document.images;

setLazyload(allImages); // Execução da função
```
 
Pode ser que você não precise aplicar o lazyload em todas as imagens. E sim apenas naquelas que, por exemplo, possuem a classe `.lazy`.
 
Nesse caso, é preciso filtrar a lista de imagens antes de executar a função:
 
```javascript
function setLazyload(imagesList) {
  // Essa função recebe a lista de
  // imagens para aplicar o lazy load
}

const allImages = Array.from(document.images);

const imagesToLazyload = allImages.filter(
 image => image.classList.contains('lazy')
);

setLazyload(imagesToLazyload); // Execução da função
```
 
### Os formulários da página: document.forms
 
Essa propriedade retorna um HTML collection com todas as tags `<form>` da página.
 
Se você tiver apenas um formulário, pode chegar até ele assim `document.forms[0]`.
 
Caso tenha mais de um, é arriscado usar números maiores que zero, exemplo `document.forms[3]`. Isso porque se você adicionar um formulário na DOM, o índice de todos eles mudarão nessa lista e podem parar de funcionar.
 
Você pode usá-lo quando quiser prevenir o comportamento default dos formulários que é recarregar a página:
 
```javascript
// Prevenir o comportamento default de todos os forms

Array.from(document.forms).forEach(form => {
  form.addEventListener('submit', event => {
    event.preventDefault();
  });
});

// Depois é só aplicar os outros eventos
// de forma individual normalmente

document.forms[0].addEventListener(
  'submit', () => alert('form 1')
);

document.forms[1].addEventListener(
  'submit', () => alert('form 2')
);
```
 
### Os scripts da página: document.scripts
 
Essa propriedade retorna um HTML collection com todas as tags `<script>` da página.
 
Imagine que você precise inserir uma tag `<script>` em uma página, mas só possa fazer isso com Javascript. Muitas libs, por questão de organização, inserem antes da primeira ou após a última tag `<script>`.
 
Primeiro, crie a tag:
 
```javascript
const scriptTag = document.createElement('script');
scriptTag.type = "text/javascript";
scriptTag.async = 'async';
scriptTag.src = 'https://www.example.com/my-script/'

// A tag script ficará assim:
// 
// <script 
//   type="text/javascript"
//   async="async"
//   src="https://www.example.com/my-script/"
// ></script>
```
 
Agora, insira ela na DOM:
 
```javascript
// Inserir antes da primeira tag <script> da página
document.scripts[0].insertAdjacentHTML(
  'beforebegin', scriptTag.outerHTML
);

// Inserir depois da última tag <script> da página
Array.from(document.scripts)
  .at(-1) // busca a última tag script da lista
  .insertAdjacentHTML('afterend', scriptTag.outerHTML); // insere após ela
```
 
### [EXTRA] O título da página: document.title
 
Essa propriedade retorna o conteúdo em formato de texto que está dentro da tag `<title>`.
 
De todos apresentados, é o único que retorna um texto puro, e não um elemento ou lista de elementos HTML.
 
## Limitações dessas alternativas
 
Nem tudo são flores.
 
Essas alternativas que mostrei servem apenas para quando você precisa selecionar e mexer em vários elementos. Com exceção, é claro, do `document.head`, `document.body` e `document.title`.
 
Por isso, dei exemplos onde você altera todas as imagens, links e formulários.
 
Caso precise mexer em um elemento específico, prefira usar algum dos métodos `querySelectorAll`, `querySelector` e `getElementById`. Caso contrário, dentro do HTML collection de retorno, precisará saber a posição do elemento desejado.
 
Se essas alternativas não forem o suficiente para o seu caso, está tudo bem.
 
Existe uma forma de tornar os métodos `querySelectorAll` e `querySelector` mais performáticos.
 
## Como melhorar a performance dos métodos querySelectorAll e querySelector
 
Você deve estar muito acostumado a usar esse método junto com o objeto global `document`:
 
```javascript
document.querySelectorAll('.alguma-coisa');

// Ou

document.querySelector('.alguma-coisa');
```
 
A técnica que uso é bem simples:
 
**Faça apenas uma busca na DOM inteira. Isso não significa usar apenas uma vez esses métodos. Você fará a primeira busca por um elemento que envolve todos os outros necessários. Depois disso, as próximas buscas irão iniciar nesse elemento, e não em document.**
 
Por exemplo, a div que envolve todos elementos de um modal possui a classe `modal-wrapper`:
 
```javascript
const modalWrapper = document.querySelector('.modal-wrapper');
```
 
Agora, a próxima busca irá iniciar nesse elemento, que foi colocado na constante `modalWrapper`:
 
```javascript
// Busca iniciando em document
const modalWrapper = document.querySelector('.modal-wrapper');

// Busca iniciando modalWrapper
const closeModalButton = modalWrapper.querySelector('.close-modal-button');
const modalTitle = modalWrapper.querySelector('.modal-title');

// ...
```
 
A partir do segundo `querySelector`, a busca não ocorre mais a partir do elemento raiz da DOM. Ele irá acontecer apenas dentro da seção capturada no primeiro `querySelector`.
 
**Importante**: essa técnica irá funcionar apenas com `querySelectorAll` e `querySelector`.
 
Use o método `getElementById` somente junto com `document`.
 
## E outros métodos como getElementsByTagName, getElementsByName e getElementsByClassName?
 
Eles possuem comportamentos muito parecidos.
 
Assim como os métodos que já expliquei, todos aqui recebem uma string como argumento. Porém eles retornam um HTML collection (e não um nodelist) com os elementos que atendem aos requisitos:
 
- `getElementsByTagName`: recebe o nome de uma tag HTML e retorna os elementos com essa tag
- `getElementsByClassName`: recebe uma classe e retorna os elementos com essa classe
- `getElementsByName`: recebe um valor e retorna os elementos que possuem esse valor em seu atributo `name`
 
Confesso que são métodos pouco usados na prática.
 
**A grande diferença é que eles retornam um HTML collection, e o `querySelectorAll` retorna um nodelist. Você pode iterar em um nodelist com o método `forEach()`, o que não é possível fazer num HTML collection. A não ser que você converta para uma array com o método `Array.from()`.**
 
Além disso, o `querySelectorAll` permite usar seletores mais complexos, como esse:
 
```javascript
const links = document.querySelectorAll(
  '.product-cart > img[src*="png"]'
);
```
 
## Callback
 
Os métodos `querySelectorAll`, `querySelector` e `getElementById` são indispensáveis para selecionar elementos e manipular a DOM.
 
Porém tente não abusar do uso deles.
 
Use atributos como:
 
- `document.head`
- `document.body`
- `document.links`
- `document.images`
- `document.forms`
- `document.scripts`
 
Eles ajudarão no momento de selecionar elementos na tela.
 
Busque elementos na DOM inteira apenas uma vez, para otimizar as buscas.
 
Lembre-se que essas alternativas possuem limitações.
 
Por outro lado, se quiser quebrar todas essas regras, fique à vontade. E torne-se [o pior front-end da empresa](https://demenezes.dev/posts/o-pior-front-end-da-empresa/) 😄
 
E aí, já conhecia elas?
 
Comenta aí!
 
---
 
Continue lendo:
 
- [https://stackoverflow.com/questions/15046356/performance-using-js-queryselector](https://stackoverflow.com/questions/15046356/performance-using-js-queryselector)
- [https://pt.stackoverflow.com/questions/339172/quando-utilizar-queryselector-ao-inv%C3%A9s-de-getelementbyid](https://pt.stackoverflow.com/questions/339172/quando-utilizar-queryselector-ao-inv%C3%A9s-de-getelementbyid)
- [https://unicorntears.dev/posts/queryselectorall-vs-getelementsbyclassname/](https://unicorntears.dev/posts/queryselectorall-vs-getelementsbyclassname/)