Criando um Plugin de Marca D’água com jQuery

, , • 15/09/2009

Neste tutorial você irá aprender a criar um plugin para jQuery que insere uma marca d’água em inputs do tipo text e textarea.

O primeiro passo é criar um arquivo javascript. Uma sugestão de nome é jquery.{nome do plugin}.js, então no meu caso vou nomear o arquivo jquery.watermark.js.

Várias bibliotecas/frameworks de javascript dão ao símbolo $ uma função especial, e isotp não é diferente para jQuery, então o primeiro passo é evitar que este conflito ocorra. Para isso devemos iniciar nosso plugin com uma função anônima autoexecutável, passando o parâmetro jQuery e transformando-o em $, como no código abaixo.

(function($){
//Todo o código que compõe o plugin deve ficar aqui.
})(jQuery);

Em seguida devemos mencionar que estamos iniciando um plugin, então escrevemos o seguinte

(function($){

	$.fn.watermark = function(){
		//Aqui vem os parâmetros e as funções que compõem o plugin.
	}

})(jQuery);

Agora devemos decidir se nosso plugin terá parâmetros ou não. Neste caso eu adicionarei alguns parâmetros, são eles:

  1. corMarcaDagua
  2. estiloMarcaDagua
  3. corTexto
  4. estiloTexto
  5. valorPadrao

estes parâmetros me darão a cor e o estilo da marca d’água, a cor e o estilo do texto que é digitado, e o valor padrão do input ou textarea que não tiver valor especificado.

(function($){

	$.fn.watermark = function(parametros){
		//Aqui vem os parâmetros e a funções que compõem o plugin.
	}

})(jQuery);

A variável parâmetros será um objeto passado para o plugin. Caso o usuário não passe nenhum valor o plugin deve estar preparado para usar valores padrões, por isso crio um objeto que será usado para este fim.

(function($){

	$.fn.watermark = function(parametros){
		var padroes = {
				 corMarcaDagua    : '#bbbbbb'
				,estiloMarcaDagua : 'italic'
				,corTexto         : '#000000'
				,estiloTexto      : 'normal'
				,valorPadrao      : 'pesquisar...'
			}

			//O plugin continua aqui...
	}

})(jQuery);

O plugin deve usar os valores padrões caso o usuário não passe nenhum valor, mas caso seja passado o plugin deve usar os valores do usuário. Para isto acontecer devemos estender os valores padrões e mescla-los com os valores do usuário. Abaixo vê-se como criar chegar a este fim

function($){

	$.fn.watermark = function(parametros){
		var  padroes = {
				 corMarcaDagua    : '#bbbbbb'
				,estiloMarcaDagua : 'italic'
				,corTexto         : '#000000'
				,estiloTexto      : 'normal'
				,valorPadrao      : 'pesquisar...'
			}

		padrao = $.extend({},padroes,parametros);

		//O plugin continua aqui...
	}

})(jQuery);

A função $.extend() tem 3 parâmetros. O primeiro é um objeto vazio, o segundo é a variável com os padrões do plugin, e o terceiro é o objeto passado pelo usuário. Repare como a variável padrao agora se torna os parâmetros que o usuário passar, ou assume os valores padrões que criamos, caso o usuário não passe nada.

O próximo passo é criar a(s) função(s) que deve ser executada para CADA(each) elemento que for atingido com o seletor de jQuery.

(function($){

	$.fn.watermark = function(parametros){
		var padroes = {
				 corMarcaDagua    : '#bbbbbb'
				,estiloMarcaDagua : 'italic'
				,corTexto         : '#000000'
				,estiloTexto      : 'normal'
				,valorPadrao      : 'pesquisar...'
			}

		padrao = $.extend({},padroes,parametros);

		return this.each(function(){
			//Todos os elementos que forem atingidos com
			//o seletor do jQuery sofrerão com o que vier
			//aqui dentro.
		});

	}

})(jQuery);

Repare que usei return antes de this.each(). Isto não é estritamente necessário, mas é altamente recomendável, para que nosso plugin permita chaining no jQuery.

No começo eu disse que o plugin seria limitado a inputs do tipo texto e textarea, porém o plugin ainda não sabe da minha intenção, então tenho que dizer explicitamente que quero isso. em jQuery usamos o método is() para descobrir com qual elementos estamos trabalhando.

