Formulario inteligente con Javascript

Publicado: febrero 9, PM en Javascript
Etiquetas:, ,

Hoy vamos a ver cómo crear un formulario web que, además de estar validado, tenga la virtud de poder ser ampliado con muy poco esfuerzo y que todas sus funcionalidades sigan en marcha.
Primero aclarar que con HTML5 los campos de tipo entrada pueden validarse automáticamente con la propiedad required (para comprobar que no estén vacíos) o, por ejemplo en el caso de un campo de entrada de email, con type=”email” poder testar que se escribe una dirección de correo correctamente. Esto está bien si supiéramos de antemano que todos nuestros visitantes usan un navegador que soporta HTML5. Pero, como sabemos, esto, desgraciadamente, no es así en más de las ocasiones de las que imaginamos. Conozco gente que todavía usa Internet Explorer 7.
Dicho esto, vamos a confeccionar este formulario a la vieja usanza, es decir, validado con JavaScript. Así que no me enrollo más y comenzamos con el código del HTML.

<!DOCTYPE html>
<html>
<head>
	<meta http-equiv="Content-Type" content="text/html; charset=utf-8" /> 
	<title>Ejemplo POO Javascript</title>
	<link rel="stylesheet" type="text/css" href="css/estilo.css"/>
</head>


<body>

	<h1>Ejemplo POO Javascript</h1>

	<fieldset><legend>Formulario POO javascript</legend>
		<form id="formulario" method="POST" action="ejemplo.php">
			Nombre *:
				<br />
				<input class="negro" name="campo" type="text" id="nombre" />
				<br/>
				<br/>
			Email *:
				<br />
				<input  class="negro" name="campo" type="text" id="email"/>
				<br/>
				<br/>
			Teléfono:
				<br />
				<input  class="negro" name="telefono" type="text" id="telefono"/>
				<br/>
				<br/>
			Comentario *:
				<br/>
				<textarea class="negro" name="campo" id="comentario" ></textarea>
				<br/>
				<br/>
			<noscript>
				<input type="submit" id="enviar" value="Enviar"/>
			</noscript>
			<button type="button" id="enviar">Enviar</button>
			<button type="reset" id="borrar">Borrar</button>
		</form>
		<br />
		<p id="mensaje"></p>
		<br />
		<progress id="progreso" max="100" value="0">Progreso</progress>
	</fieldset>
</body>

<script type="text/javascript" src="js/script.js"></script>

</html>

Como vemos se trata de un formulario que contiene cuatro campos, tres obligatorios y uno opcional. El método action del form apunta a una supuesta página de envío que, en este ejemplo, solo está a modo de test para comprobar que el formulario funciona como debe hacerlo.
Lo realmente importante de aquí son los class, name e id de los campos así como, por supuesto, sus valores. Observamos que todos los campos obligatorios tienen el mismo class y el mismo name. Los id, sin embargo, hacen alusión al objetivo de cada uno de ellos. Bien, queda claro que el llamarles así ha sido de mi elección. Tú puedes llamarlos como quieras, eso sí, respetando que se repita en todos los campos obligatorios así como tenerlo en cuenta a la hora de ser referenciados desde nuestro archivo js, el cual, cargamos al final de nuestro archivo, antes del cierre.
Fuera del formulario tenemos una etiqueta de párrafo vacía con id mensaje que usaremos para ir informando al usuario si algo no está correcto. Además hemos puesto una barra de progreso que irá indicando visualmente qué tanto por ciento correcto se ha rellenado del formulario.
También hemos indicado en el head una hoja de estilos externa que veremos más adelante.
Vamos entonces paso a paso con nuestro archivo script.js, que lo tendremos dentro de una carpeta llamada js tal y como lo hemos indicado en el HTML.

//------añadir listeners sobre el evento onblur de cada campo del formulario
//------con nombre "campo"

var elementos = document.getElementsByName("campo");

// Recorremos todos los elementos
for (var i=0; i < elementos.length; i++) {

      // Añadimos el evento onblur a cada campo del formulario con nombre "campo"
	  //llamando a la funcion crearEvento que los añade dinámicamente
      crearEvento(elementos[i], "blur");
	  
}

//Esta función es llamada desde el bucle for anterior y añade dinámicamente
//los eventos a los elementos que se le pasan como argumento. En este caso todos
//los elementos con nombre "campo"
function crearEvento(elemento, evento) {
	
	if (elemento.addEventListener) {
	elemento.addEventListener(evento, 
		function(){var lanzando = new comprobarCampo(elemento);}, false);	  
	} 
	//-----Para IE
	else {
	elemento.attachEvent("on" + evento, 
		function(){var lanzando = new comprobarCampo(elemento);});
	}
}

