Eventos em Javascript

Eventos em Javascript são disparados por alguma ação, tanto do usuário quanto do seu código, tais como um clique com o mouse, uma tecla do teclado pressionada, entrar ou sair de um campo de um formulário, e assim por diante.

Qualquer evento em Javascript pode ser capturado e tratado por manipuladores de eventos, que, por sua vez, aceitam uma função para detalhar o que será feito quando tal evento for disparado.

Por exemplo, você pode disparar um alerta na tela do navegador quando o usuário clicar em determinada área do HTML do seu site.

Um dos problemas que você vai enfrentar muito daqui em diante, será o problema da diferença entre os navegadores. Infelizmente, os navegadores (principalmente o Internet Explorer) apresentam algumas diferenças que devem ser dribladas com alguns truques no seu código. Felizmente, vou detalhar tudo o que você precisa saber para manipular eventos em todos os navegadores, incluindo os mais antigos, que nem são suportados mais por sites como YouTube, por exemplo, tais como IE6 e IE7.

Eventos em Javascript

Vou detalhar métodos antigos e novos para manipulação de eventos em Javascript, isso porque você deve entender como os eventos funcionam e o porquê você deve driblar as diferenças entre os navegadores.

Por este motivo, vamos iniciar do mais básico, que continua funcionando na maioria dos navegadores mais novos, até os métodos usados atualmente para manipular tais eventos.

Eventos em Javascript são categorizados em três áreas distintas pela W3C: interface de usuário (teclado e mouse), lógica (resultante de um processo qualquer) e mutação (ação que altera determinada área do documento).

Dentro os eventos que podem ser utilizados, temos os seguintes:

  • abort – Quando uma imagem não pode ser carregada
  • blur, focus – Quando entra ou sai de um campo do formulário
  • change – Quando altera o valor de uma opção do formulário
  • click, dbclick – Um ou dois cliques com o mouse
  • contextmenu – Clique com o botão direito do mouse
  • error – Quando a página ou uma imagem não pode ser carregada
  • keydown, keyup, keypress – Pressionar, soltar, ou pressionar e soltar uma tecla do teclado
  • load, unload – Quando a página termina de ser carregada, ou ao sair da página
  • mousedown, mouseup – Pressionar e soltar o mouse
  • mouseover, mouseout – Quando o mouse está sobre algo; quando o mouse deixa de estar sobre algo
  • reset – Zerar os campos do formulário
  • resize – A janela é redimensionada
  • select – Seleção de texto
  • scroll – Quando rola a página
  • submit – Quando envia um formulário

Como você pôde perceber, existem milhares de eventos (e outros além dos descritos) para cada ação do usuário na página. Cada um desses eventos têm suas propriedades, que podem ser acessadas para que sua aplicação faça algo interessante.

Tais eventos podem ser manipulados com os manipuladores de eventos, os quais recebem o prefixo "on" antes do nome do evento. Por exemplo, para o evento load, o manipulador de evento chama-se onload.

Eventos em Javascript do DOM nível 0

Eventos em Javascript são relacionados com os elementos da sua página, sendo assim, você pode acessá-los como um atributo de uma tag HTML.

Por exemplo, você pode utilizar o atributo onload() diretamente na tag body do seu HTML:

<body onload="var a = 10; var b = 2; alert(a * b);">

Isso deverá resultar em um alerta com o número 20 na tela do navegador.

Ou você poderia chamar uma função que você tenha escrito em seu código:

<body onload="alerta('Oi mundo!');">

Isso chamaria a função alerta( parâmetro ) com o parâmetro "Oi mundo!".

Se você escrever uma função que exibe um alerta do parâmetro passado, o texto "Oi mundo!" será exibido na caixa do alerta.

Este método de utilização de eventos em Javascript não é recomendável, e você pode imaginar o motivo!

Imagine um site grande com milhares de páginas e arquivos, se você for especificar em cada uma das tags <body> de todos os arquivos do site, provavelmente perderia muito tempo e deixaria de ser eficiente.

