Navegando pelo DOM com o Objeto Node em JavaScript
Cada nó individual, seja um elemento HTML, seu atributo ou texto, é representado na estrutura do DOM por um objeto Node. Pode surgir a pergunta: como os elementos de uma página web estão relacionados aos nós dessa mesma página? É importante notar que todo elemento de uma página web é um nó, mas nem todo nó é um elemento; por exemplo, os atributos e o texto dos elementos também são nós separados.
O objeto Node oferece uma série de propriedades que nos permitem obter informações sobre o nó em questão:
- childNodes: contém uma coleção de nós filhos.
- children: contém uma coleção de nós filhos que são elementos.
- firstChild: retorna o primeiro nó filho do nó atual.
- firstElementChild: retorna o primeiro nó filho que é um elemento.
- lastChild: retorna o último nó filho do nó atual.
- lastElementChild: retorna o último nó filho que é um elemento.
- previousSibling: retorna o nó anterior que está no mesmo nível que o atual.
- nextSibling: retorna o próximo nó que está no mesmo nível que o atual.
- previousElementSibling: retorna o nó anterior que é um elemento e que está no mesmo nível que o atual.
- nextElementSibling: retorna o próximo nó que é um elemento e que está no mesmo nível que o atual.
- ownerDocument: retorna o nó raiz do documento.
- parentNode: retorna o nó pai do nó atual.
- parentElement: retorna o nó pai que é um elemento.
- nodeName: retorna o nome do nó.
- nodeType: retorna o tipo de nó em forma de número.
- nodeValue: retorna o texto do nó de texto.
Primeiramente, podemos usar as propriedades nodeName e nodeType para identificar o tipo de nó:
<!DOCTYPE html>
<html>
  <head>
    <meta charset="utf-8" />
    <title>Programício</title>
  </head>
  <body>
    <div id="article">
      <h1 id="header">Home Page</h1>
      <p>Page Text</p>
    </div>
    <script>
      const article = document.getElementById("article");
      console.log(article.nodeName); // DIV
      console.log(article.nodeType); // 1
    </script>
  </body>
</html>Aqui, obtemos informações sobre o elemento com o id="header". Em particular, a propriedade nodeName retorna o nome da tag do elemento que é div, e a propriedade nodeType o número 1, que corresponde a um elemento. Cada tipo de nó é associado a um número específico:
- nodeType 1: Elemento
- nodeType 2: Atributo
- nodeType 3: Texto
Obtendo o Elemento Pai
Para obter o elemento pai, utilizamos as propriedades parentNode e parentElement. Por exemplo:
<!DOCTYPE html>
<html>
  <head>
    <meta charset="utf-8" />
    <title>Programício</title>
  </head>
  <body>
    <div id="article">
      <h1 id="header">Home Page</h1>
      <p>Page Text</p>
    </div>
    <script>
      // Selecionando o elemento com id="header"
      const header = document.getElementById("header");
      // Obtendo o elemento pai
      const headerParent = header?.parentElement;
      // Também pode ser assim
      // const headerParent = header?.parentNode;
      console.log(headerParent); // Exibe o elemento pai no console
    </script>
  </body>
</html>Neste exemplo, exibimos no console o elemento que contém o elemento com id="header".
É importante notar que, embora ambos os métodos geralmente retornem o mesmo elemento, há uma exceção: o elemento <html>. Para ele, o nó pai será o objeto document, mas ele não terá um elemento pai (será null):
const htmlEl = document.getElementsByTagName("html")[0];
const parentElem = htmlEl.parentElement;
const parentNode = htmlEl.parentNode;
console.log(parentElem); // null
console.log(parentNode); // objeto documentObtendo Nós Filhos
O método hasChildNodes() retorna true se o elemento contém nós filhos:
const article = document.querySelector("div");
if (article.hasChildNodes()) {
  console.log("There are child nodes");
} else {
  console.log("No child nodes");
}Para obter elementos filhos, podemos usar a propriedade children:
<!DOCTYPE html>
<html>
  <head>
    <meta charset="utf-8" />
    <title>Programício</title>
  </head>
  <body>
    <div id="article">
      <h1 id="header">Home Page</h1>
      <p>Page Text</p>
    </div>
    <script>
      // Selecionando o elemento com id="article"
      const article = document.getElementById("article");
      for (elem of article.children) {
        console.log(elem);
      }
    </script>
  </body>
