Formularios, setFocus y tabIndex

Publicado por Iván Gajate el 3 de octubre de 2008 en AS2, AS3, Flash, Tutoriales

Un problema muy común a la hora de hacer un formulario en Flash es que estén los campos rellenos con un texto informativo de lo que hay que poner dentro, y que cuando el usuario hace clic cobre uno de los campos (recoge el foco), se borra esa información para que pueda escribir.

El problema viene cuando el campo deja de tener el foco pero el usuario no ha puesto nada. No se tiene que quedar en blanco, sino volver a poner el texto informativo del principio. Este es el resultado.

 

 

 

Para hacer esto lo que tenemos que hacer es guardar en una variable el contenido de cada campo de texto y al perder el foco comprobar si ha cambiado o está vacío, en cuyo caso pondremos el texto por defecto.

Como además este problema suele venir de la mano del tabIndex, para que se pueda pasar de un campo a otro con el tabulador, he hecho un pequeño script que lo gestiona todo de forma automática, tan solo hay que crear un array al principio indicándole los nombres de los campos de texto, y su texto por defecto.

En AS2 y línea de tiempo:

import mx.utils.Delegate;

// Array con los campos de texto en el orden que queramos que se tabulen
var campos_array:Array = new Array();

//ponemos los nombres de los campos a utilizar en orden de tabulación
campos_array[0] = {campo:campoNombre_txt, textoPorDefecto:"Nombre"};
campos_array[1] = {campo:campoApellidos_txt, textoPorDefecto:"Apellidos"};
campos_array[2] = {campo:campoTelefono_txt, textoPorDefecto:"Teléfono"};
campos_array[3] = {campo:campoEmail_txt, textoPorDefecto:"Email"};
//
var _numCampos = campos_array.length;
var _listenerTextos:Object = new Object();

// Asigno los EventListener y pongo el orden de tabulación a los campos de texto
function init():Void {
	_listenerTextos.onSetFocus = Delegate.create(this, onSetFocus);
	Selection.addListener(_listenerTextos);
	for (var i:Number = 0; i<_numCampos; i++) {
		campos_array[i].campo.text = campos_array[i].textoPorDefecto;
		campos_array[i].campo.tabEnabled = true;
		campos_array[i].campo.tabIndex = i+1;
	}
}

// Controlo el evento onSetFocus de los campos de texto 
function onSetFocus(oldFocus:Object, newFocus:Object):Void{
	// Si no hay campo con el foco, salgo
	if(newFocus != undefined && newFocus != null){
		// Limpio el campo de texto pulsado si tiene el texto predeterminado
		var n:Number = buscar(TextField(newFocus));
		if (newFocus.text == campos_array[n].textoPorDefecto) {
			newFocus.text = "";
		}
	}

	// Si no hay campo con el foco, salgo
	if(oldFocus != undefined && oldFocus != null){
		// Al perder el foco un campo de texto, si no se ha escrito nada en él, pongo el texto por defecto
		var n:Number = buscar(TextField(oldFocus));
		if (oldFocus.text == "") {
			oldFocus.text = campos_array[n].textoPorDefecto;
		}
	}
}

// Devuelve el índice de la matriz de campos que corresponda con el TextField proporcionado
function buscar(campo:TextField):Number {
	for (var i = 0; i<_numCampos; i++) {
		if (campos_array[i].campo == campo) {
			return i;
		}
	}
}
//
init();

 

Recomiendo meter los campos de texto y el código en un clip de película para evitar conflictos con otros campos o variables de nuestra película.

Está en AS2 y en línea de tiempo, tal vez lo pase a una clase externa y seguramente a AS3. Por exigencia expresa de Drus 😉 lo he pasado a AS3, y he mejorado algunas líneas utilizando el método forEach de AS3. También a AS2 con clases. Ahí queda:

 

En AS2 con clases:

import mx.utils.Delegate;

class es.yporqueno.utils.FocoFormularios {

	private var _campos_array:Array;
	private var _numCampos:Number;
	private var _listenerTextos:Object = new Object();

	public function FocoFormularios(campos:Array) {
		_campos_array = campos.slice();
		_numCampos = _campos_array.length;
		_listenerTextos = new Object();
		init();
	}

	// Asigno los EventListener y pongo el orden de tabulación a los campos de texto
	private function init():Void {
		_listenerTextos.onSetFocus = Delegate.create(this, onSetFocus);
		Selection.addListener(_listenerTextos);
		for (var i:Number = 0; i<_numCampos; i++) {
			_campos_array[i].campo.text = _campos_array[i].textoPorDefecto;
			_campos_array[i].campo.tabEnabled = true;
			_campos_array[i].campo.tabIndex = i+1;
		}
	}