(function($){

	$.fn.watermark = function(parametros){
		var padroes = {
				 corMarcaDagua    : '#bbbbbb'
				,estiloMarcaDagua : 'italic'
				,corTexto         : '#000000'
				,estiloTexto      : 'normal'
				,valorPadrao      : 'pesquisar...'
			}

		padrao = $.extend({},padroes,parametros);

		return this.each(function(){

			if($(this).is('input:text') || $(this).is('textarea') ){
				//Aqui ficarão as ações que serão executadas
				//em todos os input's e textarea's que forem
				//atingidos com nosso seletor de jQuery
			}

		});

	}

})(jQuery);

Repare que filtrei os inputs do tipo texto somente escrevendo ‘:text’, mas eu poderia alternativamente usar a sintaxe de CSS input[type=text].

até este momento usei o elemento $(this) mais de uma vez, então é uma boa ideia usar uma variável que tenha o mesmo valor dele, para armazena-lo em cache e tornar nosso script mais rápido. Vou chamar a variável de $elemento, para ficar claro que ele representará cada elemento. Também coloco um cifrão no começo, para que fique claro que estou mexendo com um objeto do jQuery.

(function($){

	$.fn.watermark = function(parametros){
		var padroes = {
				 corMarcaDagua    : '#bbbbbb'
				,estiloMarcaDagua : 'italic'
				,corTexto         : '#000000'
				,estiloTexto      : 'normal'
				,valorPadrao      : 'pesquisar...'
			}

		padrao = $.extend({},padroes,parametros);

		return this.each(function(){

			$elemento = $(this);

			if($elemento.is('input:text') || $elemento.is('textarea') ){
				//Aqui ficarão as ações que serão executadas
				//em todos os input's e textarea's que forem
				//atinigidos com nosso seletor de jQuery
			}

		});

	}

})(jQuery);

Quando iniciamos o plugin queremos que o valor padrão que aparece nos input’s e textarea’s esteja na forma de marca d’água, então devemos dar a ele a cor de marca d’água e o estilo de marca d’água. Para isso usamos o método css() em todos os elementos usando a variável $elemento.

(function($){

	$.fn.watermark = function(parametros){
		var  padroes = {
				 corMarcaDagua    : '#bbbbbb'
				,estiloMarcaDagua : 'italic'
				,corTexto         : '#000000'
				,estiloTexto      : 'normal'
				,valorPadrao      : 'pesquisar...'
			}

		padrao = $.extend({},padroes,parametros);

		return this.each(function(){

			$elemento = $(this);

			if($elemento.is('input:text') || $elemento.is('textarea') ){

				$elemento.css({
					 color     : padrao.corMarcaDagua
					,fontStyle: padrao.estiloMarcaDagua
				});

			}

		});

	}

})(jQuery);

Em seguida temos de descobrir uma maneira de guardar os valores padrões dos input’s e textarea’s. Escolhi guarda-lo no atributo rel, que será criado dinamicamente com o script.
Caso o input tenha um valor no HTML, o usaremos, caso contrário usaremos o que será passado ao plugin pelo usuário, se nada for passado usaremos o padrão do plugin(‘pesquisar…’).

(function($){

	$.fn.watermark = function(parametros){
		var  padroes = {
				 corMarcaDagua    : '#bbbbbb'
				,estiloMarcaDagua : 'italic'
				,corTexto         : '#000000'
				,estiloTexto      : 'normal'
				,valorPadrao      : 'pesquisar...'
			}

		padrao = $.extend({},padroes,parametros);

		return this.each(function(){

			$elemento = $(this);

			if($elemento.is('input:text') || $elemento.is('textarea') ){

				$elemento.css({
					 color     : padrao.corMarcaDagua
					,fontStyle : padrao.estiloMarcaDagua
				});

				vall = ($elemento.val() != '')?$elemento.val():padrao.valorPadrao;

				$elemento.val(vall).attr('rel',vall); 

				//O plugin continua aqui...

			}

		});

	}

})(jQuery);

Repare que o que vem após vall =(linha 25) nada mais é do que um if/else no modo compacto. O que vem antes da interrogação é a condição. o que vem entre os dois pontos e a interrogação é o valor retornado se a condição for verdadeira, e o que q vem depois dos dois pontos é o valor retornado se a condição for falsa. Nesse caso vall vai ser igual ao valor do elemento que está no HTML caso haja algum valor ($element.val() é diferente de vazio), e vall será igual ao valor passado pelo usuário ou o valor padrão do plugin, caso nenhum valor seja passado direto no HTML. Na linha 27 esse valor é passado para o elemento, e em seguida para o atributo rel.

Agora devemos criar a parte onde indicamos o que deve ocorrer quando o usuário entra ou sai do input/textarea. Primeiro vou criar a parte que indica o que deve ocorrer quando o usuário ENTRA no input/textarea.

Para não ficar reescrevendo todo o script, vou mostrar somente a parte onde há a interação com os elementos. Para indicar a ação de entrada no input/textarea usamos o evento focus, e nesta entrada executamos uma função.

$elemento.focus(function(){

});

Quando estamos dentro do focus o $(this) indica o elemento quando está entrando em foco, então não podemos confundir com o $(this) usado anteriormente. Para facilitar o entendimento vou criar uma variável chamada $elementoEmFoco e direciona-la ao $(this).

$elemento.focus(function(){
	$elementoEmFoco = $(this);
});

Quando entramos no input/textarea sempre haverá um valor assinalado (no tutorial em vídeo não me atentei a este detalhe, e acabei escrevendo uma parte redundante no código), e estamos seguros disto pois quando escrevermos o código para quando o cursor sair do input/textarea colocaremos um valor no mesmo. Então quando entramos queremos ter a cor e o estilo do texto do usuário, o que é criado nas linhas 3-6

$elemento.focus(function(){
	$elementoEmFoco = $(this);
	$elementoEmFoco.css({
		 color:padrao.corTexto
		,fontStyle:padrao.estiloTexto
	});
});

Por fim, se quando entrarmos no elemento ele tenha o valor igual ao que foi guardado no atributo rel, queremos que esse valor desapareça e que o input/textarea fique vazio, para que o usuário possa digitar seu texto.

$elemento.focus(function(){
	$elementoEmFoco = $(this);
	$elementoEmFoco.css({
		 color:padrao.corTexto
		,fontStyle:padrao.estiloTexto
	});

	if($elementoEmFoco.val() == $elementoEmFoco.attr('rel')){
		$elementoEmFoco.val('');
	}

});

Terminada a ação executada quando entramos no input/textarea temos de criar a ação que será executada quando sairmos do mesmo. Para isso usamos o método blur(). Inicialmente faremos o mesmo que fizemos anteriormente, executaremos uma função assim que sairmos do input/textarea, e criaremos uma variável que será igual ao $(this), que neste caso indica o elemento quando está saindo de foco.

$elemento.blur(function(){
	$elementoForaDeFoco = $(this);
});

Se quando sairmos do input/textarea seu valor seja igual a vazio ou seu valor seja igual ao valor do atributo rel, queremos que ele seja tratado como uma marca d’água, então ele deve ter a cor e o estilo da marca d’água.

$elemento.blur(function(){
	$elementoForaDeFoco = $(this);
	if($elementoForaDeFoco.val() == '' || $elementoForaDeFoco.val() == $elementoForaDeFoco.attr('rel')){
		$elementoForaDeFoco.css({
	 		 color     : padrao.corMarcaDagua
			,fontStyle : padrao.estiloMarcaDagua
		});
	}
});

Além disso queremos que o valor volte a ser o valor padrão do input/textarea caso o mesmo esteja vazio ou tenha o valor igual ao valor do atributo rel, então incluimos uma linha de código dentro do if.

$elemento.blur(function(){
	$elementoForaDeFoco = $(this);
	if($elementoForaDeFoco.val() == '' || $elementoForaDeFoco.val() == $elementoForaDeFoco.attr('rel')){
		$elementoForaDeFoco.css({
	 		 color     : padrao.corMarcaDagua
			,fontStyle : padrao.estiloMarcaDagua
		});

	$elementoForaDeFoco.val($elementoForaDeFoco.attr('rel'));

	}
});

O plugin termina aqui. Farei somente uma modificação no código, que é inicializar todas as variáveis no início do documento. Esta prática não é estritamente necessária, porém particularmente eu gosto de fazer assim. A seguir está todo o plugin, incluindo essas modificações que fiz.

(function($){

	$.fn.watermark = function(parametros){
		var  padrao
			,vall
			,$elemento
			,$elementoEmFoco
			,$elementoForaDeFoco
			,padroes = {
				 corMarcaDagua    : '#bbbbbb'
				,estiloMarcaDagua : 'italic'
				,corTexto         : '#000000'
				,estiloTexto      : 'normal'
				,valorPadrao      : 'pesquisar...'
			}

		padrao = $.extend({},padroes,parametros);

		return this.each(function(){

			$elemento = $(this);

			if($elemento.is('input:text') || $elemento.is('textarea') ){

				$elemento.css({
					 color     : padrao.corMarcaDagua
					,fontStyle: padrao.estiloMarcaDagua
				});

				vall = ($elemento.val() != '')?$elemento.val():padrao.valorPadrao;
				$elemento.val(vall).attr('rel',vall); 

				$elemento.focus(function(){
					$elementoEmFoco = $(this);
					$elementoEmFoco.css({
						 color:padrao.corTexto
						,fontStyle:padrao.estiloTexto
					});

					if($elementoEmFoco.val() == $elementoEmFoco.attr('rel')){
						$elementoEmFoco.val('');
					}

				});

				$elemento.blur(function(){
					$elementoForaDeFoco = $(this);
					if($elementoForaDeFoco.val() == '' || $elementoForaDeFoco.val() == $elementoForaDeFoco.attr('rel')){
						$elementoForaDeFoco.css({
					 		 color     : padrao.corMarcaDagua
							,fontStyle : padrao.estiloMarcaDagua
						});

					$elementoForaDeFoco.val($elementoForaDeFoco.attr('rel'));

					}
				});

			}

		});

	}

})(jQuery);

O plugin ,do jeito que está, tem 1565 bytes de tamanho, o que em princípio é considerado pequeno, porém é sempre uma boa ideia diminuir o máximo possível o tamanho dos arquivos que serão incluídos nas páginas. O próprio framework jQuery apresenta uma versão minimizada.

Para criar essa versão usarei um compressor chamado YUI Compressor. Este compressor comprimi arquivos de javascript e CSS. O primeiro passo é baixar o compressor, e para isso você deve ir à página de download e baixa-lo. Baixei a única versão disponível na data de criação deste tutorial 2.4.2.
Primeiro você deve extrair a pasta zipada para uma pasta de sua escolha, eu escolhi a pasta que meu plugin está. Em seguida você deve jogar o plugin pra dentro da pasta build, que está dentro da pasta yuicompressor-x.y.z(no meu caso x=2, y=4 e z=2). Eu não gosto de jogar meu arquivo diretamente, eu faço uma cópia e coloco a mesma lá dentro, para evitar que, por qualquer motivo, meu arquivo seja danificado.

pasta.jquery.watermark

Para executar o compressor você deve acessar a pasta onde os arquivos estão localizados, o modo mais simples é copiando o endereço (no meu caso é o destacado na imagem acima). Agora você deve abrir o prompt de comando, caso você esteja usando Windows vá em Iniciar>Executar(start>Run) , digite cmd e pressione ok.

cmd.jquery.watermark

Um prompt de comando surgirá na tela. digite cd “{endereço do arquivo em java e plugin}”, incluindo as aspas e pressione Enter. no meu caso digitarei:

cd "C:\Users\Eduardo\My Documents\Aptana Studio Workspace\watermark\yuicompressor-2.4.2\build"

Neste momento você deve estar na pasta onde seus arquivos estão. para criar o arquivo comprimido do plugin executamos o seguinte comando: java -jar {nome do arquivo em java da pasta}.jar {nome do arquivo a ser comprimido}.js -o {nome do arquivo de saída}.js. No meu caso ficou da seguinte maneira

java -jar yuicompressor-2.4.2.jar jquery.watermark.js -o jquery.watermark.min.js

Em poucos instantes um arquivo é criado na pasta, chamado jquery.watermark.min. O tamanho dele é 679 bytes, 56,6% menor que o arquivo original. Mas tome CUIDADO para não descartar o arquivo original! Sempre que você quiser modificar seu plugin, é nele que você vai mexer, e no fim você comprimi dinovo.

Para testar o seu plugin crie uma página em HTML como a seguinte

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
	<head>
		<style type="text/css">
			input,textarea{display:block;}
		</style>
		<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
		<title>Watermark Plugin</title>
	</head>
	<body>
		<input type="text" />
		<input type="text" value="nome"/>
		<input type="text" value="email"/>
		<textarea rows="7" cols="25">Digite sua mensagem</textarea>
	<script type="text/javascript" src="http://ajax.googleapis.com/ajax/libs/jquery/1.3.2/jquery.min.js"></script>
	<script type="text/javascript" src="jquery.watermark.min.js"></script>
	<script type="text/javascript">
		$(document).ready(function(){
			$('input,textarea').watermark();
		});
	</script>
	</body>
</html>

Não se esqueça que o arquivo minimizado deve estar na mesma página do documento em HTML para que o endereço do atributo src, e também não esqueça que o framework jQuery deve ser adicionado antes de todos os arquivos que contenham jQuery. Nesse caso em específico adicionei os scripts no fim da página(imediatamente antes do término do body), mas poderia ter sido no head.

Para executar o plugin selecionei todos os input’s e textarea’s da página (poderia selecionar somente uma parte) e executei o comando watermark(). Você pode modificar os valores padrões, e para isso basta escrever o objeto como parâmetro, da seguinte maneira

$(document).ready(function(){
	$('input,textarea').watermark({
		 corMarcaDagua:'#abc'
		,estiloMarcaDagua:'normal'
		,corTexto:'green'
		,estiloTexto:'italic'
		,valorPadrao:'digite seu texto...'
	});
});

Abra a página novamente e teste. Você verá que os estilos foram mudados, além do texto marca d’água que aparece nos input’s/textarea’s que não tem valor. Você não é obrigado a passar todos os valores, mas somente aqueles que interessam ser mudados.

Você pode testar o resultado final, ou baixar os arquivos, nos links abaixo:

Preview Online
Baixar Arquivos

Comentários

  1. sandalye16/09/2009

    tnahk you admin; perfect code

    Responder

  2. David CHC17/09/2009

    Ótimo Tutorial, muito bem detalhado, e de fácil compreensão. Dessa maneira vou ter que parar de fazer Video Aulas sobre Jquery, e fazer só de PHP, rs.

    abrs

    Responder

  3. Diogo Lopes17/09/2009

    Ainda não assisti, vou ver agora!
    mas já sei o que me espera, sempre tutoriais bons!

    Responder

  4. Tutorial City17/09/2009

    David CHC
    Que isso cara, de maneira nenhuma. Todos os tutoriais são válidos desde que sejam úteis. Tenho certeza que todo mundo que continuar assistindo aos seus tutoriais, sejam sobre PHP ou jQuery. ;)

    Abraços.

    Responder

  5. Ronaldo18/09/2009

    Essa aula foi show! Fazia tempo que eu queria aprender a fazer isso.

    Responder

  6. Walmir Neto29/09/2009

    Muito boa sua aula, mas não sei se aconteceu com vc
    mas quando eu clico no ‘input’ o nome some direitinho mas há vezes que por algum motivo quando acionado o ‘Blur’ ele toma o valor padrão ‘Pesquisar…’ no caso será que foi algo que fiz de errado

    Obrigado pela atenção e Até a proxima

    Responder

  7. Walmir Neto29/09/2009

    agora entedir é quando clico no input e atualizo a pagina ai ele aparece o valor padrão.
    mais uma obrigado pela atenção

    Responder

  8. Tutorial City29/09/2009

    @Walmir Neto
    O firefox e o Internet Explorer têm esse tipo de peculiaridade, ele se lembra do que você botou no ‘input’ imediatamente antes de recarregar a página. Se você recarregar a página quando o ‘input’ estiver vazio o navegador vai se lembrar e deixa-lo vazio quando recarregar, aí o plugin vê o ‘input’ vazio e coloca o valor padrão. Como exercício você pode tentar corrigir esse problema nesses dois navegadores (dê uma pesquisada em $.browser).

    Responder

  9. Diogo Lopes23/10/2009

    Quando sai o próximo tutorial?

    Responder

  10. Tutorial City23/10/2009

    @Diogo Lopes
    Vou tentar fazer nesse fim de semana.

    Abraços

    Responder

  11. Diogo Lopes13/11/2009

    Eae o blog acabou?

    Responder

  12. Tutorial City14/11/2009

    @Diogo Lopes
    Estou tendo problemas pra fazer novos tutoriais. Espero voltar o mais breve possível.
    Peço desculpas pela demora.

    Abraços

    Responder

  13. halı yıkama makinası05/12/2009

    Quando sai o próximo tutorial

    Responder

  14. Tutorial City13/12/2009

    @#13
    Estou com problemas e não estou podendo postar no momento,mas deixo claro que volto a criar novos tutoriais o mais breve possível!

    Abraços

    Responder

  15. Atila27/02/2010

    SHOW DE BOLA CARA, PARABENS. VOU SEMPRE ACESSAR AGORA!!!!

    Responder

  16. Amy18/04/2010

    Eae o blog acabou?

    Responder

  17. Tutorial City18/04/2010

    @Amy
    Não. O blog não acabou.

    Responder

  18. Diogo Matheus26/04/2010

    Parabéns, tutorial ficou excelente, muito fácil de entender.

    Responder

  19. Neto20/05/2010

    Parabéns pela didática!

    Responder

  20. Steve28/05/2010

    SHOW DE BOLA CARA, PARABENS. VOU SEMPRE ACESSAR AGORA!!!!

    Responder

  21. Dhuankles Castro30/06/2010

    salve salve manos.. parabens pelo plugin.. incrivel a sua video aula..

    Responder

Envie Seu Comentário