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
17 de febrero de 2010 a las 18:44
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í:
17 de febrero de 2010 a las 17:37
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
17 de febrero de 2010 a las 17:34
17 de febrero de 2010 a las 17:14
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
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
13 de febrero de 2009 a las 13:48
a quedado claro. Saludos.
13 de febrero de 2009 a las 13:22
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 🙂
13 de febrero de 2009 a las 12:41
Una clase muy clara y muy buen uso de las matrices en as3.
una preguntita?
donde pones:
para que sirve la variable c?:
Muy buena pagina y todo muy bien explicado. Saludos.
12 de octubre de 2008 a las 19:14
Pasado a AS3, la verdad es que ha quedado mejor. Muy clarito. Y con la clase muy fácil de implementar.
3 de octubre de 2008 a las 21:05
Muy buena solución e implementación, aunque no lo hagas clase aún, ya estás tardando en pasarlo a AS3!!