	// Controlo el evento onSetFocus de los campos de texto 
	private function onSetFocus(oldFocus:Object, newFocus:Object):Void{
		// Si no hay campo con el foco, salgo
		if(newFocus != undefined && newFocus != null){
			// Limpio el campo de texto pulsado si tiene el texto predeterminado
			var n:Number = buscar(TextField(newFocus));
			if (newFocus.text == _campos_array[n].textoPorDefecto) {
				newFocus.text = "";
			}
		}

		// Si no hay campo con el foco, salgo
		if(oldFocus != undefined && oldFocus != null){
			// Al perder el foco un campo de texto, si no se ha escrito nada en él, pongo el texto por defecto
			var n:Number = buscar(TextField(oldFocus));
			if (oldFocus.text == "") {
				oldFocus.text = _campos_array[n].textoPorDefecto;
			}
		}
	}

	// Devuelve el índice de la matriz de campos que corresponda con el TextField proporcionado
	private function buscar(campo:TextField):Number {
		for (var i = 0; i<_numCampos; i++) {
			if (_campos_array[i].campo == campo) {
				return i;
			}
		}
	}
}

 

En AS3 y en línea de tiempo:

import flash.events.FocusEvent;
import flash.text.TextField;

// Gestiona los taborder de los campos de texto y comprueba los focos y sus textos
// Array con los campos de texto en el orden que queramos que se tabulen
var campos_array:Array = new Array();

// Ponemos los campos de texto en orden de tabulación
campos_array[0] = {campo:campoNombre_txt, textoPorDefecto:"Nombre"};
campos_array[1] = {campo:campoApellidos_txt, textoPorDefecto:"Apellidos"};
campos_array[2] = {campo:campoTelefono_txt, textoPorDefecto:"Teléfono"};
campos_array[3] = {campo:campoEmail_txt, textoPorDefecto:"Email"};

// Asigno los EventListener y pongo el orden de tabulación a los campos de texto
function init(item:*, index:uint, array:Array):void {
	item.campo.addEventListener(FocusEvent.FOCUS_IN, focoIn);
	item.campo.addEventListener(FocusEvent.FOCUS_OUT, focoOut);
	//
	var c:TextField = TextField(item.campo);
	c.text = item.textoPorDefecto;
	c.tabEnabled = true;
	c.tabIndex = index+1;
}
// Limpio el campo de texto pulsado si tiene el texto predeterminado
function focoIn(evento:FocusEvent):void {
	var campoNuevo:TextField = TextField(evento.currentTarget);
	var n:uint= buscar(campoNuevo);
	if (campoNuevo.text == campos_array[n].textoPorDefecto) {
		campoNuevo.text = "";
	}
}
// Al perder el foco un campo de texto, si no se ha escrito nada en él, pongo el texto por defecto
function focoOut(evento:FocusEvent):void {
	var campoViejo:TextField = TextField(evento.currentTarget);
	var n:uint= buscar(campoViejo);
	if (campoViejo.text == "") {
		campoViejo.text = campos_array[n].textoPorDefecto;
	}
}
// Devuelve el índice de la matriz de campos que corresponda con el TextField proporcionado
function buscar(campo:TextField):uint {
	var index:uint=0;
	for (var i:uint = 0; i<campos_array.length; i++) {
		if (campos_array[i].campo == campo) {
			index = i;
			break;
		}
	}
	return index;
}
//
campos_array.forEach(init);

 

Y en AS3 con clases:

package es.yporqueno.utils{

	import flash.events.FocusEvent;
	import flash.text.TextField;

	public class FocoFormularios {

		private var _campos_array:Array;
		private var _numCampos:uint;

		public function FocoFormularios(campos:Array) {
			_campos_array = campos.slice();
			_numCampos = _campos_array.length;
			_campos_array.forEach(init);
		}

		// Asigno los EventListener y pongo el orden de tabulación a los campos de texto
		private function init(item:*, index:uint, array:Array):void {
			item.campo.addEventListener(FocusEvent.FOCUS_IN, focoIn);
			item.campo.addEventListener(FocusEvent.FOCUS_OUT, focoOut);
			//
			var c:TextField = TextField(item.campo);
			c.text = item.textoPorDefecto;
			c.tabEnabled = true;
			c.tabIndex = index+1;
		}

		// Limpio el campo de texto pulsado si tiene el texto predeterminado
		private function focoIn(evento:FocusEvent):void {
			var campoNuevo:TextField = TextField(evento.currentTarget);
			var n:uint= buscar(campoNuevo);
			if (campoNuevo.text == _campos_array[n].textoPorDefecto) {
				campoNuevo.text = "";
			}
		}

		// Al perder el foco un campo de texto, si no se ha escrito nada en él, pongo el texto por defecto
		private function focoOut(evento:FocusEvent):void {
			var campoViejo:TextField = TextField(evento.currentTarget);
			var n:uint= buscar(campoViejo);
			if (campoViejo.text == "") {
				campoViejo.text = _campos_array[n].textoPorDefecto;
			}
		}

		// Devuelve el índice de la matriz de campos que corresponda con el TextField proporcionado
		private function buscar(campo:TextField):uint {
			var index:uint=0;
			for (var i:uint = 0; i<_campos_array.length; i++) {
				if (_campos_array[i].campo == campo) {
					index = i;
					break;
				}
			}
			return index;
		}
	}
}

 