</html>Aqui, exibimos no console todos os elementos filhos do elemento com id="article". São dois elementos:
<h1 id="header">Home Page</h1> <p>Page Text</p>
Se quisermos selecionar todos os nós filhos (não apenas elementos, mas também atributos e texto), usamos o método childNodes:
<!DOCTYPE html>
<html>
  <head>
    <meta charset="utf-8" />
    <title>Programício</title>
  </head>
  <body>
    <div id="article">
      <h1 id="header">Home Page</h1>
      <p>Page Text</p>
    </div>
    <script>
      // Selecionando o elemento com id="article"
      const article = document.getElementById("article");
      for (node of article.childNodes) {
        let type = "";
        if (node.nodeType === 1) type = "elemento";
        else if (node.nodeType === 2) type = "atributo";
        else if (node.nodeType === 3) type = "texto";
        console.log(node.nodeName, ": ", type);
      }
    </script>
  </body>
</html>Aqui, selecionamos o mesmo elemento, mas agora percorremos seus nós. O console exibirá cinco nós, incluindo espaços entre elementos que também são considerados nós de texto:
#text : texto H1 : elemento #text : texto P : elemento #text : texto
É importante notar que os espaços em branco são considerados nós de texto. Se não houvesse espaços entre as tags, o console exibiria apenas três nós:
<div id="article">
  <h1 id="header">Home Page</h1>
  <p>Page Text</p>
</div>H1 : elemento P : elemento
Obtendo o Primeiro e o Último Elemento/Nó
Para obter o primeiro e o último nó/elemento, usamos as propriedades firstChild/firstElementChild e lastChild/lastElementChild, respectivamente:
<!DOCTYPE html>
<html>
  <head>
    <meta charset="utf-8" />
    <title>Programício</title>
  </head>
  <body>
    <div id="article">
      <h1 id="header">Home Page</h1>
      <p>Page Text</p>
    </div>
    <script>
      const article = document.getElementById("article");
      console.log("First Child:", article.firstElementChild);
      console.log("Last Child:", article.lastElementChild);
    </script>
  </body>
</html>Resultado no console:
First Child: <h1 id="header">Home Page</h1> Last Child: <p>Page Text</p>
Contando os Elementos Filhos
Para contar o número de elementos filhos, usamos a propriedade childElementCount: Este método é equivalente a usar a propriedade children.length.
const article = document.getElementById("article");
console.log(article.childElementCount); // 2
console.log(article.children.length); // 2Obtendo Elementos do Mesmo Nível
As propriedades previousSibling/previousElementSibling e nextSibling/nextElementSibling permitem obter os elementos anteriores e posteriores que estão no mesmo nível que o atual. Por exemplo:
<!DOCTYPE html>
<html>
  <head>
    <meta charset="utf-8" />
    <title>Programício</title>
  </head>
  <body>
    <div id="article">
      <h1 id="header">Home Page</h1>
      <p>Page Text 1</p>
      <p>Page Text 2</p>
      <p>Page Text 3</p>
    </div>
    <script>
      const article = document.getElementById("article");
      let tempNode = article.firstElementChild;
      while (tempNode != null) {
        console.log(tempNode);
        tempNode = tempNode.nextElementSibling;
      }
    </script>
  </body>
</html>Aqui, obtemos o elemento com id="article". Em seguida, capturamos seu primeiro elemento na variável tempNode e, em um loop, enquanto tempNode não for igual a null, exibimos seu valor no console e depois atribuímos a essa variável o próximo elemento do mesmo nível (elemento vizinho).
Também é possível iterar os nós em ordem inversa: primeiro obtemos o último nó e, então, acessamos o nó irmão anterior:
const article = document.getElementById("article");
let tempNode = article.lastElementChild;
while (tempNode != null) {
  console.log(tempNode);
  tempNode = tempNode.previousElementSibling;
}Obtendo o Conteúdo Textual e nodeValue
A propriedade nodeValue permite obter o conteúdo de um nó de texto, ou seja, seu texto. Por exemplo:
<!DOCTYPE html>
<html>
  <head>
    <meta charset="utf-8" />
    <title>Programício</title>
  </head>
  <body>
    <div id="article">
      <h1 id="header">Home Page</h1>
      <p id="text">Page Text</p>
    </div>
    <script>
      // Obtendo o elemento com id="text"
      const pageText = document.getElementById("text");
      console.log(pageText.nodeValue); // null
      for (textNode of pageText.childNodes) {
        console.log(textNode.nodeValue);
      }
    </script>
  </body>
</html>Neste caso, tentamos obter o texto do elemento com id="text". Primeiramente, obtemos esse elemento na constante pageText. No entanto, não podemos chamar diretamente a propriedade nodeValue nele, pois isso retornará null:
console.log(pageText.nodeValue); // nullIsso ocorre porque o elemento obtido não é um nó de texto. O nó de texto está localizado dentro do elemento pageText. E para obter o texto, precisamos acessar esse nó de texto através da coleção childNodes:
for (textNode of pageText.childNodes) {
  console.log(textNode.nodeValue);
}Embora possamos obter o conteúdo textual dos elementos desta forma, não é o método mais otimizado, e exploraremos outros métodos posteriormente.