Blog do Urubatan
msgbartop
Desenvolvedor, Arquiteto, Palestrante, Coordenador do RSJUG, Patinador e Blogger
msgbarbottom

21 Feb 08 Javascript não intrusivo com Lowpro e Prototype

Como eu disse que iria fazer, e fui cobrado no último rails podcads brasil, segue um post sobre o lowpro.
Isto não vai ser exatamente um tutorial, apenas uma coleção de dicas sobre esta excelente extensão para o prototype[bb], o Lowpro.
O Lowpro ja foi até transformado em um plugin para o rails, o UJS Rails Plugin, mas sinceramente, eu prefiro adicionar a meia duzia de linhas de javascript no meu application.js mesmo do que usar outro plugin só pra isto :D

Mas seguindo o baile, tudo o que vou escrever neste post, vocês podem usar com ou sem o rails, só sera necessário o prototype e o lowpro, inclusive o lowpro ja tem até uma versão que funciona junto com o jQuery[bb] para os que não gostam do prototype :D

Mas seguindo com o assunto deste post, o que vocês acham de adicionar alguns recursos Javascript e até mesmo AJAX[bb] em uma página pronta sem mexer no HTML gerado? nem para adicionar Javascript nos onclick, onblur, onXXX …
Na verdade, sera necessário adicionar pelo menos 3 linhas:

1
2
3
<script src="prototype.js" type="text/javascript" charset="utf-8"></script>
<script src="lowpro.js" type="text/javascript" charset="utf-8"></script>
<script src="meuarquivodescripts.js" type="text/javascript" charset="utf-8"></script>

A última linha, ou seja o “meuarquivodescripts.js” define o arquivo onde vamos trabalhar :D

Um dos recursos que eu acho mais espetaculares no lowpro, é o suporte a comportamentos …
Como assim?
imagine a seguinte situação:
Tenho uma página pronta, é uma página de listagem, e no final da tabela tem um link para a criação de um novo item, hoje toda a página é atualizada quando clico neste link, mas eu quero que apenas a parte da tablea seja substituida pelo formulário, em vez de recarregar a tela toda …
Bom isto é muito fácil de fazer :D
E não vamos precisar de praticamente nenhum código …
Claro, precisamos de alguma forma de identificar o link de adicionar novo objeto na tabela, então, consideremos que este e todos os links que quisermos que sejam abertos por ajax no lugar da tabela, tenham a classe “my_ajax_link”, e consideremos que a tabela esta dentro de uma DIV com ID ‘minha_tabela’…
Se isto for verdade, podemos fazer o seguinte:

1
Event.addBehavior({ 'a.my_ajax_link' : Remote.Link({update:'minha_tabela'}) });

Com isto, todos os links que tenham a classe “my_ajax_link” quando clicados vão ser executados via ajax, e vão substituir o conteúdo do elemento de id “minha_tabela” …

OK, mas é só isto que eu posso fazer com o tal de lowpro? E estes tais de comportamentos?

Na verdade o negócio é bem mais flexivel que isto :D
Vamos ver mais alguns exemplos …
Se você quiser executar algum código no momento em que a página aparecer para o usuário (não é no onload, o onload só é executado depois de toda a página carregada :D )
Basta fazer algo assim:

1
2
3
Event.onReady(function() {
  $$('h1').first().innerHTML += ' (Version ' + LowPro.Version + ')'; 
});

Isto vai mostrar a versão do Lowpro dentro da primeira tag H1 da página :D
O Event.onReady executa um código qualquer no momento em que o DOM da página estiver carregado, o que é bem antes de carregas as imagens, por exemplo, e o onload só sera executado depois de tudo carregado …

Outro exemplo bem simples:
Eu quero que quando o usuário colocar o mouse sobre um link ele fique verde, quando tirar o mouse ele volte a cor padrão, e quando clicar o link incremente um contador dizendo quantas vezes ele ja foi clicado …

Para isto precisamos criar um comportamento …

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
var LinkBehavior = Behavior.create({
  initialize : function() {
    this.count = 0;
  },
  onmouseover : function() {
    this.element.addClassName('goo');
  },
  onmouseout : function() {
    this.element.removeClassName('goo');
  },
  onclick : function() {
    this.element.innerHTML = 'Clicks: ' + ++this.count;
    return false;
   }
});

O que vai fazer os nossos links mudarem de cor é a classe “goo”, por tanto para que as cores mudem como eu disse antes, é preciso criar a classe “goo” em algum arquivo css carregado pela página.

Mas só este código apenas cria uma classe “comportamento” que define que o objeto que tiver este comportamento vai receber a classe “goo” no evento onmouseover, tera esta classe removida no evento onmouseout e vai ter o conteúdo alterado quando clicado.

Para que isto funcione com todos os links da página, precisamos adicionar este comportamento aos links da página …

1
Event.addBehavior({ 'a' : LinkBehavior });

Pronto, agora todos os links vão ter o comportamento especificado pela classe.

Claro que alterar assim todos os links de uma página não é a coisa mais útil do mundo, mas se compararmos este último exemplo com o anterior, podemos ver que o lowpro, utiliza css selectors para identificar onde os behaviors devem ser aplicados …
Como assim?

  • a - todas as tags a
  • a.meulink - todas as tags a que tenham a classe “meulink”
  • a#link - a tag a com ID link
  • #qualquerid - qualquer tag mas que tenha o ID “qualquerid”
  • .qualquerclasse - qualquer tag que tenha a classe “qualquerclasse”