Por este motivo, você pode acessar os manipuladores de eventos em Javascript diretamente como uma propriedade de um objeto.

Um exemplo disso, seria chamar a propriedade onload() do objeto window, como o trecho a seguir exemplifica.

function alerta() {
	alert('Oi mundo!');
}

window.onload = alerta;

Este caso seria o mesmo do exemplo onde utilizamos o atributo onload() na tag body do HTML, ou seja, assim que a página terminar de carregar, executa a função alerta().

Um problema encontrado aqui, é que não é possível utilizar um evento Javascript de nível 0 mais de uma vez. Se você tentar chamar duas vezes o evento de onload, ele será disparado apenas na primeira chamada, na próxima não fará absolutamente nada no seu código.

Outro problema que você vai encontrar aqui (e em outras áreas do Javascript) é a diferença que o Internet Explorer 6, 7 e 8, trata os eventos em Javascript se comparado aos outros navegadores.

Especificamente o IE, anexa um evento como uma propriedade do objeto window, já os outros navegadores, anexam um evento como parâmetro de uma função. Com isso seu código deverá funcionar em apenas uma das duas partes: a dos IEs (6, 7 e 8) ou a dos outros navegadores (Firefox, Chrome, Safari, Opera e IE9, 10 e posteriores – É estranho, mas a própria Microsoft entrou em contradição aqui).

Vamos exemplificar melhor ao que estou me referindo em códigos Javascript.

Eventos em Javascript no IE6, IE7 e IE8:

// Captura o evento click
document.onclick = alerta;

// A função alerta
function alerta() {
	// Cria uma variável e da um valor inicial pra ela
	var alertar = 'Posição X é: ';
	
	// Concatena a posição em que o clique foi feito na variável
	alertar += window.event.screenX;
	
	// Exibe o alerta: Posição X é: o local do clique
	alert(alertar);
}

Se você criar um código como o que descrevi acima, ele vai funcionar apenas nas versões do IE mencionadas, os outros navegadores apresentarão um erro: window.event is undefined.

Isso acontece exatamente pelo que descrevi anteriormente, apenas aquelas versões do IE anexam o evento ao objeto window, os demais navegadores fazem isso de outra maneira. Veja:

Eventos em Javascript em outros navegadores:

Como os outros navegadores anexam eventos Javascript em parâmetros da função executada na sua captura, basta mudar um pouquinho o código e pronto:

// Captura o evento click
document.onclick = alerta;

// A função alerta
function alerta( evento ) {
	// Cria uma variável e da um valor inicial pra ela
	var alertar = 'Posição X é: ';
	
	// Concatena a posição em que o clique foi feito na variávlel
	alertar += evento.screenX;
	
	// Exibe o alerta: Posição X é: o local do clique
	alert(alertar);
}

Simplesmente adicionei um parâmetro (evento) na função, e troquei a palavra window.event por este parâmetro. Resolvido? Não, porque agora seu código deixou de funcionar no IE6, 7 e 8.

Veja a solução:

Eventos em Javascript para todos os navegadores:

// Captura o evento click
document.onclick = alerta;

// A função alerta
function alerta( evento ) {
	// Configura o evento
	var o_evento = evento ? evento : window.event;
	
	// Cria uma variável e da um valor inicial pra ela
	var alertar = 'Posição X é: ';
	
	// Concatena a posição em que o clique foi feito na variável
	alertar += o_evento.screenX;
	
	// Exibe o alerta: Posição X é: o local do clique
	alert(alertar);
}

Agora sim, funciona em todos os navegadores.

A solução foi o seguinte: você se lembra quando falamos sobre estruturas condicionais em Javascript? Também detalhamos como utilizar if e else em linha, que foi exatamente o que utilizamos para configurar a nova variável (o_evento), que entrou no trecho de código acima. Fizemos uma condição para saber se o parâmetro "evento" estava definido, se sim, seu valor seria atribuído à variável, caso contrário, window.event seria atribuído.

Ou seja, resolve o problema para os IEs e os outros navegadores.

Mas as diferenças, com certeza, não param por aí. Você vai penar bastante em outras partes do seu código com isso, tudo porque a Microsoft quis ser diferente com essas versões antigas do IE, mas com um if aqui, outro ali, você chega no fim do seu código sem problemas.

Eventos em Javascript do DOM nível 2

Certo, você viu como os eventos eram capturados e manipulados antigamente, e ainda funcionam normalmente em todos os navegadores mais modernos, incluindo as versões que temos hoje (24/04/2014 às 23:32 – hora em que estou escrevendo essa parte do artigo), porém, como um desenvolvedor de aplicações em Javascript, você deve se adequar aos novos métodos de desenvolvimento (isso é altamente recomendável, sem brincadeira).

Neste método, cada objeto apresenta três métodos: addEventListener, removeEventListener e dispatchEventListener.

O primeiro é utilizado para capturar um evento em um elemento, o segundo para remover e o terceiro para enviar um novo evento.

objeto.addEventListener('Evento', 'função', true ou false);

O primeiro parâmetro deve ser o evento (sem o prefixo "on"), o segundo será a função utilizada para tratar tal evento, o terceiro será true para transformar o listener em um modelo de captura de eventos em cascata, e false para transforma-los em eventos de propagação.

Veja o exemplo que expliquei anteriormente modificado para o novo modelo:

// Captura o evento click
document.addEventListener('click', alerta, true);

// A função alerta
function alerta( evento ) {
	// Cria uma variável e da um valor inicial pra ela
	var alertar = 'Posição X é: ';
	
	// Concatena a posição em que o clique foi feito na variávlel
	alertar += evento.screenX;
	
	// Exibe o alerta: Posição X é: o local do clique
	alert(alertar);
}

Praticamente a mesma coisa, não? Não, aqui você também terá problemas sobre a diferença de navegadores.

Novamente o IE6, 7 e 8 não suportam a função "addEventListener", ao invés disso, criaram uma nova propriedade: attachEvent.

Para que isso funcione no IE6-8, você terá que fazer o seguinte:

// Captura o evento click
document.attachEvent('onclick', alerta);

// A função alerta
function alerta() {
	// Cria uma variável e da um valor inicial pra ela
	var alertar = 'Posição X é: ';
	
	// Concatena a posição em que o clique foi feito na variávlel
	alertar += window.event.screenX;
	
	// Exibe o alerta: Posição X é: o local do clique
	alert(alertar);
}

É praticamente a mesma coisa sobre o que eu disse no trecho anterior desse artigo, o IE não utiliza o parâmetro da função, mas captura o evento no objeto window, já na parte do attachEvent, você só tem que enviar dois parâmetros, o evento (com o prefixo "on") e a função que será executada.

Neste caso, voltamos às nossas condições para corrigir o problema para todos os navegadores, veja o trecho de código abaixo:

// Checagem para todos os navegadores
if ( document.attachEvent ) {
	document.attachEvent('onclick', alerta);
} else if ( document.addEventListener ) {
	document.addEventListener('click', alerta, true);
}

// A função alerta
function alerta( evento ) {
	// Configurando o evento para todo mundo ficar feliz
	var o_evento = evento ? evento : window.event;
	
	// Cria uma variável e da um valor inicial pra ela
	var alertar = 'Posição X é: ';
	
	// Concatena a posição em que o clique foi feito na variávlel
	alertar += o_evento.screenX;
	
	// Exibe o alerta: Posição X é: o local do clique
	alert(alertar);
}

Pronto! Agora todo mundo fica feliz e sabe interpretar nosso código Javascript.

Se você analisar a função acima, verá que fizemos tudo o que já expliquei anteriormente. Primeiro verificamos se o navegador suporta addEventListener no objeto document, se não adicionamos o attachEvent da Microsoft, em seguida colocamos o parâmetro de volta na função, criamos a variável o_evento; testamos a condição para ver se o parâmetro evento estava definido, se não, adicionamos o padrão da Microsoft, window.event. Fechou!

Cascata ou propagação?

Você viu que addEventListener tem uma terceira opção, um valor booleano que define se o evento vai ser tratado em cascata ou simplesmente uma propagação de eventos. Na verdade isso é bastante útil e mais simples do que você pode ter imaginado anteriormente.

Isso não faz praticamente nada se você estiver manipulando apenas um evento de um elemento, porém, pode acontecer de você adicionar mais de um evento em um mesmo elemento, e aqui essa opção faz a diferença.

Suponhamos que você tenha dois elementos DIV, um dentro do outro:

<div id="a" style="width:1000px; height:100px; background:red;">
	a
	<div id="b" style="width:500px; height:50px; background:blue;">
		b
	</div>
</div>

Você poderia adicionar dois eventos "click" nas duas:

// Checagem para todos os navegadores
   if ( window.attachEvent ) {
	window.attachEvent('onload', config);
} else if ( window.addEventListener ) {
	window.addEventListener('load', config, true);
}

// A função de configuração
function config( evento ) {
	var elementA = document.getElementById('a');
	var elementB = document.getElementById('b');
	
	elementA.addEventListener('click', alerta, true);
	elementB.addEventListener('click', alerta, true);
}

// A função alerta
function alerta( el ) {
	alert( this.innerHTML );
}

Observação: Não se assuste, você verá document.getElementById e as demais funções não mencionadas aqui em aulas posteriores.

Neste caso, configuramos a terceira opção para true (cascata).

O que vai acontecer é que os eventos serão executados de fora para dentro, ou seja, da DIV b para a DIV a (literalmente em cascata). Se você mudar essa opção para false:

// Checagem para todos os navegadores
   if ( window.attachEvent ) {
	window.attachEvent('onload', config);
} else if ( window.addEventListener ) {
	window.addEventListener('load', config, true);
}

// A função de configuração
function config( evento ) {
	var elementA = document.getElementById('a');
	var elementB = document.getElementById('b');
	
	elementA.addEventListener('click', alerta, false);
	elementB.addEventListener('click', alerta, false);
}

// A função alerta
function alerta( el ) {
	alert( this.innerHTML );
}

Os eventos serão executados de dentro para fora (propagação de eventos). Neste caso, quando eu clicar na DIV a, terei apenas um alerta, porém, se eu clicar na DIV b, terei dois alertas, da DIV a e da DIV b, porque a DIV b está dentro da DIV a, e quando eu clicar na DIV b, automaticamente estarei clicando na DIV a (sei que é confuso, mas é isso que acontece).

Caso queira parar a propagação de eventos, basta utilizar a opção stopPropagation() (para todos os navegadores) ou configurar a opção cancelBubble = true; para os IE antigos.

if ( o_evento.stopPropagation ) {
	o_evento.stopPropagation();
} else {
	o_evento.cancelBubble = true;
}

Veja a função completa que funciona em todos os navegadores (parando a propagação):

// Checagem para todos os navegadores
   if ( window.attachEvent ) {
	window.attachEvent('onload', config);
} else if ( window.addEventListener ) {
	window.addEventListener('load', config, true);
}

// A função de configuração
function config() {
	var elementA = document.getElementById('a');
	var elementB = document.getElementById('b');
	
	if ( elementA.addEventListener ) {
		elementA.addEventListener('click', alerta, false);
		elementB.addEventListener('click', alerta, false);
	} else {
		elementA.attachEvent('onclick', alerta);
		elementB.attachEvent('onclick', alerta);
	}
}

// A função alerta
function alerta( el ) {
	var o_evento = el ? el : window.event;
	var target = o_evento.target ? o_evento.target : o_evento.srcElement;
	
	alert(target.innerHTML);
	
	if ( o_evento.stopPropagation ) {
		o_evento.stopPropagation();
	} else {
		o_evento.cancelBubble = true;
	}
}

Em todos os navegadores, você pode utilizar a palavra "this" para se referir ao objeto que você está manipulando dentro da função, porém, por questões de compatibilidade, é interessante que você utilize target (para todos os navegadores) ou srcElement (para os IEs mais velhos).

var target = o_evento.target ? o_evento.target : o_evento.srcElement;

O exemplo acima faz o teste condicional para verificar qual das duas propriedades estarão disponíveis, target ou srcElement.

Último problema com os IEs antigos

Agora é o último problema que você vai encontrar, prometo!

O problema com os modelos que a Microsoft utiliza, é que a memória é alocada para cada manipulador de eventos e, se você recarregar a página, a memória continua a ser alocada. Ou seja, dependendo do tamanho da sua aplicação, o navegador pode chegar a travar por utilizar tanta memória.

Para resolver este problema, vamos fazer com que o evento seja liberado a cada vez que o usuário sair da página.

Para isso, basta utilizar o evento onunload (quando a página é descarregada) do objeto window.

window.attachEvent('onunload', cancela_eventos);

A função cancela_eventos tem o efeito contrário da função config:

function cancela_eventos() {
	var elementA = document.getElementById('a');
	var elementB = document.getElementById('b');
	
	if ( elementA.detachEvent ) {
		alert('Eliminando eventos');
		elementA.detachEvent('onclick', alerta);
		elementB.detachEvent('onclick', alerta);
	}
}

Simplesmente verifica se o objeto suporta detachEvent e o aplica com as mesmas configurações usadas em attachEvent.

A função completa para não fazer praticamente nada ficou assim:

// Checagem para todos os navegadores
   if ( window.attachEvent ) {
	window.attachEvent('onload', config);
	window.attachEvent('onunload', cancela_eventos);
} else if ( window.addEventListener ) {
	window.addEventListener('load', config, true);
}

// A função de configuração
function config() {
	var elementA = document.getElementById('a');
	var elementB = document.getElementById('b');
	
	if ( elementA.addEventListener ) {
		elementA.addEventListener('click', alerta, false);
		elementB.addEventListener('click', alerta, false);
	} else {
		elementA.attachEvent('onclick', alerta);
		elementB.attachEvent('onclick', alerta);
	}
}

// A função alerta
function alerta( el ) {
	var o_evento = el ? el : window.event;
	var target = o_evento.target ? o_evento.target : o_evento.srcElement;
	
	alert(target.innerHTML);
	
	if ( o_evento.stopPropagation ) {
		o_evento.stopPropagation();
	} else {
		o_evento.cancelBubble = true;
	}
}

function cancela_eventos() {
	var elementA = document.getElementById('a');
	var elementB = document.getElementById('b');
	
	if ( elementA.detachEvent ) {
		alert('Eliminando eventos');
		elementA.detachEvent('onclick', alerta);
		elementB.detachEvent('onclick', alerta);
	}
}

Viu como a Microsoft é gente fina?

Links úteis

Mostrei a você como capturamos e manipulamos eventos em Javascript, porém, não mostrei todas as propriedades do evento. Nos meus exemplos, estava utilizando apenas a propriedade screenX para pegar a posição X da página quando o usuário clicar em determinada área, porém, existem várias propriedades que você pode utilizar no seu código.

No site W3schools, você verá milhares de propriedades que podem ser úteis para seu código, dependendo do que for fazer, basta substituir a propriedade screenX dos meus exemplos para testar, segue o link (em inglês):

Caso tenha dúvidas, não hesite em perguntar nos comentários desse artigo, estamos aqui para ajudar.

Outras aulas

Caso tenha perdido a aula anterior, segue o link:

Próximo aula:

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