Boas práticas com JavaScript e jQuery: código não-obstrusivo

Boas práticas com JavaScript e jQuery: código não-obstrusivo
gabriel.oliveira
gabriel.oliveira

Compartilhe

Já há bastante tempo, por conta do amadurecimento da Web como plataforma de aplicações, a programação front-end de aplicações para Web vem adotando alguns padrões e boas práticas. Os benefícios disso são claros, principalmente quando consideramos o fato de que até as mais simples páginas Web não são tão triviais: são compostas de camadas distintas de marcação, apresentação e interatividade, normalmente tendo HTML, CSS e JavaScript como responsáveis.

Uma das práticas mais importantes quando pensamos na tríade do front-end é o desacoplamento dessas camadas. Ou seja, não devemos adicionar informações visuais, nem sobre interatividade com o usuário, na camada de marcação. Dessa maneira, a manutenção de cada componente é muito mais fácil pois cada um tem seu lugar distinto. Infelizmente, a mistura ainda ocorre, como este frequente código:

 <a style="font-size:16px" href="#" onclick="adicionaItem()">Adicionar item</a> 

No exemplo acima, posso distinguir claramente quem não pertence ao HTML, se levarmos em consideração as camadas visuais (style) e de interatividade (onclick). Elas não deveriam estar ali. Para mantermos nossa marcação limpa, podemos utilizar um seletor do CSS para externalizar as informações visuais. Para deixar o exemplo simples, vamos trocar os atributos alienígenas (pertencentes a outras camadas) por um atributo id:

 <a id="additem" href="#">Adicionar item</a> 

Agora, podemos criar o seguinte seletor no CSS e adicioná-lo ao HTML de maneira adequada (tag <style> ou <link> para arquivo CSS externo):

 #additem { font-size: 16px; } 

De acordo com o demonstrado no primeiro exemplo, o navegador deve disparar a execução da função JavaScript adicionaItem(), quando o usuário clicar na área da página ocupada pelo elemento <a>. A implementação dessa função não é de nosso interesse no exemplo, só precisamos executá-la. Como fazer isso sem adicionar essa função diretamente na marcação HTML?

Uma das características mais importantes da execução de JavaScript em uma página Web é que o navegador disponibiliza acesso a todo e qualquer elemento declarado no HTML através da DOM API (Document Object Model API). Isso significa que podemos, em nosso código JavaScript, criar objetos que fazem referência direta a tags do HTML.

Alguns desses objetos refletem a alteração de seus atributos imediatamente no navegador. Para implementar o comportamento necessário, devemos informar ao navegador que, em determinado elemento, há uma função a ser executada caso ele seja o target de um evento. Para obtermos esse resutado, precisamos interagir com o EventListener desse elemento. Em JavaScript puro, teríamos a seguinte abordagem:

 // Primeiro é necessário criar um objeto que faz // referência ao elemento no HTML: var linkAddItem = document.getElementById('additem');

// Depois adicionamos a função "adicionaItem" à lista de // funções que devem ser executadas quando o usuário clica // na área do elemento no navegador: linkAddItem.addEventListener('click', adicionaItem, false); 

Esse padrão é o que chamamos de JavaScript não-obstrusivo (não intrusivo). Estamos adicionando interatividade à página através do JavaScript, sem a adição de atributos e informações desnecessárias na marcação. Note que passamos como segundo argumento o nome da função somente, sem os parênteses, necessários para executá-la. Isso porque não queremos executá-la de fato, apenas delegar sua execução à ocorrência do evento.

Fato é que os navegadores (principalmente o IE antigo) não são exatamente consistentes na implementação desses objetos em suas APIs. A própria função addEventListener não existe no IE até sua versão 9. No IE8 e anteriores, devemos chamar a função attachEvent, que implementa o mesmo comportamento.

Para termos um código compatível entre navegadores, seria necessário realizar uma série de verificações para identificar qual abordagem tomar em cada caso. A boa notícia é que algumas bibliotecas de JavaScript fazem isso para nós. Elas abstraem essas diferenças em funções relativamente mais simples e compatíveis com os principais navegadores, versões e plataformas, se não todas.

Eventos com jQuery

O jQuery, hoje a biblioteca JavaScript que é quase onipresente, interage com os EventListeners de maneira bem direta:

 $('#additem').on('click', adicionaItem); 

Caso você precise usar uma versão anterior a 1.7 do jQuery, substitua a função "on()" pela "bind()".

No código acima, a função $('#additem') nos retorna um objeto que representa o elemento com id additem em nossa marcação. Esse é um objeto do jQuery e podemos chamar sua função on() que precisa de dois argumentos: o nome do evento a ser observado e o nome da função que deve ser executada ao ocorrer esse evento na área que o elemento ocupa na página. Essa abordagem nos permite descartar o atributo alienígena onclick no HTML, sendo que o próprio JavaScript identifica qual função deve ser executada quando ocorre um evento em determinado elemento da página.

Nos casos em que a função a ser executada precise de argumentos, não podemos passar os argumentos com esse padrão, é necessário passar como argumento para a função on() uma função anônima e, dentro dessa, chamar a função adicionaItem() com argumentos.

Essa função anônima (bem como a função adicionaItem nos exemplos anteriores) recebe, por padrão, um objeto que representa o evento ocorrido no caso de sua execução. Esse objeto contém diversas informações interessantes como um timestamp de quando ocorreu o evento, no caso do evento click, quais eram as coordenadas do mouse na janela do navegador no momento do clique entre outras.

Vamos supor um número como argumento por exemplo:

 $('#additem').on('click', function(event) { // Essa função anônima pode conter uma lógica mais complexa. adicionaItem(1); }); 

Por estarmos adicionando um evento a um link nesse exemplo, o comportamento padrão do navegador é, após a execução da função JavaScript, o evento retornar ao seu ciclo normal e o usuário ser levado ao endereço declarado no link, nesse caso o topo da página (href="#"). Normalmente, esse comportamento é indesejado, então vamos informar nossa função que queremos anular o restante do ciclo do evento:

 $('#additem').on('click', function(event) { // Essa função anônima pode conter uma lógica mais complexa. adicionaItem(1);

// Anular a continuação do ciclo do evento no navegador: event.preventDefault(); }); 

Nos primeiros exemplos, quando passamos o nome da função diretamente à funcão on(), seria necessário adicionar a linha event.preventDefault() dentro da função adicionaItem() para obtermos o comportamento correto. Alguns desenvolvedores também utilizam return false; no lugar de event.preventDefault(); o que, em nosso simples exemplo, terá o mesmo resultado.

Esse tratamento direto do objeto event permite inclusive a adoção de um outro padrão recomendado: nenhum link deve levar ao destino "#" (topo da página), a não ser que seja essa sua finalidade. O ideal é que no atributo href seja utilizado um URL que realizará o mesmo comportamento que esperamos com o JavaScript, só que sem JavaScript. Esse fallback é importante para acessibilidade e atende dispositivos que não suportam JavaScript.

Podemos atribuir uma função ao EventListener de qualquer elemento, não só dos links. Nesses casos, não precisamos anular o comportamento padrão do evento com "event.preventDefault()" visto que clicar em uma <img> ou <div> qualquer não tem efeito colateral na interação do usuário com a página.

Esses e outros assuntos avançados de JavaScript, a gente discute em mais detalhes na Formação Web da Caelum.

Veja outros artigos sobre Front-end