Tudo bem, esta melhorando, mas eu não curti esta idéia de ter que criar uma classe para definir o que eu quero que aconteça quando o usuário clicar em um link, não tem como fazer tudo isto com uma linha de código só?
Na verdade tem :D

1
Event.addBehavior({'a#bung:click' : function () {alert('Você Clicou no link');return false;}})

O que estamos fazendo agora é ligar uma função javascript ao evento onclick da tag a com ID bung.

E como é possível ver (talver não tão fácil por que a letra do código aqui no blog é meio pequena, o parâmetro apra a função addBehavior é um objeto, esta sendo utilizada a sintaxe de objetos JSON para passar os parâmetros, por tanto, isto quer dizer que podemos fazer algo parecido com isto:

1
2
3
4
5
Event.addBehavior({
  'a#bung:click' : function () {alert('Você Clicou no link');return false;},
  'a.ajax_link' : Remote.Link({update:'qualquerid'}),
  '.mudacoreconta': LinkBehavior 
})

Ou seja, passar diversos itens a cada chamada do Event.addBehavior.
Outra coisa interessante, é que novas chamadas, vão empilhar métodos na fila se chamada, ou seja, este código:

1
2
3
Event.addBehavior({'a#bung:click' : function () {alert('Você Clicou no link');return false;}})
Event.addBehavior({'a#bung:click' : function () {alert('Você Clicou no link novamente');return false;}})
Event.addBehavior({'a#bung:click' : function () {alert('Você Clicou no link');return false;}})

Vai mostrar 3 mensagens quando o link for clicado.

Uma outra coisa interessante do lowpro é que ele pode aplicar os comportamentos em objetos que forem carregados por uma chamada ajax também, para isto precisamos de mais uma linha de código (pode ser executada no inicio da aplicação)

1
Event.addBehavior.reassignAfterAjax = true;

Isto configura o lowpro para reaplicar os eventos após cada chamada AJAX.

Claro que podemos misturar isto com outras bibliotecas Javascript, por exemplo no meu pet project de planejamento doméstico, eu tenho o seguinte código no application.js:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
var WindowedForm = Behavior.create({
   onclick : function() {
    if (remote_window){
      remote_window.destroy();
    }
    remote_window = new UI.Window({ theme: 'leopard', width: 600, height: 300, focus : true, minimize : false });
    remote_window.setAjaxContent(this.element.href, {method: 'get'});
    remote_window.center();
    remote_window.activate();
    remote_window.show(true);
    return false;
  } 
});
remote_window = false;
 
Event.addBehavior({ 'a.action_form' : WindowedForm });

Isto faz com que todos os links que tenham a classe action_form sejam abertos em uma janela modal criada pela biblioteca “prototype-ui”.

Ahh, e para finalizar com chave de ouro, uma dica para quem usa rails e will_paginate para a paginação, o código que vou mostrar agora utiliza a lowpro para fazer com que a paginação da will_paginate passe a funcionar via AJAX :D

1
2
3
Event.addBehavior({
  'div.pagination a' : Remote.Link({update:'nomedadiv'})
})

Pronto, só isto, agora é só fazer a action correspondente retornar apenas a tabela e não a página toda quando for uma requisição AJAX, e colocar uma div com ID “nomedadiv” na volta da tabela :D

E por último mas não menos importante, não esqueça de alterar o código no servidor para retornar ou Javascript para ser executado, ou a parte do HTML desejado, mas sem as tags html,head,body, …

O lowpro facilita muito o trabalho no client, mas as respostas as chamadas AJAX continuam necessitando ser implementadas adequadamente.

Bom, espero que este post ajude alguem :D
Quaisquer dúvidas podem perguntar por aqui mesmo :D

Se você gostou deste post, lembre-se de assinar o RSS feed do blog, para ser notificado de novos posts!

Tags: , , , ,

Reader's Comments

  1. |

    Legal a biblioteca, mas o que ela traz de novo que o jquery já não faça?

    Com o jquery já tenho todos esses seletores, posso adicionar attr, css class, css attr, eventos …

    Logo só acho interessante estudá-la caso a pessoa não tenha conhecimento ainda de nenhum framework para javascript.

    Ou estou enganado, o lowpro tem algo novo que não enxerguei?

    Atenciosamente,
    Thiago Antonius

    Reply to this comment
  2. |

    [...] Mas o último exemplo que eu quis fazer não funcionou, era um exemplo de como fazer Paginação via AJAX no grails utilizando Javascript não com o lowpro, parecido com o que eu comentei no post sobre lowpro para o rails. [...]

    Reply to this comment
  3. |

    [...] Javascript não intrusivo com Lowpro e Prototype | Blog do Urubatan Isto não vai ser exatamente um tutorial, apenas uma coleção de dicas sobre esta excelente extensão para o prototype[bb], o Lowpro. (tags: http://www.urubatan.com.br 2008 mes1 dia23 at_home prototype javascript unobtrusive não_intrusivo **** blog_post lowpro) [...]

    Reply to this comment
  4. |

    ::o que nao entendo eh o titulo: Javascript não intrusivo com Lowpro e Prototype

    ::o nao-obtrusivo depende de vc saber faze-lo da maneira correta, independente de existir um lowpro ou nao. independente mesmo de existir prototype ou qualquer outro fwrk.

    ::entao p q o lowpro? se o desenvolvedor q codifica obtrusivamente, com o lowpro ele nao tera como? duvido?

    Reply to this comment

Leave a Comment