Ajax em Javascript

Você já deve ter notado que algumas páginas web utilizam recursos que atualizam os dados da página sem enviar uma nova requisição para o servidor. Isso, provavelmente, é Ajax em Javascript.

Ajax (Asynchronous Javascript and XML) é o uso das tecnologias providas pelos navegadores (como Javascript e XML), para tornar páginas mais interativas com o usuário, utilizando-se de solicitações assíncronas de informações.

Assíncrona quer dizer que a solicitação pode ser enviada "por baixo dos panos" para o servidor, e o usuário não tem que parar e esperar que o processo termine. Os dados são retornados para o cliente como texto, JSON, XML, ou como o desenvolvedor preferir. Este último, por sua vez, pode fazer o que quiser com os dados, como atualizar o conteúdo de um elemento da página, mostrar um erro (se for o caso) ou até mesmo atualizar o conteúdo da página inteira.

Para fazer toda a mágica acontecer, utilizamos um objeto especial chamado XMLHttpRequest, que gerencia a comunicação assíncrona entre o servidor e o cliente. Porém, se você ainda suportar o IE6, terá que fazer aqueles malabarismos, que já conversamos várias vezes em artigos anteriores, para alternar entre o objeto XMLHttpRequest e o ActiveXObject, que é o que funciona com versões antigas do Internet Explorer, como o IE6.

Para sua sorte, do IE7 em diante, XMLHttpRequest é suportado.

Agora chega de conversa e vamos ver códigos, que é o que interessa pra gente.

Acertando o meio de campo

Normalmente, precisamos que algum evento aconteça para que a página seja atualizada, e para que um evento aconteça, é claro que precisamos ter algo em HTML para o usuário interagir.

Vamos criar a página HTML, chamar nosso script .js e colocar dois elementos dentro do body: um link (a) e uma div.

<!DOCTYPE html>
<html>
	<head>
		<meta charset="UTF-8">
		
		<title>Página</title>
		
		<script src="meu_script.js"></script>
	</head>

	<body>
		<a href="" id="clique">Clique</a>
		<div id="minha_div"></div>
	</body>
</html>

Dica: Modifique a codificação do seu editor de textos para UTF-8.

Como vamos fazer uma requisição para chamar outro arquivo dentro do nosso servidor, crie um novo arquivo HTML com o nome que você preferir e coloque algum texto dentro dele. Veja meu arquivo chamado de texto.html:

<h1>Texto</h1>
<p>Olha só! Sou um texto.</p>

Agora podemos trabalhar em nosso arquivo Javascript.

O evento click

Como estamos trabalhando dentro de um evento de clique na página, precisamos capturar este evento.

Veja como fazer isso:

// Captura o evento load da página
window.onload=function(){
	// Localiza o elemento a pelo id
	var a = document.getElementById('clique');
	
	// Captura o evento click no a
	a.onclick=function(){
		// Uma função genérica
		geral();
		
		// Retorna falso para não atualizar a página
		return false;
	}
}

geral = function(){
	// TRABALHAREMOS AQUI
}

Perceba que essa não é a melhor maneira para capturar eventos (veja eventos em Javascript), mas vamos continuar este artigo, até pouco antes do fim, utilizando este método para que os exemplos fiquem menores e fáceis para serem entendidos. Além disso, para facilitar ainda mais, tudo o que você vai ver abaixo estará exatamente onde escrevi // TRABALHAREMOS AQUI, quando for diferente disso, vou dizer.

Feito isso, vamos começar a criar a nossa requisição Ajax.

Mundo dos sonhos: XMLHttpRequest

Se vivêssemos em um mundo onde todos os navegadores fossem novos e aceitassem todas as maravilhas que as novidades nos oferecem, seria só criar uma instância do objeto XMLHttpRequest e mandar a requisição, como mostro abaixo:

Como vimos no artigo anterior, criaremos uma instância do objeto XMLHttpRequest:

// Cria o objeto XMLHttpRequest
var chamada = new XMLHttpRequest();

Agora que a instância "chamada" está criada, vamos detalhar o que precisamos abrir:

// Configura os parâmetros do arquivo que será aberto
chamada.open('GET', 'texto.html', true);

Para resumir, o método "open" do objeto XMLHttpRequest, recebe os seguintes parâmetros: "POST" ou "GET" (existem outros, mas só precisamos de POST e GET por agora), o arquivo que deverá ser aberto (qualquer arquivo no mesmo domínio), e true para conexão assíncrona ou false para síncrona (Veja detalhes aqui).

Agora precisamos enviar os dados para o servidor. Como não estamos enviando nenhum parâmetro, utilizamos o método "send" com um valor null.

// Envia a requisição
chamada.send(null);

Agora temos que verificar se a conexão foi realizada com sucesso. Para isso, vamos utilizar o método onreadystatechange para capturar as mudanças de estados do evento que ocorrendo no objeto XMLHttpRequest.

// Verifica os estados do objeto XMLHttpRequest
chamada.onreadystatechange = function(){
	// Verifica se a página foi carregada completamente sem erros
    if ( this.readyState == 4 && this.status == 200 ) {
		// Localiza nossa div com id minha_div
		var div = document.getElementById('minha_div');
		// Adiciona o conteúdo do texto.html na div
		div.innerHTML = this.responseText;
	}
};

Onreadystatechange recebe uma função para tratar dos estados que estão ocorrendo para a nossa requisição (que vão de 0 a 4). O estado – que está na propriedade readyState – mais importante para a nossa requisição, é o 4, que indica que o arquivo terminou de ser carregado e já temos o resultado. Além disso, verificamos também a resposta do servidor, que neste caso, 200 significa que o arquivo foi carregado sem erros.

Sabendo que no trecho acima tudo está correto, podemos fazer o que quisermos com a resposta do servidor.

Eu simplesmente peguei o texto retornado pelo servidor com chamada.responseText ou this.responseText, e o incluí em nossa DIV de exemplo:

Resposta do Ajax

Como você pode ver na imagem acima, após clicar no link, o texto que estava no arquivo "texto.html", foi enviado diretamente para a nossa DIV sem atualizar a página.

É claro que não vivemos no mundo dos sonhos. Precisamos verificar várias coisas além do que foi descrito acima para ter a certeza que nosso script vai funcionar em todos os navegadores. Porém, os trechos de código acima nos dão um começo para entender como Ajax funciona na vida real.

Abaixo o código completo:

// Captura o evento load da página
window.onload=function(){
	// Localiza o elemento a pelo id
	var a = document.getElementById('clique');
	
	// Captura o evento click no a
	a.onclick=function(){
		// Uma função genérica
		geral();
		
		// Retorna falso para não atualizar a página
		return false;
	}
}

geral = function(){
	// Cria o objeto XMLHttpRequest
	var chamada = new XMLHttpRequest();
	// Configura os parâmetros do arquivo que será aberto
	chamada.open('GET', 'texto.html', true);
	// Envia a requisição
	chamada.send(null);

	// Verifica os estados do objeto XMLHttpRequest
	chamada.onreadystatechange = function(){
		// Verifica se a página foi carregada completamente sem erros
		if ( this.readyState == 4 && this.status == 200 ) {
			// Localiza nossa div com id minha_div
			var div = document.getElementById('minha_div');
			// Adiciona o conteúdo do texto.html na div
			div.innerHTML = this.responseText;
		}
	};
};

Lembre-se que isso não funciona com o IE6, e que você não deve mais utilizar o método de captura de eventos que mencionei acima, prefira sempre addEventListener ou attachEvent, que já expliquei aqui no TutsUP.

Agora vamos para o mundo real.

XMLHttpRequest e ActiveXObject

A primeira coisa para que nosso código funcione no IE6, será modificar o objeto XMLHttpRequest para ActiveXObject. E, para que continuemos dando suporte para todos os navegadores, teremos que utilizar estruturas condicionais.

Veja:

// Cria o objeto XMLHttpRequest
var chamada;
if (window.XMLHttpRequest) { // Mozilla, Safari, ...
	chamada = new XMLHttpRequest();
} else if (window.ActiveXObject) { // IE 6 e anteriores
	chamada = new ActiveXObject("Microsoft.XMLHTTP");
}

Agora sim, se XMLHttpRequest não existir no objeto window, configuramos nossa chamada para o objeto ActiveXObject.

Simples assim!

Agora configuramos o método "open", da mesma maneira:

// Configura os parâmetros do arquivo que será aberto
chamada.open('GET', 'texto.html', true);

Em seguida configuramos o onreadystatechange, porém, devemos trocar a palavra "this", pelo nome da instância do objeto ActiveXObject, em nosso caso, "chamada".

// Verifica os estados do objeto XMLHttpRequest
chamada.onreadystatechange = function(){
	// Verifica se a página foi carregada completamente sem erros
	if ( chamada.readyState == 4 && chamada.status == 200 ) {
		// Localiza nossa div com id minha_div
		var div = document.getElementById('minha_div');
		// Adiciona o conteúdo do texto.html na div
		div.innerHTML = chamada.responseText;
	}
};

Por fim, enviamos a requisição:

// Envia a requisição
chamada.send(null);

POST e GET

