Input type range y javascript
Que queremos
Queremos crear varios controles deslizantes ( sliders ), cada uno con sus características, y queremos poder visualizar el valor de éstos en cada momento.
Que necesitamos
Primero tenemos que decidir donde poner los nuevos controles deslizantes:
En el HTML tenemos dos div
s donde queremos colocar los "sliders"
<div class="inputDiv i1" ></div>
<div class="inputDiv i2" ></div>
En JavaScript identificamos estos dos elementos ( .inputDiv.i1
y .inputDiv.i2
) como elementos padre:
var elementoPadre1 = document.querySelector(".inputDiv.i1"); var elementoPadre2 = document.querySelector(".inputDiv.i2");
Como que tendremos varios controles deslizantes, para poder manipularlos en grupo, creamos un array donde guardar los nuevos objetos creados.
var inputsRy = [];
Un elemento input type = "range"
tiene este aspecto en HTML:
<input type="range" value="35" min="0" max="100" autocomplete="off" step="1">
Así que si creamos un nuevo objeto input necesitamos una propiedad que contenga todos estos atributos:
function Input() { this.att = {}; this.att.type = "range"; this.att.value = 35; this.att.min = 0; this.att.max = 100; this.att.autocomplete = "off"; this.att.step = "1"; }
Asimismo necesitamos poder guardar una referencia hacia el elemento input ( this.input
) que vamos a crear, y otra referencia hacia el elemento output ( this.output
), donde el valor del input value
aparece en pantalla.
function Input() { this.att = {}; this.att.type = "range"; this.att.value = 35; this.att.min = 0; this.att.max = 100; this.att.autocomplete = "off"; this.att.step = "1"; this.input; this.output; }
Crear un nuevo objeto input
También tenemos que escribir una función que crea un nuevo elemento <input>
y el div .output
.
this.crear = function(elementoPadre) { // crea un nuevo elemento input this.input = document.createElement("input"); // para cada propiedad del objeto att for (var name in this.att) { if (this.att.hasOwnProperty(name)) { // establece un nuevo atributo del elemento input this.input.setAttribute(name, this.att[name]); } } // crea un nuevo elemento div this.output = document.createElement("div"); // establece el valor del atributo class del nuevo div this.output.setAttribute("class","output"); // y el contenido (innerHTML) de este this.output.innerHTML = this.att.value; // inserta los dos elementos creados al final del elemento padre elementoPadre.appendChild(this.input); elementoPadre.appendChild(this.output); }
La función actualizar
Finalmente necesitamos una función que actualice el contenido ( innerHTML
) del div .output
, y el valor de this.att.value
.
this.actualizar = function(){ this.output.innerHTML = this.input.value; this.att.value = this.input.value; }
Crear un nuevo elemento input
Ahora crear un nuevo "slider" es fácil. Solo necesitamos crear un nuevo objeto Input
var i = new Input();
Para crear un nuevo slider llamamos la función crear
i.crear(elementoPadre1);
Y guardamos el nuevo objeto input en un array.
inputsRy.push(i);
También podemos modificar el valor de algunos atributos del input de esta manera:
var i2 = new Input(); i2.att.value = 70; i2.att.min = 20; i2.att.max = 120; i2.crear(elementoPadre2); inputsRy.push(i2);
El evento input
El evento input se dispara en seguida que el valor de un elemento <input>
o <textarea>
cambia.
A continuación aprovechamos este evento para cambiar el valor que aparece en cada elemento .output, y como que tenemos un array de controles deslizantes con sus elementos .output, utilizamos un bucle for ( for loop
) para iterar sobre este array. Y cada vez que se dispara el evento input para uno de este sliders actualizamos el HTML del .output
.
for (var n = 0; n < inputsRy.length; n++) {
(function(n) {
inputsRy[n].input.addEventListener("input", function() {
inputsRy[n].actualizar();
}, false)
}(n));
}
Ahora al arrastrar los controles deslizantes, el valor que aparece dentro del elemento .output
esta siendo actualizado según corresponda.
Vea este ejemplo en codepen:
See the Pen Input type range object by Gabi (@enxaneta) on CodePen.
Lea más acerca de addEventListener
Actualizar el CSS
Podemos mejorar todo esto dando un color diferente al "track" ( la barra de desplazamiento ) antes y después del "thumb" ( el botón o el control de la barra de desplazamiento ).
Para dar estilos diferentes a las varias partes del "track" el Internet Explorer nos lo pone muy fácil:
Para la parte baja ( lower ), antes del botón:
input[type=range]::-ms-fill-lower {
background-color: HotPink
}
Para la parte alta ( upper ), después del botón:
input[type=range]::-ms-fill-upper {
background-color: black;
}
Para los demás navegadores lo tenemos más difícil. Básicamente podemos utilizar para la barra de dezplazamiento un gradiente lineal que va de "HotPink
" a "black
" por ejemplo. Sabemos que un gradiente de color es una transición suave y progresiva entre dos o más colores. No tiene por que ser así. Podemos conseguir límites tajantes utilizando color-stop
s situados muy cerca el uno del otro. Es lo que haremos a continuación.
Si por ejemplo el "thumb" se encuentra justo en el medio del slider ( al 50% ) tendremos que poner estas reglas en el CSS:
#id::-webkit-slider-runnable-track{ background-image:-webkit-linear-gradient(left, HotPink 50%,Black 50%); } #id::-moz-range-track{ background-image:-moz-linear-gradient(left, HotPink 50%,Black 50%) }
El problema es que tendremos que actualizar esto cada vez que se dispara el evento input
( cada vez que alguien mueve el thumb o sea el botón del slider ). La solución es crear un nuevo elemento <style>
en el <head>
del documento, y manipular el contenido de este vía JavaScript.
// una nueva hoha de estilo var nuevaHojaDeEstilo = document.createElement("style"); document.head.appendChild(nuevaHojaDeEstilo); // una cadena de texto donde guardar los estilos var HojaCSS = "";
Ahora viene lo importante. Para manipular los elementos <style>
tenemos que escribir un nuevo método de Input
: el método CSSstyle()
. Este método establece las nuevas reglas CSS de cada slider ( this.style
)
this.CSSstyle = function() { // calcula la posición del thumb en porcentajes this.porcentaje = ((this.att.value - this.att.min) / this.interval) * 100; // establece las nuevas reglas CSS, // prácticamente las mismas reglas de arriba, // donde hemos reemplazado los elementos destacados en rojo con variables this.style = "#" + this.att.id + "::-webkit-slider-runnable-track{ background-image:-webkit-linear-gradient(left, HotPink " + this.porcentaje + "%, black " + this.porcentaje + "%)}"; this.style += "#" + this.att.id + "::-moz-range-track{ background-image:-moz-linear-gradient(left, HotPink " + this.porcentaje + "%, black " + this.porcentaje + "%)}"; }
Llamamos el nuevo método CSSstyle()
al final del método crear()
para establecer los estilos iniciales de los sliders.
this.crear = function(elementoPadre) {
this.input = document.createElement("input");
this.output = document.createElement("div");
this.output.innerHTML = this.att.value;
this.output.setAttribute("class", "output");
for (var name in this.att) {
if (this.att.hasOwnProperty(name)) {
this.input.setAttribute(name, this.att[name]);
}
}
elementoPadre.appendChild(this.input);
elementoPadre.appendChild(this.output);
this.CSSstyle()
}
También lo llamamos al final del método actualizar()
. De esta manera cada vez que cambia la posición del thumb actualizamos también los estilos del track ( la barra de dezplazamiento ).
this.actualizar = function() {
this.output.innerHTML = this.input.value;
this.att.value = this.input.value;
this.CSSstyle();
}
Casi lo tenemos. Lo que queda por hacer es escribir una nueva función que pone juntas todas las nuevas reglas CSS en una sola cadena de texto y modifica el contenido ( textContent
) de la nuevaHojaDeEstilo.
function actualizarCSS() {
// una cadena de texto donde guardar los estilos
var HojaCSS = "";
for (var i = 0; i < inputsRy.length; i++) {
HojaCSS += inputsRy[i].style;
}
nuevaHojaDeEstilo.textContent = HojaCSS;
}
for (var n = 0; n < inputsRy.length; n++) {
(function(n) {
inputsRy[n].input.addEventListener("input", function() {
inputsRy[n].actualizar();
actualizarCSS();
}, false);
}(n));
}
Vea este ejemplo en codepen:
See the Pen Input type range object #3 by Gabi (@enxaneta) on CodePen.
Veamos otra posibilidad
En el CSS podemos establecer como imagen de fondo un gradiente lineal, por ejemplo:
background-image:linear-gradient(90deg,red 0%,yellow 300px);
Y después podemos utilizar la propiedad background-size para especificar el tamaño de la imagen de fondo. Si por ejemplo el "thumb
" se encuentra justo en el medio del slider ( al 50% ) tendremos que poner estas reglas en el CSS:
#id::-webkit-slider-runnable-track{ background-size:50% 100%;} #id::-moz-range-track{ background-size:50% 100%}
Ahora podemos utilizar esta idea y reescribir el método CSSstyle()
. Este método establece las nuevas reglas CSS de cada slider ( this.style
)
this.CSSstyle = function() { // calcula la posición del thumb en porcentajes this.porcentaje = ((this.att.value - this.att.min) / this.interval) * 100; // establece las nuevas reglas CSS // prácticamente las mismas reglas de arriba, // donde hemos reemplazado los elementos destacados en rojo con variables this.style = "#" + this.att.id + "::-webkit-slider-runnable-track{ background-size:" + this.porcentaje + "% 100%;}"; this.style += "#" + this.att.id + "::-moz-range-track{ background-size:" + this.porcentaje + "% 100%}"; }
Y ya está. Todo lo demás queda como en el ejemplo anterior.
Vealo en codepen:
See the Pen Input type range object #4 by Gabi (@enxaneta) on CodePen.