Originalmente escrito por Mike Alsup no blog Learning jQuery

Eu tenho desenvolvido plugins do jQuery por um tempo e me acostumei com um estilo bastante particular de desenvolver plugins para meus scripts. Este artigo pretende compartilhar o padrão de desenvolvimento que eu achei extremamente útil para a autoria de plugins. Ela parte do pressuposto que você já tenha conhecimento do desenvolvimento de plugins para jQuery. Se você um autor de plugin novato, por favor leia o Guia de autoria de plugins do jQuery (inglês, por enquanto 😉 )

Há poucos requerimentos que eu acho fazer um plugin se comportar bem:

  • Dê somente um nome no namespace do jQuery
  • Aceite um argumento de opções para controlar o comportamento do plugin
  • Provê acesso público para configurações de plugin padrões
  • Provê acesso público para funções secundárias (quando possível)
  • Mantenha funções privadas privadas

Eu analisei esses requerimentos um a um, e trabalhei em cima dele para construir um simples plugin que pisca o texto.

Dando um único nome no namespace do jQuery

Isto implica o único script para cada plugin. Se seu script contém múltiplos plugins, ou complementa outros plugins (como $.fn.facaAlgo() e $.fn.desfacaAlgo() ) então múltiplos nomes são necessários. Mas em geral quando estamos desenvolvendo um plugin, tente ao máximo usa somente um único nome para conter todos os detalhes da implementação.

No nosso plugin de exemplo nós iremos colocar o nome “hilight” .

//definicao do plugin

$.fn.hilight = function(){

//a implementacao do plugin vem aqui};

// executando o plugin em outro script

$("#myDiv").hilight();

Mas e se quebrarmos nossa implementação em mais de uma função? Há muitas razões para se fazer isto: o design pode requerer isto que pode resultar em uma implementação mais simples e mais versátil; e irá manipular melhor as semânticas da programação orientada a objeto.

É realmente trivial quebrar a implementação em múltiplas funções sem adicionar “sujeira” no namespace. Nós iremos fazer isto reconhecendo, e levando vantagem do fato de funções no javascript serem objetos da primeira classe. Como qualquer outro objeto, funções podem conter propriedades. Desde que nós já demos um nome para o “hilight” no objeto jQuery prototype, qualquer outra propriedade ou função que nós precisamos expor pode ser declarada como propriedade da função “hilight”. Mais sobre isto adiante.

Adicionar o argumento de opções para controlar o comportamento do plugin

Vamos adicionar suporte para nosso plugin hilight para especificar a cor de frente e de fundo para serem usadas. Nós deveremos permitir opções como estas passadas como um argumento de opções para a função do plugin. Por exemplo:

//definicao do plugin$.fn.hilight = function(options) {

var defaults = {

foreground: 'red';

background: 'yellow';

};};

//extendendo nossas opcoes

var opts = $.extend(defaults,options);

//implementacao do plugin

//seu plugin pode ser invocado assim:

$('#myDiv').hilight({

foreground: 'blue'

});

Provê acesso público para configurações padrões do plugin

Um aparfeiçoamento que podemos ter, e deveríamos, é fazer o código acima “expor” as configurações padrões do plugin. Isto é importante por que torna-se muito fácil para os usuários sobrescreverem / customizarem o plugin com código mínimo. E é assim que começaremos a tirar vantagem do objeto função.

//definicao do plugin

$.fn.hilight = function(options) {

//extendendo nossa opcao padrao

//note que o primeiro argumento

// e um objeto vazio, isto e para

// mante-lo de nao alterar o "defaults"

var opts = $.extend({},$.fn.hilight.defaults,options);

// implementacao do plugin vem aqui

};

//padrao do plugin$.fn.hilight.defaults = {

foreground: 'red';

background: 'yellow';

};//Agora usuários podem incluir

//uma linha como esta nos seus scripts

$.fn.hilight.defaults.foreground = 'blue';

//agora poderemos chamar o metodo

//do plugin e ele ficara com uma cor

// de frente azul por padrao

$('#myDiv').hightlight();

Como podemos ver, nós permitimos o usuário a escrever uma única linha de código para alterar a propriedade padrão da cor de frente para o plugin. E os usuários podem seletivamente sobrescrever este novo método padrão quando desejamos:

//sobrescrever a cor de frente padrao

$.fn.hilight.defaults.foreground = 'blue';

//invocando o plugin usando o padrao
$('.hilightDiv').hiligth();
//sobrescrevendo passando um options

$("#green").hiligth({

foreground: 'green';

});

Provê acesso público para acessar funções secundárias quando aplicável

Este item vai de mãos dadas com o item anterior e é uma maneira interessante de extender seu plugin (e para deixar outros estenderem seu plugin). Por exemplo, a implementação de seu plugin pode definir uma função chamada “format” que formata o texto com hilight. Nosso plugin pode agora se parecer assim, com a implementação padrão do método format definido abaixo da função hilight.

//definicao do plugin

$.fn.hilight = function(options) {

//percorrer e reformatar cada elemento encontrado
return this.each(function() {

var $this = $(this);

var markup = $this.html();

// call our format function

markup = $.fn.hilight.format(markup);

$this.html(markup);

});

};

