Como Carregar e Exibir Dados HTML com XMLHttpRequest em JavaScript
Frequentemente, em um código de página, é necessário obter algum código HTML do servidor. Por exemplo, uma página pode ser um site de uma única página que carrega o código HTML necessário através de uma solicitação AJAX e o insere na página. Portanto, vamos examinar como carregar código HTML via AJAX.
Para o servidor, assim como no tema anterior, usaremos Node.js como a opção mais simples, mas, naturalmente, qualquer outra tecnologia de servidor ou servidor web pode ser usada.
Então, vamos definir uma pasta no disco rígido para o projeto, onde criaremos três arquivos:
- index.html: a página principal da aplicação
- home.html: a página com o código HTML que carregaremos via AJAX
- server.js: o arquivo da aplicação de servidor que usará Node.js
Definindo o Servidor
O arquivo server.js conterá o código do servidor Node.js. Definiremos o seguinte código:
const http = require("http");
const fs = require("fs");
http
  .createServer((request, response) => {
    // Obtendo o caminho após a barra, a barra é o primeiro caractere no caminho
    let filePath = request.url.substring(1);
    // Se o caminho estiver vazio, enviamos a página principal index.html
    if (!filePath) filePath = "index.html";
    // Definimos o tipo de resposta como HTML
    response.setHeader("Content-Type", "text/html; charset=utf-8;");
    fs.readFile(filePath, (error, data) => {
      if (error) {
        // Em caso de erro
        response.statusCode = 404;
        response.end("Recurso não encontrado!");
      } else {
        response.end(data);
      }
    });
  })
  .listen(3000, () => console.log("Servidor iniciado em http://localhost:3000"));Vamos examinar o código em detalhes. Primeiro, são importados os pacotes com funcionalidades que utilizaremos:
const http = require("http"); // Para lidar com solicitações recebidas
const fs = require("fs"); // Para ler arquivos do disco rígidoPara criar o servidor, usamos a função http.createServer(). Esta função recebe uma função de callback que é chamada toda vez que o servidor recebe uma solicitação. Esta função possui dois parâmetros: request (contém os dados da solicitação) e response (controla o envio da resposta).
Na função de callback, podemos obter o caminho do recurso solicitado através da propriedade request.url. Precisamos lidar com solicitações para as páginas "index.html" e "home.html" (e, no futuro, para outras páginas HTML). O caminho sempre começa com uma barra "/". Por exemplo, uma solicitação para a página "home.html" terá o caminho "/home.html". Portanto, para obter o caminho dos arquivos no disco rígido, precisamos remover a barra inicial:
let filePath = request.url.substring(1);No entanto, se a solicitação for para a raiz do site, o caminho consistirá apenas de uma barra - "/". Nesse caso, se removermos essa barra, teremos uma string vazia. Portanto, se a solicitação for para a raiz da aplicação web, consideraremos que a solicitação é para a página principal - index.html:
if (!filePath) filePath = "index.html";Como a resposta do servidor será código HTML, usamos o método setHeader() para definir o valor "text/html" para o cabeçalho "Content-Type":
response.setHeader("Content-Type", "text/html; charset=utf-8;");Em seguida, usamos a função fs.readFile para ler o arquivo solicitado. O primeiro parâmetro da função é o caminho do arquivo (presumimos que o arquivo esteja na mesma pasta que o arquivo do servidor server.js). O segundo parâmetro é uma função de callback que é chamada após a leitura do arquivo e recebe seu conteúdo como segundo parâmetro, data. Se o arquivo solicitado não existir, enviamos um erro 404:
fs.readFile(filePath, (error, data) => {
  if (error) {
    // Em caso de erro
    response.statusCode = 404;
    response.end("Recurso não encontrado!");
  } else {
    response.end(data);
  }
});Por fim, usamos a função listen() para iniciar o servidor web na porta 3000. Ou seja, o servidor será iniciado no endereço http://localhost:3000.
}).listen(3000, () => console.log("Servidor iniciado em http://localhost:3000"));Definindo o Código HTML para Carregamento
O arquivo home.html conterá um código simples que será carregado pela página web. O código será o seguinte:
<!DOCTYPE html>
<html>
  <head>
    <meta charset="utf-8" />
    <title>Home Page</title>
  </head>
  <body>
    <h1>Home Page</h1>
    <p>Home Page Text</p>
  </body>