Não faz sentido fazer requisições para o servidor, se não vamos postar nem obter nada em troca.

Por exemplo: suponhamos que você tenha um formulário e queira enviá-lo para o servidor utilizando os métodos POST ou GET, como você enviaria isso via AJAX?

Para isso, é necessário configurar o método setRequestHeader da seguinte maneira:

chamada.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded');

No trecho acima, configuramos o MIME Type da aplicação, como costumamos fazer com nossos arquivos HTML.

Agora, se você quiser postar algo, pode fazer assim:

POST

// Cria o objeto XMLHttpRequest
var chamada;
if (window.XMLHttpRequest) { // Mozilla, Safari, ...
	chamada = new XMLHttpRequest();
} else if (window.ActiveXObject) { // IE 6 e anteriores
	chamada = new ActiveXObject("Microsoft.XMLHTTP");
}
// Configura os parâmetros do arquivo que será aberto
chamada.open('POST', 'texto.php', true);
chamada.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded');

// Verifica os estados do objeto XMLHttpRequest
chamada.onreadystatechange = function(){
	// Verifica se a página foi carregada completamente sem erros
	if ( chamada.readyState == 4 && chamada.status == 200 ) {
		// Localiza nossa div com id minha_div
		var div = document.getElementById('minha_div');
		// Adiciona o conteúdo do texto.html na div
		div.innerHTML = chamada.responseText;
	}
};

// Envia a requisição
chamada.send('valor1=' + encodeURIComponent('Meu valor 1') + '&' +
			'valor2=' + encodeURIComponent('Meu valor 2') + '&' +
			'valor3=' + encodeURIComponent('Meu valor 3')
);

A parte do nosso código que mudou foi:

// Configura os parâmetros do arquivo que será aberto
chamada.open('POST', 'texto.php', true);
chamada.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded');

Ao invés de GET, utilizamos POST, e adicionamos uma nova linha para configurar o MIME Type.

Outra parte que mudou foi a seguinte:

// Envia a requisição
chamada.send('valor1=' + encodeURIComponent('Meu valor 1') + '&' +
			'valor2=' + encodeURIComponent('Meu valor 2') + '&' +
			'valor3=' + encodeURIComponent('Meu valor 3')
);

Você envia o valor para send() no seguinte formato:

"parâmetro=valor&parâmetro2=valor2"

Para garantir que nossos dados cheguem do outro lado de maneira segura, utilizamos encodeURIComponent, que assegura que os caracteres inválidos sejam codificados para um formato de URI.

Pronto, os dados chegam do outro lado como um array (em PHP, no caso):

Array
(
    [valor1] => Meu Atenção 1
    [valor2] => Meu valor 2
    [valor3] => Meu valor 3
)

GET

Se você quiser passar parâmetros por GET, deve modificar a URL que você envia para o método open:

chamada.open('GET', 'texto.php?param1=' + encodeURIComponent('Meu valor') +
			'&' + 'param2=' + encodeURIComponent('Meu valor 2') + 
			'&' + 'param3=' + encodeURIComponent('Meu valor 3'), true);

Aqui os parâmetros são enviados diretamente na URL, por exemplo:

"teste.php?param1=Valor 1&param2=Valor 2"

Veja o código completo:

// Captura o evento load da página
window.onload=function(){
	// Localiza o elemento a pelo id
	var a = document.getElementById('clique');
	
	// Captura o evento click no a
	a.onclick=function(){
		// Uma função genérica
		geral();
		
		// Retorna falso para não atualizar a página
		return false;
	}
}

geral = function(){
	// Cria o objeto XMLHttpRequest
	var chamada;
	if (window.XMLHttpRequest) { // Mozilla, Safari, ...
		chamada = new XMLHttpRequest();
	} else if (window.ActiveXObject) { // IE 6 e anteriores
		chamada = new ActiveXObject("Microsoft.XMLHTTP");
	}
	// Configura os parâmetros do arquivo que será aberto
	chamada.open('GET', 'texto.php?param1=' + encodeURIComponent('Meu valor') +
				'&' + 'param2=' + encodeURIComponent('Meu valor 2') + 
				'&' + 'param3=' + encodeURIComponent('Meu valor 3'), true);
	chamada.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded');

	// Verifica os estados do objeto XMLHttpRequest
	chamada.onreadystatechange = function(){
		// Verifica se a página foi carregada completamente sem erros
		if ( chamada.readyState == 4 && chamada.status == 200 ) {
			// Localiza nossa div com id minha_div
			var div = document.getElementById('minha_div');
			// Adiciona o conteúdo do texto.html na div
			div.innerHTML = chamada.responseText;
		}
	};

	// Envia a requisição
	chamada.send(null);
};