//definir a funcao format

$.fn.hilight.format = function(txt) {

return '' + txt + '';

};

Nós poderíamos facilmente suportar outra propriedade no options object que permite uma função de callback para sobrescrever a formatação padrão. Isto é outro suporte excelente para customização de plugins. A técnica mostrada aqui vai um passo mais adiante expondo a função format para ser redefinida. Com esta técnica seria possível para outros fazerem seus próprios “sobrescrevidos” personalizados de seus plugins, eu outras palavras, significa que outros possam escrever plugins de seus plugins.
Considerando o exemplo trivial de plugin construído neste artigo, você pode estar se perguntando como isto deveria ser útil. Um exemplo do mundo real é o Cycle plugin. O Cycle plugin é um slideshow que suporta um número de bibliotecas implementadas como efeito de scroll, slide, fade, etc. Mas realisticamente, não há maneira de de definir cada único tipo de efeito que você gostaria de aplicar a uma transição slide. E que onde este tipo de versatilidade é útil. O Cycle Plugin expõe o objeto “transition” que permite usuários adicionem suas próprias transições customizadas.

$.fn.cycle.transitions = {

//implementacao aqui

}

Com esta técnica, torna-se possível para outros definirem e darem definições de transição “plugados” ao Cycle Plugin.

Manterem funções privadas, privadas!

A técnica de expor parte de seu plugin para serem sobrescritas podem ser muito poderosas. Mas se você precisa pensar cuidadosamente sobre que partes de sua implementação você deixaria exposto. Uma vez exposto, você precisa manter em mente que qualquer mudanças para os argumentos chamados ou semântica pode quebrar problemas de compatibilidade. Como regra geral, se você não tem certeza que função particular expor, você provavelmente não deveria.

Então como definir mais funções sem estragar o namespace e sem expor a implementação? Isto é uma tarefa para os “closures“. Para demonstrar, vamos adicionar outra função para nosso plugin chamada “debug”. A função debug irá logar o número de elementos selecionados para o console do firebug. Para criar um “closure“, nós escrevemos o plugin inteiro dentro de uma função.

(function($) {

$.fn.hilight = function(options){

debug(this);

}

};

//funcao privada para debugar

function debug($obj) {

if(window.console && window.console.log)

window.console.log('hilight selection count:' + $obj.size());

};

//fim do closure

})(jQuery);

Nosso método “debug” não pode ser acessado fora do “closure” e isto será uma implementação para o jQuery de métodos privados.

Este padrão de desenvolvimento tem permitido fazer que criemos mais poderosos e consistentes plugins. Eu espero que isto ajude a você fazer o mesmo.

16 Replies to “Um padrão de desenvolvimento para plugins do jQuery”

  1. Boa iniciativa alexandre, estou começando em jquery, e não sei bem como fazer um plugin, vejo muitas variantes e nenhuma bem explicada, no seu caso gostei muito, só que gostaria de ver ela inteira pronta, ou alguns exemplos prontos simples, tem alguma dica?

    Abraços!

  2. Muito obrigado Daniel,

    Estou preparando alguns plugins como tutorial, por enquanto este post é para dar um embasamento na teoria, já estou preparando algo prático. Abraços.

  3. Olá Alexandre

    Estou com um problema, mas não sei como fazer, extender um plugin existente, tipo no php:

    class A{
    function Test()
    {
    echo 1;
    }
    }

    class B extends A
    {
    function C()
    {
    }
    }

    queria algo similar a isso com o jquery plugins, se existe alguma possibilidade, me dê uma dica ou um exemplo.

    Seguindo o exemplo de criação de plugin, não consigo ver como seria possível, pois eu queria extender um plugin, para por eventos e não apenas métodos ou propriedades.

  4. @candido: A possibilidade de extensão do plugin depende muita da forma como foi feito e a concepção de orientação a objeto no javascript é bem diferente do PHP. Estender plugins existentes muitas vezes não é possível, mas muitos plugins permitem isto. Isto estará contido na documentação. Eventos tem o mesmo funcionamento de métodos. Espero que ajude, boa sorte, abraços…

  5. Eu segui o seu roteiro para criar uma plugin, mais quando aplico ele em mais de um objeto, ele reconhece somente o primeiro, os outros não funcionam, tem algum metodo para isso..

    Parabens pelo artigo

  6. Blz Alexandre parábens pelo artigo, muito esclarecedor.
    Deixa eu te perguntar uma coisinha, teria como criar um plugin que para ser chamado não fosse precisa chamar o metodo $()

    To criando um plugin para criar campos de formulario só que eu não queria chamar $(“”).campo()

    queria chamar assim obj.campo();

    Como eu faço para chamar um plugin sem ter que usar esse metodo( $() ) antes?

    muito obrigado

  7. Olá Alexandre,
    Ótimo artigo, era exatamente o que eu estava procurando.
    Apenas um comentário, mas quando no seu código onde esta “//padrao do plugin$.fn.hilight.defaults = {” não deveria ser “//padrao do plugin
    $.fn.hilight.defaults = {” (com a quebra de linha antes de “$.fn”)?

Deixe um comentário

O seu endereço de e-mail não será publicado.