Y se implementa asi, tanto en AS2 como AS3:

// Gestiona los taborder de los campos de texto y comprueba los focos y sus textos
import es.yporqueno.utils.FocoFormularios;

// Array con los campos de texto en el orden que queramos que se tabulen
var campos_array:Array = new Array();

//ponemos los nombres de los campos a utilizar en orden de tabulación
campos_array[0] = {campo:campoNombre_txt, textoPorDefecto:"Nombre"};
campos_array[1] = {campo:campoApellidos_txt, textoPorDefecto:"Apellidos"};
campos_array[2] = {campo:campoTelefono_txt, textoPorDefecto:"Teléfono"};
campos_array[3] = {campo:campoEmail_txt, textoPorDefecto:"Email"};
//
new FocoFormularios(campos_array);

Nota: Acordarse de incrustar las fuentes en los campos de texto, incluidos los acentos y caracteres especiales.

Descargar ejemplo en AS2 y línea de tiempo

Descargar ejemplo en AS y clases

Descargar ejemplo en AS3 y línea de tiempo

Descargar ejemplo en AS3 y clases

Descargar todo el paquete es.yporqueno

 

11 comentarios para “Formularios, setFocus y tabIndex”

  1. Hola GisKaRD
    esta clase no valida el formulario, tan solo se ocupa del tabulador. Antes de enviar los datos tendrás que validar si los campos no están rellenos de espacios, si los mails tienen un formato correcto, códigos postales…
    No le veo la utilidad a saber si son espacios o “aaa”, pero si te sirve de algo se podría hacer algo así:

    var cadena:String = "   ";
    
    trace(cadena.length);
    trace(cadena.split(" ").length-1);
    
    if(cadena.length == cadena.split(" ").length-1){
    	trace("Relleno con espacios");	
    }
    
  2. Bueno comentar que al postear el comentario la pagina me a eliminado los espacios que introduje en el codigo de ejemplo entre las comillas pero bueno que funciona aurrevoire

  3. 
    //Bueno solucionado aqui el sistema con expresiones regulares
    
    miTexto.text = "          ";
    
    trace(miTexto.length); //result  10
    
    miTexto.text = trim(miTexto.text);
    
    trace(miTexto.length); //result  0
    
    //tmb si el contenido de miTexto contubiera espacios en blanco delante o detras de un texto los quitaria 
    miTexto.text = "     Hola mundo        ";
    
    miTexto.text = trim(miTexto.text);
    
    trace(miTexto.text); //result  = "Hola mundo"
    
    //Aqui la funcion
    
    function trim(str:String):String {
    return str.replace(/^\s*(.*?)\s*$/g, "$1");
    }
    
  4. Hola bueno el ejemplo esta muy pero date cuenta de una cosa que es la que quiero evitar y es el motivo por el cual e terminado encontrando este post,

    haced CLIC dentro de un campo de texto del ejemplo de arriba y en vez de escribir algo presionar simplemente la barra espaciadora una vez y saliros del campo de texto
    ¿que paso?
    pues que en el campo de texto no hay nada pero en realidad hay un caracter en blanco o mas dependiendo de cuantas veces le demos a la barra espaciadora osea si le damos 3 veces al espacio y hacemos un

    trace(miCampoTexto.length); // nos devolvera = 3

    mi pregunta es la siguiente como se puede comprobar si el campo de texto esta vacio o si no lo esta comprobar si son caracteres en blanco, necesito encontrar una solucion porque este problemita podria causarme errores en unos textos que no se pueden enviar vacios

  5. a quedado claro. Saludos.

  6. Hola Oscar
    la variable c guarda una referencia al campo de texto para luego acceder más facilmente a él.
    Además, optimiza el rendimiento, pues no tiene que estar evaluando constantemente lo que es TextField(item.campo) y si me da por cambiar el nombre del campo a miCampo no tengo que cambiar todas las líneas donde he puesto item.campo, tan solo la primera, es más por esto 😉
    De hecho, debería estar lo primero, antes de los addEventListener.
    Me alegro que te guste 🙂

  7. Una clase muy clara y muy buen uso de las matrices en as3.
    una preguntita?

    donde pones:

    var c:TextField = TextField(item.campo);
    c.text = item.textoPorDefecto;
    c.tabEnabled = true;
    c.tabIndex = index+1;
    

    para que sirve la variable c?:

    item.campo.text = item.textoPorDefecto;
    item.campo.tabEnabled = true;
    item.campo.tabIndex = index+1;
    

    Muy buena pagina y todo muy bien explicado. Saludos.

  8. Pasado a AS3, la verdad es que ha quedado mejor. Muy clarito. Y con la clase muy fácil de implementar.

  9. Muy buena solución e implementación, aunque no lo hagas clase aún, ya estás tardando en pasarlo a AS3!!