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çãohome.html
: a página com o código HTML que carregaremos via AJAXserver.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ígido
Para 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ção
Aqui, é 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.jsindex.html
: página principal da aplicaçãohome.html
: arquivo do componente homeabout.html
: arquivo do componente aboutcontact.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: