Otro caso práctico: sliders

facebook-svg gplus-svg twitter-svg

Podemos utilizar variables CSS para dar estilo a los sliders (input type=range). Todavía necesitamos JavaScript, pero las variables simplifican mucho el código. Veamos como.

El HTML

En el HTML tengo un slider que puede tomar valores desde 0 hasta 100. El valor actual value = "50".

<input type="range" min="0" max="100" value="50">

El JavaScript

En JavaScript utilizamos el método setProperty() para recuperar el valor del slider ( input.value ) y guardar este valor en una variable CSS que llamamos --value.

var input = document.querySelector("input[type=range]");
input.style.setProperty("--value", input.value);

El evento "input" se dispara en seguida que el valor de un elemento <input> cambia. Podemos aprovechar este evento para cambiar el valor de la variable --value cada vez que el usuario mueve el cursor ( thumb ) del slider.

input.addEventListener("input", function(evt) {
   input.style.setProperty("--value", input.value);
  },false);

El CSS

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-stops situados muy cerca el uno del otro.

Para dar estilo al "track" o la barra de desplazamiento del slider vamos a crear un gradiente lineal de izquierda a la derecha ( to right ), de HotPink a negro.
Si por ejemplo el valor del atributo value = "50" ( el cursor se encuentra justo en el medio del slider ) tenemos que poner esta regla en el CSS para crear un limite claro al 50%:

background-image:linear-gradient(to right, HotPink 50%, black 50%));

Un pequeño secreto: Si en un gradiente la posición del segundo color-stop es menor que la posición del primer color-stop, el CSS considera que la posición del segundo color-stop coincide con la posición anterior, y por lo tanto crea un límite tajante. Así que podemos escribir:

background-image: linear-gradient(to right, HotPink 50%, black 0));

Esto crea un gradiente lineal de izquierda a la derecha ( to right ),  de hecho una imagen ( background-image ) mitad HotPink mitad negro.
Pero necesitamos que el limite del gradiente coincida siempre con la posición del thumb ( o el control del slider ). Para esto, en lugar de 50% vamos a utilizar el valor del atributo value, que hemos guardado en una variable CSS llamada --value.
Pero --value es un numero y la sintaxis nos pide un porcentaje. La solución es utilizar el método calc() y multiplicar la variable var(--value) * 1%.

background-image: linear-gradient(to right, HotPink calc(var(--value) * 1%), black 0));

Así es como queda el código:

  input[type=range]{
    width:20em;
    height:.5em;
    display:block;
    margin:5em auto;
    -webkit-appearance: none; /*en los navegadores de tipo -webkit- anula los estilos con los cuales los sliders vienen de fabrica.*/
    background-image:linear-gradient(to right, HotPink calc(var(--value)*1%), black 0);
  }

See the Pen Input type range + CSS vars # 0* by Gabi (@enxaneta) on CodePen.

Esta vez hemos tenido suerte. Pero no siempre los sliders tienen valores de 0 a 100. Probemos a utilizar un slider diferente.

<input type="range" min="30" max="170" step="1" value="90">

Las cosas ya no cuadran. El valor 50 ya no cae en el medio del slider. Así que en el javascript tenemos que recalcular la posición de los color-stop en función del valor de los atributos min y max del input:

var inputMin = input.getAttribute("min");
var inputMax = input.getAttribute("max");
var unidad = (inputMax - inputMin) / 100;
input.style.setProperty("--value", (input.value - inputMin)/unidad);  

Y para no repetir todo este código dos veces lo voy a poner en una función:

function actualizarInput(input) {
   var inputMin = input.getAttribute("min");
   var inputMax = input.getAttribute("max");
   var unidad = (inputMax - inputMin) / 100;
   input.style.setProperty("--value", (input.value - inputMin)/unidad);  
}

Véalo en codepen:

See the Pen Input type range + CSS vars #1* by Gabi (@enxaneta) on CodePen.

¡Pero no es así como se hace!

Claro que no. de hecho lo que tenemos que hacer es dar estilo a la barra de desplazamiento ( track ), y no a todo el slider.
Recuerde que para dar estilo al track  utilizamos estas otras reglas de estilo:

input[type=range]::-webkit-slider-runnable-track{}
input[type=range]::-moz-range-track{}

I en este caso utilizamos el gradiente que hemos creado antes como imagen de fondo para la barra de desplazamiento del slider:

input[type=range]::-webkit-slider-runnable-track {
    height: 3px;
    background:linear-gradient(to right, HotPink calc(var(--value)*1%), black 0);
}
input[type=range]::-moz-range-track {
    height: 3px;
    background:linear-gradient(to right, HotPink calc(var(--value)*1%), black 0);
  }

See the Pen Input type range + CSS vars #2* by Gabi (@enxaneta) on CodePen.

De paso he añadido una etiqueta <label> donde poner el valor del slider, y dos líneas de código en el JavaScript para actualizar el innerHTML de la etiqueta cada vez que se dispara el evento "input".

function actualizarInput(input){
     var label = input.parentElement.querySelector("label");
     label.innerHTML = input.value;
     var inputMin = input.getAttribute("min");
     var inputMax = input.getAttribute("max");
     var unidad = (inputMax - inputMin) / 100;
     input.style.setProperty("--value", (input.value - inputMin)/unidad);
}

Dos o más sliders

Muchas veces tenemos más de un slider en cada página. Para tomar esto en consideración necesitamos modificar el JavaScript por tal de iterar sobre todos los sliders de la página. Y no es nada complicado. Utilizamos la sentencia for...of para iterar sobre todos los sliders de la página.

for( input of document.querySelectorAll("input[type=range]")){
     actualizarInput(input); 
}

A continuación utilizamos el evento input para modificar el estilo de cada slider. Para apuntar a todos los sliders aplicamos addEventListener al documento ( document.addEventListener ) y para precisar cual de los inputs escucha al evento en un determinado momento utilizamos var input = evt.target. ( donde target = objetivo, meta; y evt representa al evento – "input" en este caso )

document.addEventListener("input", function(evt) {
     var input = evt.target;
     actualizarInput(input)
  });

See the Pen Input type range + CSS vars #3 by Gabi (@enxaneta) on CodePen.

Misión cumplida en 17 líneas de código JavaScript.