</html>Definindo a Página Principal e Carregando Dados
Agora, definiremos o código da página principal index.html, que carregará a página home.html:
<!DOCTYPE html>
<html>
  <head>
    <meta charset="utf-8" />
    <title>Programício</title>
  </head>
  <body>
    <script>
      const xhr = new XMLHttpRequest();
      xhr.onload = () => {
        // Handler para receber a resposta do servidor
        if (xhr.status == 200) {
          // Se o código de resposta for 200
          const html = xhr.responseText; // Obtemos a resposta
          console.log(html); // Exibimos a resposta no console do navegador
        } else {
          // Caso contrário, exibimos o status da resposta
          console.log("Resposta do servidor: ", xhr.statusText);
        }
      };
      xhr.open("GET", "/home.html"); // Solicitação GET para o recurso /home.html
      xhr.setRequestHeader("Accept", "text/html"); // Aceitamos apenas HTML
      xhr.send(); // Enviamos a solicitação
    </script>
  </body>
</html>No handler de carregamento xhr.onload, obtemos o texto da resposta através de xhr.responseText e exibimos a resposta no console.
Agora, no console, navegue até a pasta do servidor usando o comando cd e inicie o servidor com o comando node server.js:
C:\app>node server.js Servidor iniciado em http://localhost:3000
Após iniciar o servidor, podemos acessar o endereço http://localhost:3000 no navegador. A página exibida conterá um código JavaScript que fará uma solicitação à página home.html. O código JavaScript obterá essa página e exibirá seu conteúdo no console:

Gerenciando o Conteúdo HTML
No exemplo anterior, obtivemos o conteúdo da página como texto simples. No entanto, como esse texto contém marcação HTML, podemos carregá-lo na página web. Assim, vamos modificar o código da página index.html da seguinte forma:
<!DOCTYPE html>
<html>
  <head>
    <meta charset="utf-8" />
    <title>Programício</title>
  </head>
  <body>
    <div id="content"></div>
    <script>
      const contentDiv = document.getElementById("content"); // Elemento para carregar HTML
      const xhr = new XMLHttpRequest();
      xhr.onload = () => {
        if (xhr.status == 200) {
          contentDiv.innerHTML = xhr.responseText; // Exibimos a resposta recebida em contentDiv
        } else {
          // Caso contrário, exibimos o texto do status
          console.log("Resposta do servidor: ", xhr.statusText);
        }
      };
      xhr.open("GET", "/home.html"); // Solicitação GET para o recurso /home.html
      xhr.setRequestHeader("Accept", "text/html"); // Aceitamos apenas HTML
      xhr.send(); // Enviamos a solicitação
    </script>
  </body>
</html>Neste caso, carregamos o código da página home.html no elemento com id="content":

No entanto, o problema aqui é que o código da página home.html contém elementos head, title e metadados com a tag <meta />. Não faz sentido carregar esses elementos em outra página web. Além disso, podemos querer carregar apenas um elemento específico da página home.html, e não todo o seu código. Nesse caso, podemos obter a resposta através da propriedade responseXML e, em seguida, manipulá-la como um documento HTML padrão. Por exemplo, vamos modificar o código JavaScript da seguinte forma:
const contentDiv = document.getElementById("content");
const xhr = new XMLHttpRequest();
xhr.onload = () => {
  // Handler para receber a resposta do servidor
  if (xhr.status == 200) {
    // Carregamos apenas o conteúdo do elemento body
    contentDiv.innerHTML = xhr.responseXML.body.innerHTML;
  } else {
    console.log("Resposta do servidor: ", xhr.statusText);
  }
};
xhr.open("GET", "/home.html"); // Solicitação GET para o recurso /home.html
xhr.responseType = "document"; // Definimos o tipo de resposta
xhr.setRequestHeader("Accept", "text/html"); // Aceitamos apenas HTML
xhr.send(); // Enviamos a solicitaçãoAqui, é importante observar dois pontos. Primeiro, definimos o tipo de resposta como "document":
xhr.responseType = "document";Isso nos permite obter a resposta como um objeto do tipo Document, semelhante à propriedade document em uma página web.
Para obter a resposta em formato HTML/XML, usamos a propriedade responseXML. Como essa propriedade representa um objeto Document, utilizamos a propriedade body para acessar o conteúdo da página diretamente:
contentDiv.innerHTML = xhr.responseXML.body.innerHTML;Como resultado, o conteúdo do elemento body da página home.html será carregado em contentDiv.
Da mesma forma, podemos acessar outras propriedades do objeto Document. Por exemplo, para obter o título da página:
document.title = xhr.responseXML.title;Ou para carregar na página apenas o texto do título <h1>:
contentDiv.innerHTML = xhr.responseXML.querySelector("h1").textContent;Carregamento Dinâmico de Componentes
A capacidade de carregar código HTML e inseri-lo na página nos permite ir além e dividir a funcionalidade da aplicação em vários componentes, carregando-os conforme necessário. Por exemplo, vamos supor que no projeto temos os seguintes arquivos:
- server.js: arquivo da aplicação do servidor em Node.js
- index.html: página principal da aplicação
- home.html: arquivo do componente home
- about.html: arquivo do componente about
- contact.html: arquivo do componente contact
O arquivo da aplicação do servidor em Node.js, server.js, permanece o mesmo definido anteriormente neste tema.
O arquivo home.html contém um código simples como o seguinte:
<h1>Home Page</h1>
<p>Home Page Text</p>O arquivo about.html tem uma estrutura similar:
<h1>About Page</h1>
<p>About Page Text</p>E o código do arquivo contact.html:
<h1>Contact Page</h1>
<p>Contact Page Text</p>Esses arquivos representam componentes que serão carregados na página principal.
Na página principal index.html, definimos o seguinte código:
<!DOCTYPE html>
<html>
  <head>
    <meta charset="utf-8" />
    <title>Programício</title>
  </head>
  <body>
    <nav>
      <a href="home">Home</a> | <a href="about">About</a> |
      <a href="contact">Contact</a>
    </nav>
    <div id="content"></div>
    <script>
      const contentDiv = document.getElementById("content");
      function loadContent(fileName) {
        const xhr = new XMLHttpRequest();
        xhr.onload = () => {
          if (xhr.status == 200) {
            contentDiv.innerHTML = xhr.responseText;
            document.title = fileName;
          } else {
            console.log("Resposta do servidor: ", xhr.statusText);
          }
        };
        xhr.open("GET", fileName + ".html");
        xhr.setRequestHeader("Accept", "text/html");
        xhr.send();
      }
      // Configura o handler de clique para os links
      const links = document.getElementsByTagName("a");
      for (let i = 0; i < links.length; i++) {
        links[i].addEventListener("click", (e) => {
          loadContent(links[i].getAttribute("href"));
          e.preventDefault();
        });
      }
      // Carrega o componente home por padrão
      loadContent("home");
    </script>
  </body>
</html>Aqui, para a navegação entre os componentes, colocamos uma série de links na página:
<nav>
  <a href="home">Home</a> | <a href="about">About</a> |
  <a href="contact">Contact</a>
</nav>O endereço de cada link coincide com o nome da página do componente correspondente, sem a extensão ".html".
Cada um dos componentes será carregado na página dentro do elemento com id="content", obtido no código JavaScript na constante contentDiv:
const contentDiv = document.getElementById("content");No código JavaScript, configuramos um handler para cada link, onde chamamos a função loadContent e passamos o valor do atributo href do link, que é o endereço do componente:
const links = document.getElementsByTagName("a");
for (let i = 0; i < links.length; i++) {
  links[i].addEventListener("click", (e) => {
    loadContent(links[i].getAttribute("href"));
    e.preventDefault();
  });
}Na função loadContent, usamos o endereço do link para enviar uma solicitação AJAX, e a resposta (o HTML recebido) é carregada no elemento contentDiv:
contentDiv.innerHTML = xhr.responseText;Ao carregar a página, carregamos imediatamente o código do componente home como o componente padrão:
loadContent("home");Dessa forma, na página principal, podemos acessar componentes específicos clicando nos links:


