| |

Laboratorio: control input selector para HTML

Uno de los controles que he visto en alguna aplicación (sobre todo de tipo editor gráfico), es aquella en la que el valor numérico de una caja de texto se puede modificar mediante botones y mediante una barra de progreso.
input-selector.png
La versión que he realizado modifica los input type=”text” que haya en la página que tengan el atributo rel el valor selector[min,max], siendo min y max los valores mínimos y máximos que permite el controlador.

Entre otras cosas, el controlador permite modificar el valor mediante la forma tradicional (usando la caja de texto), usando las flechas, pinchando en la barra de progreso o haciendo drag en la misma barra.

Lo más complicado es la creación de los elementos que forman el control (sobre todo que en IE aparezca correctamente). Tenemos que coger el input e introducir la barra de progreso antes de este y los botones después.

La barra de progreso consta de dos capas, una que tan solo es el recuadro y que es la que captura los eventos y otra que se haya debajo de esta y que es la que cambia de ancho para mostrar el progreso. ¿Por qué la barra tiene que estar antes que la caja de texto?, porque le pondremos el position con valor absolute y así la caja de texto y la barra empezarán en la misma posición horizontal, y tan solo tendremos que bajar la barra para que aparezca debajo de la caja de texto.

La barra de progreso tendrá en cuenta cuatro eventos: onclick para cambiar el valor de la barra cuando se pinche sobre ella, tomando el ancho correspondiente teniendo en cuenta que el ancho total de la barra se divide entre el número de valores posibles (max-min); y también controlará los eventos onmousedown, onmouseup y onmousemove para conseguir cambiar el progreso de la barra arrastrando el ratón sobre ella.

Las flechas para aumentar o disminuir el valor no tienen mucha complicación, su colocación una sobre otra se realiza mediante estilos. La única cosa un tanto “extraña”, es que los enlaces contienen imagenes transparentes, y que las flechas se muestran mediante el background del enlace, cambiando de color
de la flecha mediante el hover y la parte del background que queremos mostrar. aumentar.gifdisminuir.gif Supongo que se podría hacer de forma más limpia, pero esta es la que se me ha ocurrido y así se puede controlar todo por estilos.

Cuando se cambia el valor de la caja de texto manualmente se actualiza la barra de progreso, cuando se usan las flechas se actualiza la barra de progreso y el valor de la caja de texto y cuando se cambia el valor de la barra de progreso se actualiza el valor de la caja de texto.

Los estilos necesarios son los siguientes:

span.contenedor-controles-input-selector {
position: absolute;
}
span.contenedor-input-selector {
position: relative;
width: 10px;
height: 12px;
background: red;
}
a.aumentar-input-selector {
position: relative;
top: 5px;
margin-left: 2px;
width: 10px;
height: 6px;
padding: 0px;
font-size: 1px;
margin-right: 4px;
}
a.aumentar-input-selector img {
width: 10px;
height: 6px;
background: url(aumentar.gif) no-repeat;
padding: 0px;
margin: 0px;
border: 0px;
vertical-align: top;
}
a.aumentar-input-selector:hover img {
background: url(aumentar.gif) no-repeat 0px -6px;
}
a.disminuir-input-selector {
position: absolute;
margin-top: 13px;
margin-left: -14px;
}
a.disminuir-input-selector img {
width: 10px;
height: 6px;
background: url(disminuir.gif) no-repeat;
padding: 0px;
margin: 0px;
border: 0px;
vertical-align: top;
}
a.disminuir-input-selector:hover img {
background: url(disminuir.gif) no-repeat 0px -6px;
}
div.barra-input-selector {
top: 0px;
height: 2px;
border: 1px solid #69AADD;
}
div.progreso-input-selector {
height: 2px;
border: 1px;
background: #69AADD;
font-size: 1px; /* IE necesita esto para que tenga el tamaño correcto */
}

El código javascript necesario es el siguiente:

var ancho_minimo = "20px"; // Del input type="text"
// Navegadores
var _IE_ = navigator.appName.toLowerCase().indexOf("microsoft") > -1;
var _MOZILLA_ = navigator.appName.toLowerCase().indexOf("netscape") > -1;
// Selecciona todos los input:text y les añade
// un control para modificar el valor numérico
// que consiste en flechas arriba/abajo y
// una barra
function modificar_input_selector(evt) {
if (document.getElementsByTagName) {
// Obtengo los elementos INPUT
var objs = document.getElementsByTagName("INPUT");
// Los modifico para incluir el selector a los que tenga el atributo rel = "selector"
for (var i=0; i<objs.length; i++) {
if (objs[i].getAttribute("rel") != null &&
objs[i].getAttribute("rel").toLowerCase().match(/^selector(\[\-?\s*\d+\s*,\s*\-?\s*\d+\])?$/) &&
objs[i].type.toLowerCase() == "text") {
// Creamos id si no lo tiene
if (!objs[i].id) {
objs[i].id = Math.random();
}
// Si no hay ancho lo ponemos
if (!objs[i].style.width) {
objs[i].style.width = ancho_minimo;
}
// Hayamos el máximo y el mínimo
var params = objs[i].getAttribute("rel").replace(/(^selector\[)|(\]$)/g, "").split(",");
var min = parseInt(params[0]);
var max = parseInt(params[1]);
if (max<min) {
max = min;
}
objs[i].max = max;
objs[i].min = min;
// Le añadimos al objeto input el evento onchange
objs[i].onchange = function() {
if (!isNaN(parseInt(this.value))) {
// Si es numérico
if (parseInt(this.value) < this.min) {
// Si es más pequeño que el mínimo toma este valor
this.value = this.min;
} else if (parseInt(this.value) > this.max) {
this.value = this.max;
}
} else {
// Si no es un número se devuelve el valor mínimo
this.value = min;
}
//this.barra.firstChild.style.width = this.barra.incremento*(parseInt(this.value)-min)+"px";
};
// Empezamos con la barra
// Lo incluimos dentro de una capa
var contenedor = document.createElement("SPAN");
contenedor.className = "contenedor-controles-input-selector";
var barra = document.createElement("DIV");
barra.className="barra-input-selector";
// Contiene otra capa que muestra el progreso
var progreso = document.createElement("DIV");
progreso.className="progreso-input-selector";
progreso.innerHTML = "&nbsp;"; // IE necesita esto para que tenga el tamaño correcto
progreso.style.width = "0px";
// Le añadimos atributos para luego poder hacer referencias
barra.max = max;
barra.min = min;
barra.cajaTexto = objs[i];
// Añadimos el nodo con el progreso
barra.appendChild(progreso);
// El ancho de la barra depende del ancho de la caja de texto
barra.style.width = (parseInt(objs[i].style.width.replace("px", ""))+(_IE_?18:15))+"px";
// La posición vertical depende del navegador y el alto que le da por defecto a la caja de texto
// Se deberia configurar
barra.style.marginTop = (_IE_)? "22px":"21px";
// La proporcion de barra de progreso que le corresponde a cada unidad de la caja de texto
barra.incremento = parseInt(barra.style.width.replace("px", ""))/(max-min);
// Por si se modifica el valor arrastrando la barra de progreso
barra.drag = false;
// Al pinchar en la barra su valor cambia según la posición del ratón
barra.onclick = function(e) {
this.drag = false;
if (!e) {
e = window.event;
}
var x = (_IE_?e.offsetX:e.layerX);
pos = (x<3)? 0: parseInt(x*(this.max-this.min)/this.style.width.replace("px", ""))+1;
this.firstChild.style.width = (pos*this.incremento)+"px";
this.cajaTexto.value = pos+this.min;
}
// Si se está haciendo un drag, se actua igual que en el click
barra.onmousemove = function(e) {
if (this.drag) {
if (!e) {
e = window.event;
}
var x = (_IE_?e.offsetX:e.layerX);
pos = (x<3)? 0: parseInt(x*(this.max-this.min)/this.style.width.replace("px", ""))+1;
// No pasarnos del valor máximo
if (pos+this.min <= this.max) {
this.firstChild.style.width = (pos*this.incremento)+"px";
this.cajaTexto.value = pos+this.min;
}
}
}
// Empieza el drag
barra.onmousedown = function(e) {
this.drag = true;
}
// Acaba el drag
barra.onmouseup = function(e) {
this.drag = false;
}
// Se añade la barra al contenedor
contenedor.appendChild(barra);
objs[i].barra = barra;
// Se añade el contenedor antes de la barra
objs[i].parentNode.insertBefore(contenedor, objs[i]);
// Aumentar
// Se crea el enlace
var a = document.createElement("A");
a.id = objs[i].id+"_AUMENTAR";
a.href="#";
// Se le añaden atributos para luego poder hacer referencia
a.cajaTexto = objs[i];
a.max = max;
a.min = min;
a.barra = barra;
// No se usa click por si se quiere que vaya
// cambiando mientras se tiene pulsado el boton del raton
a.onmousedown = function() {
// Si es numero
if (!isNaN(parseInt(this.cajaTexto.value))) {
// Si es mas pequeño que el valor máximo
if (parseInt(this.cajaTexto.value) < this.max) {
// Si es menor que el valor minimo se pone este
if (parseInt(this.cajaTexto.value) < this.min) {
this.cajaTexto.value = this.min;
} else {
// Se incrementa el valor en 1
this.cajaTexto.value = parseInt(this.cajaTexto.value)+1;
}
} else {
// Se pone el valor máximo si es mayor que este
this.cajaTexto.value = this.max;
}
} else {
// Si no es numerico se pone el valor minimo
this.cajaTexto.value = this.min;
}
// Modificamos el progreso
var progreso = this.barra.firstChild;
progreso.style.width = this.barra.incremento*(parseInt(this.cajaTexto.value)-this.min)+"px";
// Se repite el evento mientras se tenga pulsado el raton
this.idEvento = setTimeout('document.getElementById("'+this.id+'").onmousedown()', 200);
};
// Se deja de repetir el evento al soltar el boton del raton
a.onmouseup = function() {
clearTimeout(this.idEvento);
}
// Las flechas usan una imagen transparente para
// poder usar un background y hacer el efecto hover
// usando una única imágen
a.innerHTML = '<img src="sp.gif" alt=""/>';
a.className="aumentar-input-selector";
// Incluyo la imagen despues de la caja de texto
objs[i].parentNode.insertBefore(a, objs[i].nextSibling);
// Me quedo con este objeto para luego
// incluir a continuación de este la otra flecha
var a_aumentar = a;
// Disminuir
// Se crea el enlace
a = document.createElement("A");
a.id = objs[i].id+"_DISMINUIR";
a.href="#";
// Se le añaden atributos para luego poder hacer referencia
a.cajaTexto = objs[i];
a.max = max;
a.min = min;
a.barra = barra;
// No se usa click por si se quiere que vaya
// cambiando mientras se tiene pulsado el boton del raton
a.onmousedown = function(evt) {
// Si es numero
if (!isNaN(parseInt(this.cajaTexto.value))) {
// Si es mas pequeño que el valor máximo
if (parseInt(this.cajaTexto.value) > this.min) {
// Si es menor que el valor minimo se pone este
if (parseInt(this.cajaTexto.value) > this.max) {
this.cajaTexto.value = this.max;
} else {
// Se incrementa el valor en 1
this.cajaTexto.value = parseInt(this.cajaTexto.value)-1;
}
} else {
// Se pone el valor máximo si es mayor que este
this.cajaTexto.value = this.min;
}
} else {
// Si no es numerico se pone el valor minimo
this.cajaTexto.value = this.min;
}
// Modificamos el progreso
var progreso = this.barra.firstChild;
progreso.style.width = this.barra.incremento*(parseInt(this.cajaTexto.value)-this.min)+"px";
// Se repite el evento mientras se tenga pulsado el raton
this.idEvento = setTimeout('document.getElementById("'+this.id+'").onmousedown()', 200);
};
// Se deja de repetir el evento al soltar el boton del raton
a.onmouseup = function() {
clearTimeout(this.idEvento);
}
// Las flechas usan una imagen transparente para
// poder usar un background y hacer el efecto hover
// usando una única imágen
a.innerHTML = '<img src="sp.gif" alt=""/>';
a.className="disminuir-input-selector";
// Incluyo la imagen despues de la primera flecha
objs[i].parentNode.insertBefore(a, a_aumentar.nextSibling);
}
}
}
}

Ejemplo

Código

Similar Posts