Perceba que você vai ter que utilizar uma linguagem que não funcione só do lado do cliente, mas também no lado do servidor, para receber os dados de POST ou GET. Optei por PHP, mas você pode utilizar a linguagem que preferir.

No meu caso, estava só verificando se POST ou GET estavam sendo recebidos pelo arquivo texto.php.

Nele só tem algumas linhas de código:

<?php
$a = $_GET + $_POST;
print_r($a);

O trecho acima simplesmente atribui GET ou POST para uma variável, e imprime o resultado da mesma.

addEventListener ou attachEventListener

Certo, agora vamos deixar o nosso código um pouco mais moderno.

Lembra que eu disse que não era interessante utilizar o método de captura de eventos que utilizei nos exemplos anteriores? Isso porque você só pode adicionar um evento load (por exemplo) na página. Se você estiver utilizando outras bibliotecas externas que utilizam este mesmo tipo de captura de eventos (que é um pouco improvável), terá problemas.

Então veja aquele código dos exemplos acima com o modelo de captura de eventos DOM Nível 2.

// Função para capturar eventos
function captura_eventos(objeto, evento, funcao) {
    // Testa se o navegador suporta addEventListener
    if (objeto.addEventListener) {
        // Adiciona addEventListener
        objeto.addEventListener(evento, funcao, true);
    }
    // Testa se o navegador suporta attachEvent
    else if (objeto.attachEvent) {
        // Adiciona a palavra on no evento
        var evento = 'on' + evento;
        // Adicionar attachEvent
        objeto.attachEvent(evento, funcao);
    }
}

// Função para cancelar os eventos
function cancela_evento(evento) {
    // Testa se o navegador suporta stopPropagation
    if (evento.stopPropagation) {
        // Adiciona stopPropagation
        evento.stopPropagation();
        // Adiciona preventDefault
        evento.preventDefault();
    } else {
        // Configura returnValue como false para o IE
        evento.returnValue = false;
        // Cancela a propagação para o IE
        evento.cancelBubble = true;
    }
}

// Função geral para rodar após o load da página
function manipula_load() {
    // Função para capturar o click no a
    captura_eventos(document.getElementById('clique'), 'click', geral);
}

// Captura o evento load
captura_eventos( window, 'load', manipula_load );

var geral = function(evento){
	// Configura o evento em uma variável
	var evento = evento ? evento : window.event;
	
	// Cria o objeto XMLHttpRequest
	var chamada;
	if (window.XMLHttpRequest) { // Mozilla, Safari, ...
		chamada = new XMLHttpRequest();
	} else if (window.ActiveXObject) { // IE 6 e anteriores
		chamada = new ActiveXObject("Microsoft.XMLHTTP");
	}
	// Configura os parâmetros do arquivo que será aberto
	chamada.open('GET', 'texto.php?param1=' + encodeURIComponent('Meu valor') +
				'&' + 'param2=' + encodeURIComponent('Meu valor 2') + 
				'&' + 'param3=' + encodeURIComponent('Meu valor 3'), true);
	chamada.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded');

	// Verifica os estados do objeto XMLHttpRequest
	chamada.onreadystatechange = function(){
		// Verifica se a página foi carregada completamente sem erros
		if ( chamada.readyState == 4 && chamada.status == 200 ) {
			// Localiza nossa div com id minha_div
			var div = document.getElementById('minha_div');
			// Adiciona o conteúdo do texto.html na div
			div.innerHTML = chamada.responseText;
		}
	};

	// Envia a requisição
	chamada.send(null);
	
	// Cancela o evento e não atualiza a página
	cancela_evento(evento);
};

De início, pode parecer algo muito complicado, mas estou simplesmente juntando partes de código que você já viu anteriormente em outros artigos.

Por exemplo, falamos sobre isso em:

Se você tiver dúvidas quanto a eventos, releia os artigos anteriores.

Concluindo

É claro que trabalhar com Ajax em Javascript não é só isso que demonstrei anteriormente, existem mais propriedades e métodos que você pode utilizar, e mais conhecimento que você pode conseguir procurando mais sobre uma parte em específico dos trechos que detalhei. Porém, o artigo que você acabou de ler, da uma noção geral sobre como é possível sair da página sem necessariamente fazer uma nova requisição ao servidor. Espero que tenha entendido.

Se não entendeu, só comentar que vamos ajudar.

Outras aulas

Caso tenha perdido a aula anterior, segue o link:

Próxima aula:

Caso queira visualizar todas as aulas dessa sessão em ordem cronológica invertida: