martes, 26 de mayo de 2009

Partiendo el "pegado" o "paste" en diferentes campos

Hace unos días se me asignó una tarea para corregir un fallo de validación de un formulario donde el usuario intenta pegar un número telefónico completo en el campo que solo recibe el código de área. El código ya restringía el campo a solo tres carácteres al ser digitados, y cuando se llegaba a este límite el cursor era puesto al siguiente campo del número telefónico. No obstante la validación solo funcionaba con el evento donde se teclea un caracter. Quize agregarle un plus además para que partiera todo el número telefónico en los tres campos del teléfono con solo pegar el número desde el código de área. Esto se asemeja a lo que hacen algunos paquetes de instalación de software donde el código serial del producto se tiene que digitar en varios campos, pero que permite hacer copy/paste si se tiene el código en un archivo de texto en una sola tira de caracteres.

Me puse a investigar como obtener el valor del "clipboard", es decir, el texto resultante de ejecutar un "copiar" o "copy" en el navegador. Como es usual en la diversidad de la web, para Internet Explorer existe un objeto "clipboardData" que hace esto mismo y más pero que no es compatible con otros navegadores como FireFox:

window.clipboardData.setData('Text', data);
window.clipboardData.getData('Text', data);


De hecho hay un debate existencial en si se debería permitir este libre acceso al clipboard por cuestiones de seguridad.

Surfeando por la web encontré una solución diferente que funciona en todos los navegadores.

La receta incluye los siguientes ingredientes:

+ evento: onPaste

+ window.setTimeout

+ Función para evaluar si un carácter es un digito (opcional al gusto)


function isDigit(num) {

var string="1234567890";
if (string.indexOf(num)!=-1){
return true;
}
return false;
}

+ Lógica para recorrer cada uno de los caracteres de la tira

Preparación:

+ Crear una función que será llamada en el evento onPaste que lo único que hace es establecer un timeout con la función que partira la tira
en los demás campos.


function managePhonePaste() {
window.setTimeout("checkPastedValue();", 10);
}


+ Programe la lógica para partir la tira y distribuirla en los demas campos. Opcionalmente puede agregar lógica para establecer donde debería quedar el cursor al final de la operación.


function checkPastedValue() {
var pastedValue = $('areacode').value;
var areaCode='';
var phone1='';
var phone2='';

for(i=0; i <= pastedValue.length; i++) {
if (isDigit(pastedValue.charAt(i))) {
if (areaCode.length < 3) {
areaCode += pastedValue.charAt(i);
} else if(phone1.length < 3) {
phone1 += pastedValue.charAt(i);
} else if (phone2.length < 4) {
phone2 += pastedValue.charAt(i);
}
}
}
if (areaCode.length > 0) {
$('areacode').value = areaCode;
$('areacode').focus();
}
if (phone1.length > 0) {
$('phone1').value = phone1;
$('phone1').focus();
}
if (phone2.length > 0) {
$('phone2').value = phone2;
$('phone2').focus();
}
}


+ Agregue la función al campo de código de área para el evento onPaste():


<input id="areacode" name="areaCode" value="" type="text" onPaste="managePhonePaste();">
<input id="phone1" name="phone1" value="" type="text">
<input id="phone2" name="phone2" value="" type="text">



Cómo funciona?

Lo que estamos haciendo es dejar que la tira se pegue en el campo del código de área aunque exceda el límite establecido, pero al establecer un timeout tan corto para manejar el valor que se pega y repartirlo apropiadamente en los demás, se tiene la impresión de que todo se hace de manera inmediata, lo cual es cierto para el ojo humano.