RESUMEN:
Proporciono aquí una capacidad de escritorio y móvil de navegador cruzado sin jQuery para responder consistentemente a las interacciones de rango / control deslizante, algo que no es posible en los navegadores actuales. Básicamente obliga a todos los navegadores a emular el on("change"...
evento de IE11 para sus eventos on("change"...
o para ellos on("input"...
. La nueva función es ...
function onRangeChange(r,f) {
var n,c,m;
r.addEventListener("input",function(e){n=1;c=e.target.value;if(c!=m)f(e);m=c;});
r.addEventListener("change",function(e){if(!n)f(e);});
}
... donde r
está su elemento de entrada de rango y f
es su oyente. Se llamará al oyente después de cualquier interacción que cambie el valor del rango / control deslizante, pero no después de las interacciones que no cambian ese valor, incluidas las interacciones iniciales con el mouse o el toque en la posición actual del control deslizante o al salir de cualquier extremo del control deslizante.
Problema:
A principios de junio de 2016, los diferentes navegadores difieren en términos de cómo responden al uso del rango / control deslizante. Cinco escenarios son relevantes:
- mouse-down inicial (o touch-start) en la posición actual del control deslizante
- mouse-down inicial (o touch-start) en una nueva posición del control deslizante
- cualquier movimiento posterior del mouse (o toque) después de 1 o 2 a lo largo del control deslizante
- cualquier movimiento posterior del mouse (o toque) después de 1 o 2 más allá de cualquier extremo del control deslizante
- mouse-up final (o touch-end)
La siguiente tabla muestra cómo al menos tres navegadores de escritorio diferentes difieren en su comportamiento con respecto a cuál de los escenarios anteriores responden:
Solución:
La onRangeChange
función proporciona una respuesta coherente y predecible entre navegadores a las interacciones de rango / control deslizante. Obliga a todos los navegadores a comportarse de acuerdo con la siguiente tabla:
En IE11, el código esencialmente permite que todo funcione según el status quo, es decir, permite que el "change"
evento funcione de la manera estándar y el "input"
evento es irrelevante ya que nunca se dispara de todos modos. En otros navegadores, el "change"
evento se silencia efectivamente (para evitar que se disparen eventos adicionales y, a veces, no aparentes). Además, el "input"
evento activa su escucha solo cuando cambia el valor del rango / control deslizante. Para algunos navegadores (por ejemplo, Firefox) esto ocurre porque el oyente está silenciado efectivamente en los escenarios 1, 4 y 5 de la lista anterior.
(Si realmente necesita que se active un oyente en los escenarios 1, 4 y / o 5, puede intentar incorporar "mousedown"
/ "touchstart"
, "mousemove"
/ "touchmove"
y / o "mouseup"
/ "touchend"
eventos. Tal solución está más allá del alcance de esta respuesta).
Funcionalidad en navegadores móviles:
He probado este código en navegadores de escritorio pero no en ningún navegador móvil. Sin embargo, en otra respuesta en esta página, MBourne ha demostrado que mi solución aquí "... parece funcionar en todos los navegadores que pude encontrar (escritorio de Win: IE, Chrome, Opera, FF; Android Chrome, Opera y FF, iOS Safari) " . (Gracias MBourne)
Uso:
Para usar esta solución, incluya la onRangeChange
función del resumen anterior (simplificado / minimizado) o el fragmento de código de demostración a continuación (funcionalmente idéntico pero más explicativo) en su propio código. Invocarlo de la siguiente manera:
onRangeChange(myRangeInputElmt, myListener);
donde myRangeInputElmt
está el <input type="range">
elemento DOM deseado y myListener
es la función de escucha / controlador que desea invocar en "change"
eventos similares.
Su oyente puede no tener parámetros si lo desea o puede usar el event
parámetro, es decir, cualquiera de los siguientes funcionaría, dependiendo de sus necesidades:
var myListener = function() {...
o
var myListener = function(evt) {...
(La eliminación del detector de eventos del input
elemento (p. Ej., El uso removeEventListener
) no se aborda en esta respuesta).
Descripción de demostración:
En el fragmento de código a continuación, la función onRangeChange
proporciona la solución universal. El resto del código es simplemente un ejemplo para demostrar su uso. Cualquier variable que comience my...
es irrelevante para la solución universal y solo está presente por el bien de la demostración.
Los espectáculos de demostración el valor de rango / deslizante, así como el número de veces que el estándar "change"
, "input"
y la costumbre "onRangeChange"
eventos han disparado (filas A, B y C respectivamente). Al ejecutar este fragmento en diferentes navegadores, tenga en cuenta lo siguiente mientras interactúa con el rango / control deslizante:
- En IE11, los valores en las filas A y C cambian en los escenarios 2 y 3 anteriores, mientras que la fila B nunca cambia.
- En Chrome y Safari, los valores en las filas B y C cambian en los escenarios 2 y 3, mientras que la fila A cambia solo para el escenario 5.
- En Firefox, el valor en la fila A cambia solo para el escenario 5, la fila B cambia para los cinco escenarios y la fila C cambia solo para los escenarios 2 y 3.
- En todos los navegadores anteriores, los cambios en la fila C (la solución propuesta) son idénticos, es decir, solo para los escenarios 2 y 3.
Código de demostración:
// main function for emulating IE11's "change" event:
function onRangeChange(rangeInputElmt, listener) {
var inputEvtHasNeverFired = true;
var rangeValue = {current: undefined, mostRecent: undefined};
rangeInputElmt.addEventListener("input", function(evt) {
inputEvtHasNeverFired = false;
rangeValue.current = evt.target.value;
if (rangeValue.current !== rangeValue.mostRecent) {
listener(evt);
}
rangeValue.mostRecent = rangeValue.current;
});
rangeInputElmt.addEventListener("change", function(evt) {
if (inputEvtHasNeverFired) {
listener(evt);
}
});
}
// example usage:
var myRangeInputElmt = document.querySelector("input" );
var myRangeValPar = document.querySelector("#rangeValPar" );
var myNumChgEvtsCell = document.querySelector("#numChgEvtsCell");
var myNumInpEvtsCell = document.querySelector("#numInpEvtsCell");
var myNumCusEvtsCell = document.querySelector("#numCusEvtsCell");
var myNumEvts = {input: 0, change: 0, custom: 0};
var myUpdate = function() {
myNumChgEvtsCell.innerHTML = myNumEvts["change"];
myNumInpEvtsCell.innerHTML = myNumEvts["input" ];
myNumCusEvtsCell.innerHTML = myNumEvts["custom"];
};
["input", "change"].forEach(function(myEvtType) {
myRangeInputElmt.addEventListener(myEvtType, function() {
myNumEvts[myEvtType] += 1;
myUpdate();
});
});
var myListener = function(myEvt) {
myNumEvts["custom"] += 1;
myRangeValPar.innerHTML = "range value: " + myEvt.target.value;
myUpdate();
};
onRangeChange(myRangeInputElmt, myListener);
table {
border-collapse: collapse;
}
th, td {
text-align: left;
border: solid black 1px;
padding: 5px 15px;
}
<input type="range"/>
<p id="rangeValPar">range value: 50</p>
<table>
<tr><th>row</th><th>event type </th><th>number of events </th><tr>
<tr><td>A</td><td>standard "change" events </td><td id="numChgEvtsCell">0</td></tr>
<tr><td>B</td><td>standard "input" events </td><td id="numInpEvtsCell">0</td></tr>
<tr><td>C</td><td>new custom "onRangeChange" events</td><td id="numCusEvtsCell">0</td></tr>
</table>
Crédito:
Si bien la implementación aquí es en gran medida mía, se inspiró en la respuesta de MBourne . Esa otra respuesta sugirió que los eventos de "entrada" y "cambio" podrían fusionarse y que el código resultante funcionaría en los navegadores de escritorio y móviles. Sin embargo, el código en esa respuesta da como resultado eventos "extra" ocultos que se disparan, lo que en sí mismo es problemático, y los eventos disparados difieren entre los navegadores, un problema adicional. Mi implementación aquí resuelve esos problemas.
Palabras clave:
Los eventos del control deslizante de rango de tipo de entrada de JavaScript cambian la compatibilidad del navegador de entrada entre navegadores de escritorio móvil no-jQuery
onchange
no dispara. Fue en la solución de problemas que encontré esta pregunta.