onchange event on input type = range no se activa en Firefox mientras se arrastra


Cuando jugué con <input type="range">, Firefox activa un evento onchange solo si soltamos el control deslizante a una nueva posición donde Chrome y otros activan eventos onchange mientras se arrastra el control deslizante.

¿Cómo puedo hacer que suceda al arrastrar en Firefox?

HTML

<span id="valBox"></span>
<input type="range" min="5" max="10" step="1" onchange="showVal(this.value)">

SCRIPT

function showVal(newVal){
  document.getElementById("valBox").innerHTML=newVal;
}
Author: Prasanth K C, 2013-08-31

7 answers

Aparentemente Chrome y Safari están equivocados: onchange solo deben activarse cuando el usuario suelta el ratón. Para obtener actualizaciones continuas, debe usar el evento oninput, que capturará actualizaciones en vivo en Firefox, Safari y Chrome, tanto desde el ratón como desde el teclado.

Sin embargo, oninput no es compatible con IE10, por lo que su mejor apuesta es combinar los dos controladores de eventos, así:

<span id="valBox"></span>
<input type="range" min="5" max="10" step="1" 
   oninput="showVal(this.value)" onchange="showVal(this.value)">

Echa un vistazo a este hilo Bugzilla para obtener más información.

 368
Author: Frederik,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2013-09-28 13:02:39

ACTUALIZACIÓN: Dejo esta respuesta aquí como un ejemplo de cómo usar eventos de ratón para usar interacciones de rango/control deslizante en navegadores de escritorio (pero no móviles). Sin embargo, ahora también he escrito una respuesta completamente diferente y, creo, mejor en otra parte de esta página que utiliza un enfoque diferente para proporcionar una solución de escritorio entre navegadores-y-mobile para este problema.

Respuesta original:

Resumen: Un navegador cruzado, solución de JavaScript simple (es decir, sin jQuery) para permitir la lectura de valores de entrada de rango sin usar on('input'... y/o on('change'... que funcionan de manera inconsistente entre navegadores.

A partir de hoy (finales de febrero, 2016), todavía hay inconsistencia navegador por lo que estoy proporcionando un nuevo trabajo-por aquí.

El problema: Cuando se utiliza una entrada de rango, es decir, un control deslizante, on('input'... proporciona valores de rango continuamente actualizados en Mac y Windows Firefox, Chrome y Opera, así como Mac Safari, mientras que on('change'... solo informa el valor de rango al subir el ratón. Por el contrario, en Internet Explorer (v11), on('input'... no funciona en absoluto, y on('change'... se actualiza continuamente.

Informo aquí 2 estrategias para obtener informes de valores de rango continuo idénticos en todos los navegadores utilizando JavaScript vainilla (es decir, sin jQuery) mediante el uso de los eventos mousedown, mousemove y (posiblemente) mouseup.

Estrategia 1: Más corta pero menos eficiente

Si prefiere un código más corto sobre un código más eficiente, puede usar este 1er solución que utiliza mousesdown y mousemove pero no mouseup. Esto lee el control deslizante según sea necesario, pero continúa disparando innecesariamente durante cualquier evento de ratón sobre, incluso cuando el usuario no ha hecho clic y, por lo tanto, no está arrastrando el control deslizante. Esencialmente lee el valor del rango tanto después de' mousedown 'como durante los eventos' mousemove', retrasando ligeramente cada uno usando requestAnimationFrame.

var rng = document.querySelector("input");

read("mousedown");
read("mousemove");
read("keydown"); // include this to also allow keyboard control

function read(evtType) {
  rng.addEventListener(evtType, function() {
    window.requestAnimationFrame(function () {
      document.querySelector("div").innerHTML = rng.value;
      rng.setAttribute("aria-valuenow", rng.value); // include for accessibility
    });
  });
}
<div>50</div><input type="range"/>

Estrategia 2: Más larga pero más eficiente

Si necesita más código eficiente y puede tolerar una longitud de código más larga, a continuación, puede utilizar la siguiente solución que utiliza mousedown, mousemove y mouseup. Esto también lee el control deslizante según sea necesario, pero deja de leerlo adecuadamente tan pronto como se suelta el botón del ratón. La diferencia esencial es que solo comienza a escuchar 'mousemove 'después de' mousedown', y deja de escuchar' mousemove 'después de'mouseup'.

var rng = document.querySelector("input");

var listener = function() {
  window.requestAnimationFrame(function() {
    document.querySelector("div").innerHTML = rng.value;
  });
};

rng.addEventListener("mousedown", function() {
  listener();
  rng.addEventListener("mousemove", listener);
});
rng.addEventListener("mouseup", function() {
  rng.removeEventListener("mousemove", listener);
});

// include the following line to maintain accessibility
// by allowing the listener to also be fired for
// appropriate keyboard events
rng.addEventListener("keydown", listener);
<div>50</div><input type="range"/>

Demostración: Explicación más completa de la necesidad de, and implementation of, the above work-arounds

El siguiente código demuestra más plenamente numerosos aspectos de esta estrategia. Las explicaciones están incrustadas en la demostración:

var select, inp, listen, unlisten, anim, show, onInp, onChg, onDn1, onDn2, onMv1, onMv2, onUp, onMvCombo1, onDnCombo1, onUpCombo2, onMvCombo2, onDnCombo2;

select   = function(selctr)     { return document.querySelector(selctr);      };
inp = select("input");
listen   = function(evtTyp, cb) { return inp.   addEventListener(evtTyp, cb); };
unlisten = function(evtTyp, cb) { return inp.removeEventListener(evtTyp, cb); };
anim     = function(cb)         { return window.requestAnimationFrame(cb);    };
show = function(id) {
	return function() {
    select("#" + id + " td~td~td"   ).innerHTML = inp.value;
    select("#" + id + " td~td~td~td").innerHTML = (Math.random() * 1e20).toString(36); // random text
  };
};

onInp      =                  show("inp" )                                      ;
onChg      =                  show("chg" )                                      ;
onDn1      =                  show("mdn1")                                      ;
onDn2      = function() {anim(show("mdn2"));                                   };
onMv1      =                  show("mmv1")                                      ;
onMv2      = function() {anim(show("mmv2"));                                   };
onUp       =                  show("mup" )                                      ;
onMvCombo1 = function() {anim(show("cmb1"));                                   };
onDnCombo1 = function() {anim(show("cmb1"));   listen("mousemove", onMvCombo1);};
onUpCombo2 = function() {                    unlisten("mousemove", onMvCombo2);};
onMvCombo2 = function() {anim(show("cmb2"));                                   };
onDnCombo2 = function() {anim(show("cmb2"));   listen("mousemove", onMvCombo2);};

listen("input"    , onInp     );
listen("change"   , onChg     );
listen("mousedown", onDn1     );
listen("mousedown", onDn2     );
listen("mousemove", onMv1     );
listen("mousemove", onMv2     );
listen("mouseup"  , onUp      );
listen("mousedown", onDnCombo1);
listen("mousedown", onDnCombo2);
listen("mouseup"  , onUpCombo2);
table {border-collapse: collapse; font: 10pt Courier;}
th, td {border: solid black 1px; padding: 0 0.5em;}
input {margin: 2em;}
li {padding-bottom: 1em;}
<p>Click on 'Full page' to see the demonstration properly.</p>
<table>
  <tr><th></th><th>event</th><th>range value</th><th>random update indicator</th></tr>
  <tr id="inp" ><td>A</td><td>input                                </td><td>100</td><td>-</td></tr>
  <tr id="chg" ><td>B</td><td>change                               </td><td>100</td><td>-</td></tr>
  <tr id="mdn1"><td>C</td><td>mousedown                            </td><td>100</td><td>-</td></tr>
  <tr id="mdn2"><td>D</td><td>mousedown using requestAnimationFrame</td><td>100</td><td>-</td></tr>
  <tr id="mmv1"><td>E</td><td>mousemove                            </td><td>100</td><td>-</td></tr>
  <tr id="mmv2"><td>F</td><td>mousemove using requestAnimationFrame</td><td>100</td><td>-</td></tr>
  <tr id="mup" ><td>G</td><td>mouseup                              </td><td>100</td><td>-</td></tr>
  <tr id="cmb1"><td>H</td><td>mousedown/move combo                 </td><td>100</td><td>-</td></tr>
  <tr id="cmb2"><td>I</td><td>mousedown/move/up combo              </td><td>100</td><td>-</td></tr>
</table>
<input type="range" min="100" max="999" value="100"/>
<ol>
  <li>The 'range value' column shows the value of the 'value' attribute of the range-type input, i.e. the slider. The 'random update indicator' column shows random text as an indicator of whether events are being actively fired and handled.</li>
  <li>To see browser differences between input and change event implementations, use the slider in different browsers and compare A and&nbsp;B.</li>
  <li>To see the importance of 'requestAnimationFrame' on 'mousedown', click a new location on the slider and compare C&nbsp;(incorrect) and D&nbsp;(correct).</li>
  <li>To see the importance of 'requestAnimationFrame' on 'mousemove', click and drag but do not release the slider, and compare E&nbsp;(often 1&nbsp;pixel behind) and F&nbsp;(correct).</li>
  <li>To see why an initial mousedown is required (i.e. to see why mousemove alone is insufficient), click and hold but do not drag the slider and compare E&nbsp;(incorrect), F&nbsp;(incorrect) and H&nbsp;(correct).</li>
  <li>To see how the mouse event combinations can provide a work-around for continuous update of a range-type input, use the slider in any manner and note whichever of A or B continuously updates the range value in your current browser. Then, while still using the slider, note that H and I provide the same continuously updated range value readings as A or B.</li>
  <li>To see how the mouseup event reduces unnecessary calculations in the work-around, use the slider in any manner and compare H and&nbsp;I. They both provide correct range value readings. However, then ensure the mouse is released (i.e. not clicked) and move it over the slider without clicking and notice the ongoing updates in the third table column for H but not&nbsp;I.</li>
</ol>
 27
Author: Andrew Willems,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2018-03-16 11:32:40

Estoy publicando esto como una respuesta porque merece ser su propia respuesta en lugar de un comentario bajo una respuesta menos útil. Encuentro este método mucho mejor que la respuesta aceptada, ya que puede mantener todos los js en un archivo separado del HTML.

Respuesta proporcionada por Jamrelian en su comentario bajo la respuesta aceptada.

$("#myelement").on("input change", function() {
    //do something
});

Solo tenga en cuenta este comentario de Jaime aunque

Solo tenga en cuenta que con esta solución, en Chrome obtendrá dos llamadas al controlador (uno por evento), así que si te preocupas por eso, entonces necesitas protegerte de ello.

Como en él se disparará el evento cuando haya dejado de mover el ratón, y luego de nuevo cuando suelte el botón del ratón.

 25
Author: Daniel Tonon,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2015-10-15 22:54:52

RESUMEN:

Ofrezco aquí una capacidad de escritorio y móvil sin jQuery para responder consistentemente a las interacciones de rango / control deslizante, algo que no es posible en los navegadores actuales. Esencialmente obliga a todos los navegadores a emular el evento on("change"... de IE11 para sus eventos on("change"... o on("input".... La nueva función...

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 es su elemento de entrada de rango y f es su oyente. El oyente será llamado después de cualquier interacción eso cambia el valor del rango / control deslizante, pero no después de las interacciones que no cambian ese valor, incluidas las interacciones iniciales con el ratón o el tacto en la posición actual del control deslizante o al moverse fuera de cualquiera de los extremos del control deslizante.

Problema:

A principios de junio de 2016, los diferentes navegadores difieren en términos de cómo responden al uso de rango/control deslizante. Cinco escenarios son relevantes:

  1. inicial del ratón hacia abajo (o toque-inicio) en el control deslizante actual posición
  2. inicial del ratón hacia abajo (o toque de inicio) en una nueva posición de control deslizante
  3. cualquier movimiento posterior del ratón (o táctil) después de 1 o 2 a lo largo del control deslizante
  4. cualquier movimiento posterior del ratón (o toque) después de 1 o 2 después de cualquiera de los extremos del control deslizante
  5. 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 a:

tabla que muestra las diferencias del navegador con respecto a qué eventos responden y cuándo

Solución:

La función onRangeChange proporciona una respuesta cruzada consistente y predecible a las interacciones de rango/control deslizante. Obliga a todos los navegadores a comportarse de acuerdo con la siguiente tabla:

tabla que muestra el comportamiento de todos los navegadores que utilizan la solución propuesta

En IE11, el código esencialmente permite que todo funcione según el status quo, es decir, permite que el evento "change" funcione en su forma estándar y el evento "input" es irrelevante ya que nunca dispara de todos modos. En otros navegadores, el evento "change" se silencia efectivamente (para evitar que se disparen eventos adicionales y a veces no aparentes). Además, el evento "input" dispara su oyente solo cuando cambia el valor del rango/control deslizante. Para algunos navegadores (por ejemplo, Firefox) esto ocurre porque el oyente se silencia efectivamente en los escenarios 1, 4 y 5 de la lista anterior.

(Si realmente requiere que se active un oyente en cualquiera de los escenarios 1, 4 y/o 5, puede probar incorporando "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 (Win desktop: IE, Chrome, Opera, FF; Android Chrome, Opera y FF, iOS Safari)". (Gracias MBourne.)

Uso:

Para usar esta solución, incluya la función onRangeChange del resumen anterior (simplificado/minificado) o el fragmento de código de demostración a continuación (funcionalmente idéntico pero más autoexplicativo) en su propio código. Invócalo como sigue:

onRangeChange(myRangeInputElmt, myListener);

Donde myRangeInputElmt es el elemento DOM <input type="range"> deseado y myListener es la función listener/handler que desea invocar sobre eventos similares a "change".

Su oyente puede ser sin parámetros si lo desea o puede usar el parámetro event, es decir, cualquiera de los siguientes funcionaría, dependiendo de sus necesidades:

var myListener = function() {...

O

var myListener = function(evt) {...

(La eliminación del receptor de eventos del elemento input (por ejemplo, usando removeEventListener) no se aborda en esta respuesta.)

Descripción de la 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 con my... es irrelevante para la solución universal y solo está presente por el bien de la demostración.

La demostración muestra el valor de rango / control deslizante, así como el número de veces que el estándar "change", "input" y eventos personalizados "onRangeChange" se han disparado (filas A, B y C respectivamente). Al ejecutar este fragmento en diferentes navegadores, tenga en cuenta lo siguiente al interactuar 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 cambio.
  • En Chrome y Safari, los valores de 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 de 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.

Demo Código:

// 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 parte mía, se inspiró en la respuesta de MBourne. Esa otra respuesta sugirió que los eventos "input" y "change" podrían fusionarse y que el código resultante funcionaría tanto en navegadores de escritorio como móviles. Sin embargo, el código en esa respuesta resulta en eventos "adicionales" ocultos que se disparan, lo que en sí mismo es problemático, y los eventos se disparan difieren entre navegadores, un problema adicional. Mi aplicación aquí resuelve esos problemas.

Palabras clave:

JavaScript tipo de entrada range slider eventos cambiar entrada navegador compatability cross-browser escritorio móvil no-jQuery

 25
Author: Andrew Willems,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2017-05-23 12:34:41

Las soluciones de Andrew Willem no son compatibles con dispositivos móviles.

Aquí está una modificación de su segunda solución que funciona en Edge, IE, Opera, FF, Chrome, iOS Safari y equivalentes móviles (que podría probar):

Actualización 1: Se eliminó la parte "requestAnimationFrame", ya que estoy de acuerdo en que no es necesario:

var listener = function() {
  // do whatever
};

slider1.addEventListener("input", function() {
  listener();
  slider1.addEventListener("change", listener);
});
slider1.addEventListener("change", function() {
  listener();
  slider1.removeEventListener("input", listener);
}); 

Actualización 2: Respuesta a Andrew 2nd Jun 2016 respuesta actualizada:

Gracias, Andrew-que parece funcionar en todos los navegadores que pude encontrar (Win escritorio: IE, Chrome, Opera, FF; Android Chrome, Opera y FF, iOS Safari).

Actualización 3: if ("oninput in slider) solución

Lo siguiente parece funcionar en todos los navegadores anteriores. (No puedo encontrar la fuente original ahora.) Yo estaba usando esto, pero posteriormente falló en IE y así que fui a buscar uno diferente, por lo tanto terminé aquí.

if ("oninput" in slider1) {
    slider1.addEventListener("input", function () {
        // do whatever;
    }, false);
}

Pero antes de comprobar su solución, me di cuenta de que esto estaba funcionando de nuevo en IE-tal vez había algún otro conflicto.

 4
Author: MBourne,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2016-06-04 12:39:51

Para un buen comportamiento entre navegadores, menos código y comprensible, lo mejor es usar el atributo onchange en combinación de una forma:

Esta es una solución solo html/javascript, y también se puede usar en línea.

function showVal(){
 document.getElementById("valBox").innerHTML=document.getElementById("inVal").value;

}
<form onchange="showVal()">
  <input type="range" min="5" max="10" step="1" id="inVal">
  </form>

<span id="valBox">
  </span>
 0
Author: Cryptopat,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2016-09-15 07:42:11

Podría usar el evento "ondrag" de JavaScript para disparar continuamente. Es mejor que "entrada" debido a las siguientes razones:

  1. Soporte del navegador.

  2. Podría diferenciar entre el evento" ondrag "y" change". "input" se activa tanto para arrastrar como para cambiar.

En jQuery:

$('#sample').on('drag',function(e){

});

Referencia: http://www.w3schools.com/TAgs/ev_ondrag.asp

 -2
Author: Sheik,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2017-12-21 07:57:08