Lo primero que hacemos es añadir dinámicamente un evento blur a todos los elementos de nuestro formulario que tengan el nombre campo que, como ya sabemos, son aquellos campos obligatorios de nuestro formulario. Para ello primero los guardamos en un array llamado elementos, y después los recorremos llamando a la función crearEvento pasándole como argumentos tanto el mismo elemento como el evento a escuchar. Esta función es la que se encarga de hacer el trabajo dinámicamente. Observamos que dentro de esta función tenemos un condicional ya que, Internet Explorer, como siempre, hace las cosas distintas a los demás navegadores. Internet Explorer no dispone de un método addEventListener, así que debemos hacerlo con attachEvent.
Con este evento controlaremos cuándo un usuario pasa de un campo a otro dentro del formulario y llamaremos a la función comprobarCampo pasándole como argumento el propio elemento.

//clase que comprueba el contenido del campo individual que se le pasa como
//argumento
var comprobarCampo = function(campo){
		
	this.campo = campo;
		
	if (this.campo.value == ""){
			
		ponerImagen(this.campo, "error");
				
				
	}
		
	else if (this.campo.id == "email" && 
	!(/^\w+([\.-]?\w+)*@\w+([\.-]?\w+)*(\.\w{2,3})+$/.test(this.campo.value)))
	{
		
		ponerImagen(this.campo, "error");
				
				
	}
		
	else{
		
		ponerImagen(this.campo, "ok");
				
	}

		progreso();
		mensajes();
}

La función comprobará que el campo que se le pasa no esté vacío y que, en caso de que sea el campo que contiene el email, compruebe la sintaxis de la dirección de correo a través de una expresión regular. Vemos que, tanto si se supera la comprobación como si no, se llama a una función ponerImagen pasando como argumentos el elemento campo en cuestión y un string que indica si ha ocurrido un error o todo es ok. Además llamamos consecutivamente a las funciones progreso y mensajes. Veámoslas.

var ponerImagen = function(campo,estado){

		this.campo = campo;
		this.estado = estado;
						
		var imagen = document.createElement("img");
		imagen.className = "image";
		
		if (estado == "error"){
			
			if(this.campo.nextSibling.className == "image"){			
				borrarImagenIndividual(this.campo.nextSibling);
				imagen.src = "img/error.png";
				this.campo.className = "rojo";
				this.campo.style.border = "2px solid red";
				var padre = this.campo.parentNode
				padre.insertBefore(imagen, this.campo.nextSibling);			
			}
			else{
				imagen.src = "img/error.png";
				this.campo.className = "rojo";
				this.campo.style.border = "2px solid red";
				var padre = this.campo.parentNode
				padre.insertBefore(imagen, this.campo.nextSibling);
			}
		
		}
		else{
		
			if(this.campo.nextSibling.className == "image"){			
				borrarImagenIndividual(this.campo.nextSibling);
				imagen.src = "img/ok.png";
				this.campo.className = "verde";
				this.campo.style.border = "2px solid green";
				var padre = this.campo.parentNode
				padre.insertBefore(imagen, this.campo.nextSibling);
			}
			else{
				imagen.src = "img/ok.png";
				this.campo.className = "verde";
				this.campo.style.border = "2px solid green";
				var padre = this.campo.parentNode
				padre.insertBefore(imagen, this.campo.nextSibling);
			}
					
		}

}

Esta función crea dinámicamente una etiqueta image a la que damos un nombre de clase llamado image. Dependiendo de si el string indica error o no, usará un png de 24×24 para mostrarlo al lado del campo input correspondiente. Las imágenes son de tu elección, y están alojadas dentro de una carpeta img como puede verse en la ruta. Para comprobar si el campo disponía anteriormente de una imagen a su lado usamos this.campo.nextSibling.className == “image”. Si ya existía una la borramos con la función borrarImagenIndividual pasándole el target en cuestión. Si no había ninguna entonces le añadimos la nueva que le corresponda.
Usamos un estilo dinámico para el contorno del input. Si hay error rojo, sino verde.
El código para borrarImagenIndividual es simplemente:

var borrarImagenIndividual = function(elemento){
	
	elemento.parentElement.removeChild(elemento);

}

Sigamos con las funciones progreso y mensajes.

var progreso = function(){

	this.elementos = document.getElementsByName("campo");
	this.porcentaje = Math.round((100 / elementos.length) * Math.pow(10, 2)) / Math.pow(10, 2);
	this.barra_progreso = document.getElementById("progreso");
	this.contador = 100;
		
	// Recorremos todos los elementos
	for (var i=0; i < elementos.length; i++) {	
				
		if (elementos[i].value == ""){			
			
			contador -= porcentaje;
				
		}		
		else if (elementos[i].id == "email" 
		&& !(/^\w+([\.-]?\w+)*@\w+([\.-]?\w+)*(\.\w{2,3})+$/.test(elementos[i].value))){
			
			contador -= porcentaje;
		
		}
		
		barra_progreso.value = contador;

	}	

}

La función progreso calcula dinámicamente el porcentaje de campos a rellenar en base a la cantidad de campos obligatorios del formulario. Ponemos una variable contador a 100, es decir, el total de la barra de progreso. A partir de ahí, recorriendo todos los elementos obligatorios del formulario, irá restando la variable porcentaje a la variable contador para determinar qué tanto por ciento está correctamente cumplimentado. El resultado se lo aplicamos a la barra de progreso que tenemos en nuestro HTML para que lo muestre visualmente.

var mensajes = function(){
	
	this.pMensaje = document.getElementById("mensaje");
	pMensaje.innerHTML = "";
	this.elementos = document.getElementsByName("campo");
	this.mensaje1 = "";
	this.mensaje2 = "";
	
	for (var i=0; i < elementos.length; i++) {		
		
		if (elementos[i].value == ""){			
			
			mensaje1 = "Por favor, rellene todos los campos obligatorios"; 
				
		}		
		else if (elementos[i].id == "email" && 
		!(/^\w+([\.-]?\w+)*@\w+([\.-]?\w+)*(\.\w{2,3})+$/.test(elementos[i].value))){
			
			mensaje2 = "Por favor, introduzca una dirección de email correcta";
		
		}

	}	

	pMensaje.innerHTML = mensaje1 + "<br />" + mensaje2;
}

La función mensajes se encargará de recorrer todos los elementos obligatorios del formulario y, en caso de error, añadir dinámicamente un texto informativo dentro de la etiqueta párrafo vacía con id mensaje que tenemos en nuestro HTML. Previamente limpiamos el contenido por si ya existiera alguno. Además, como cabe la posibilidad de que hayan varios mensajes distintos, declaramos una variable por cada comprobación y las concatenamos al final para mostrarlo.
Vamos ahora con el botón borrar de nuestro formulario:

//listener sobre el evento onclick del botón borrar formulario

if (document.getElementById("borrar").addEventListener){

	document.getElementById("borrar").addEventListener("click", function evento(){
		
		borrarTodasImagenes("image");
		
		var elementos = document.getElementsByName("campo");
		
		for (var i=0; i < elementos.length; i++) {
			
			elementos[i].className = "negro";
			elementos[i].style.border = "1px solid black";
		}

	} , true);

}
//-----Para IE
else if (document.getElementById("borrar").attachEvent){

	document.getElementById("borrar").attachEvent("onclick", function evento(){
	
		borrarTodasImagenes("image");
	
		var elementos = document.getElementsByName("campo");
		
		for (var i=0; i < elementos.length; i++) {
			
			elementos[i].className = "negro";
			elementos[i].style.border = "1px solid black";
		}
	
	});

}

Añadimos dinámicamente, como ya hicimos anteriormente, el evento click tanto para IE como para el resto de navegadores. Aparte de resetear el formulario, el botón devolverá el contorno negro a los input y se encargará de borrar todas las imágenes que hubieran de error u ok llamando a la función borrarTodasImagenes , pasando como argumento, en este caso, el nombre de clase que queremos eliminar (image).
La función borrarTodasImagenes quedaría así:

var borrarTodasImagenes = function(clase){

	this.clase = clase;

	var elementos = document.getElementsByClassName(this.clase);
	
	// Recorremos todos los elementos
	for(var k = elementos.length-1; k >= 0; --k){
		elementos[k].parentElement.removeChild(elementos[k]);
	}

}

Vamos ya con el botón enviar:

//listener sobre el evento onclick del botón enviar formulario

if (document.getElementById("enviar").addEventListener){

	document.getElementById("enviar").addEventListener("click", function evento(){
	
	var elementos = document.getElementsByName("campo");
	var lanzando = new comprobarTodo(elementos);
	
	

	} , true);

}
//-----Para IE
else if (document.getElementById("enviar").attachEvent){

	document.getElementById("enviar").attachEvent("onclick", function evento(){
	
	
	var elementos = document.getElementsByName("campo");
	var lanzando = new comprobarTodo(elementos);
	
	
	});

}

Igualmente asociamos el evento click al botón como hemos hecho hasta ahora, recuperamos todos los input obligatorios del formulario y se los pasamos como argumento a la función comprobarTodo.

//clase para comprobar todo el formulario al intentar enviarlo
var comprobarTodo = function(elementos){
	
	this.ok = true;
	this.elementos = elementos;
	borrarTodasImagenes("image");
	
	// Recorremos todos los elementos
	for (var i=0; i < elementos.length; i++) {
		
		if (elementos[i].value == ""){
			
			this.ok = false;
			ponerImagen(elementos[i], "error");
		
		
		}
		else if (elementos[i].id == "email" 
		&& !(/^\w+([\.-]?\w+)*@\w+([\.-]?\w+)*(\.\w{2,3})+$/.test(elementos[i].value))){
		
			this.ok = false;
			ponerImagen(elementos[i], "error");
		
		}
		else{
		
			ponerImagen(elementos[i], "ok");
		
		}		
	  
	}

	if (this.ok == true){
	
		document.getElementById("formulario").submit();
	
	}

}

Esta última función lo que hace es recorrer todos los input obligatorios y llamar a la función ponerIMagen tanto si está rellenado correctamente como si no. En el caso de que todo esté ok, el formulario será enviado.
Para terminar veamos nuestro CSS:

img {
	padding-left: 10px;
}
input{
	height:30px;
    font-size:14pt;
}

input,textarea{
	padding:10px;
	transition: all 0.15s ease-in-out;
	border-radius:3px;
	border:1px solid rgba(0,0,0,0.2);
}
#mensaje{
	font-weight:bold;
	color:red;
	font-size:10pt;
}
        
.rojo:focus {box-shadow: 0 0 5px rgba(255,0,0,1);border:1px solid rgba(255,0,0,0.8);}
.verde:focus {box-shadow: 0 0 5px rgba(0,255,0,1);border:1px solid rgba(0,255,0,0.8);}
.negro:focus {box-shadow: 0 0 5px rgba(178,166,167,1);border:1px solid rgba(0,0,0,0.8);}


progress[value] {
/* Eliminamos la apariencia por defecto */
-webkit-appearance: none;
-moz-appearance: none;
appearance: none;

/* Quitamos el borde que aparece en Firefox */
border: none;
border-radius:5px;

/* Aplicamos las dimensiones */
width: 250px;
height: 20px;

/* Aplicamos color a la barra */
color: green;
}

/* Compatibilidad de color en Firefox y Chrome */
progress::-moz-progress-bar { background: #00A693; }
progress::-webkit-progress-value { background: #00A693; }

Lo más importante a destacar en este archivo es el aspecto personalizado que le damos a nuestra barra de progreso así como las clases rojo, verde y negro que le van a dar al input un efecto glow muy atractivo cuando el foco esté sobre él. Estas clases, si repasamos nuestro código anterior, veremos que se aplican dinámicamente cuando un campo es erróneo, cuando es ok o cuando se resetea con el botón borrar respectivamente.
Pues ya está. Terminado.
Ahora podemos añadir todos los input obligatorios que queramos a nuestro formulario, teniendo en cuenta fundamentalmente que su name debe ser campo y su class negro. De esta forma, todas las funcionalidades de nuestro código JavaScript serán aplicadas automáticamente sin hacer nada más. Bien es cierto que en este ejemplo solo tenemos en cuenta la comprobación de que el campo no esté vacío y que, en el caso de ser el campo email, sea una dirección de correo correcta, pero añadir nuevas validaciones no es nada complicado dado cómo tenemos estructurado nuestro código. Es obvio que nuestro formulario es quizás demasiado redundante en sus validaciones (imágenes, textos, barra de progreso), pero puedes adaptarlo fácilmente a tus necesidades sin demasiado esfuerzo. Además debemos tener en cuenta que por muy inteligente que sea nuestro JavaScript, debemos validarlo en el PHP destino para evitar posibles inyecciones de código no deseadas. Pero eso lo dejo para vosotros 🙂

Anuncios

Responder

Introduce tus datos o haz clic en un icono para iniciar sesión:

Logo de WordPress.com

Estás comentando usando tu cuenta de WordPress.com. Cerrar sesión /  Cambiar )

Google+ photo

Estás comentando usando tu cuenta de Google+. Cerrar sesión /  Cambiar )

Imagen de Twitter

Estás comentando usando tu cuenta de Twitter. Cerrar sesión /  Cambiar )

Foto de Facebook

Estás comentando usando tu cuenta de Facebook. Cerrar sesión /  Cambiar )

Conectando a %s