[javascript] onchange event on input type=range is not triggering in firefox while dragging

UPDATE: I am leaving this answer here as an example of how to use mouse events to use range/slider interactions in desktop (but not mobile) browsers. However, I have now also written a completely different and, I believe, better answer elsewhere on this page that uses a different approach to providing a cross-browser desktop-and-mobile solution to this problem.

Original answer:

Summary: A cross-browser, plain JavaScript (i.e. no-jQuery) solution to allow reading range input values without using on('input'... and/or on('change'... which work inconsistently between browsers.

As of today (late Feb, 2016), there is still browser inconsistency so I'm providing a new work-around here.

The problem: When using a range input, i.e. a slider, on('input'... provides continuously updated range values in Mac and Windows Firefox, Chrome and Opera as well as Mac Safari, while on('change'... only reports the range value upon mouse-up. In contrast, in Internet Explorer (v11), on('input'... does not work at all, and on('change'... is continuously updated.

I report here 2 strategies to get identical continuous range value reporting in all browsers using vanilla JavaScript (i.e. no jQuery) by using the mousedown, mousemove and (possibly) mouseup events.

Strategy 1: Shorter but less efficient

If you prefer shorter code over more efficient code, you can use this 1st solution which uses mousesdown and mousemove but not mouseup. This reads the slider as needed, but continues firing unnecessarily during any mouse-over events, even when the user has not clicked and is thus not dragging the slider. It essentially reads the range value both after 'mousedown' and during 'mousemove' events, slightly delaying each using requestAnimationFrame.

_x000D_
_x000D_
var rng = document.querySelector("input");_x000D_
_x000D_
read("mousedown");_x000D_
read("mousemove");_x000D_
read("keydown"); // include this to also allow keyboard control_x000D_
_x000D_
function read(evtType) {_x000D_
  rng.addEventListener(evtType, function() {_x000D_
    window.requestAnimationFrame(function () {_x000D_
      document.querySelector("div").innerHTML = rng.value;_x000D_
      rng.setAttribute("aria-valuenow", rng.value); // include for accessibility_x000D_
    });_x000D_
  });_x000D_
}
_x000D_
<div>50</div><input type="range"/>
_x000D_
_x000D_
_x000D_

Strategy 2: Longer but more efficient

If you need more efficient code and can tolerate longer code length, then you can use the following solution which uses mousedown, mousemove and mouseup. This also reads the slider as needed, but appropriately stops reading it as soon as the mouse button is released. The essential difference is that is only starts listening for 'mousemove' after 'mousedown', and it stops listening for 'mousemove' after 'mouseup'.

_x000D_
_x000D_
var rng = document.querySelector("input");_x000D_
_x000D_
var listener = function() {_x000D_
  window.requestAnimationFrame(function() {_x000D_
    document.querySelector("div").innerHTML = rng.value;_x000D_
  });_x000D_
};_x000D_
_x000D_
rng.addEventListener("mousedown", function() {_x000D_
  listener();_x000D_
  rng.addEventListener("mousemove", listener);_x000D_
});_x000D_
rng.addEventListener("mouseup", function() {_x000D_
  rng.removeEventListener("mousemove", listener);_x000D_
});_x000D_
_x000D_
// include the following line to maintain accessibility_x000D_
// by allowing the listener to also be fired for_x000D_
// appropriate keyboard events_x000D_
rng.addEventListener("keydown", listener);
_x000D_
<div>50</div><input type="range"/>
_x000D_
_x000D_
_x000D_

Demo: Fuller explanation of the need for, and implementation of, the above work-arounds

The following code more fully demonstrates numerous aspects of this strategy. Explanations are embedded in the demonstration:

_x000D_
_x000D_
var select, inp, listen, unlisten, anim, show, onInp, onChg, onDn1, onDn2, onMv1, onMv2, onUp, onMvCombo1, onDnCombo1, onUpCombo2, onMvCombo2, onDnCombo2;_x000D_
_x000D_
select   = function(selctr)     { return document.querySelector(selctr);      };_x000D_
inp = select("input");_x000D_
listen   = function(evtTyp, cb) { return inp.   addEventListener(evtTyp, cb); };_x000D_
unlisten = function(evtTyp, cb) { return inp.removeEventListener(evtTyp, cb); };_x000D_
anim     = function(cb)         { return window.requestAnimationFrame(cb);    };_x000D_
show = function(id) {_x000D_
 return function() {_x000D_
    select("#" + id + " td~td~td"   ).innerHTML = inp.value;_x000D_
    select("#" + id + " td~td~td~td").innerHTML = (Math.random() * 1e20).toString(36); // random text_x000D_
  };_x000D_
};_x000D_
_x000D_
onInp      =                  show("inp" )                                      ;_x000D_
onChg      =                  show("chg" )                                      ;_x000D_
onDn1      =                  show("mdn1")                                      ;_x000D_
onDn2      = function() {anim(show("mdn2"));                                   };_x000D_
onMv1      =                  show("mmv1")                                      ;_x000D_
onMv2      = function() {anim(show("mmv2"));                                   };_x000D_
onUp       =                  show("mup" )                                      ;_x000D_
onMvCombo1 = function() {anim(show("cmb1"));                                   };_x000D_
onDnCombo1 = function() {anim(show("cmb1"));   listen("mousemove", onMvCombo1);};_x000D_
onUpCombo2 = function() {                    unlisten("mousemove", onMvCombo2);};_x000D_
onMvCombo2 = function() {anim(show("cmb2"));                                   };_x000D_
onDnCombo2 = function() {anim(show("cmb2"));   listen("mousemove", onMvCombo2);};_x000D_
_x000D_
listen("input"    , onInp     );_x000D_
listen("change"   , onChg     );_x000D_
listen("mousedown", onDn1     );_x000D_
listen("mousedown", onDn2     );_x000D_
listen("mousemove", onMv1     );_x000D_
listen("mousemove", onMv2     );_x000D_
listen("mouseup"  , onUp      );_x000D_
listen("mousedown", onDnCombo1);_x000D_
listen("mousedown", onDnCombo2);_x000D_
listen("mouseup"  , onUpCombo2);
_x000D_
table {border-collapse: collapse; font: 10pt Courier;}_x000D_
th, td {border: solid black 1px; padding: 0 0.5em;}_x000D_
input {margin: 2em;}_x000D_
li {padding-bottom: 1em;}
_x000D_
<p>Click on 'Full page' to see the demonstration properly.</p>_x000D_
<table>_x000D_
  <tr><th></th><th>event</th><th>range value</th><th>random update indicator</th></tr>_x000D_
  <tr id="inp" ><td>A</td><td>input                                </td><td>100</td><td>-</td></tr>_x000D_
  <tr id="chg" ><td>B</td><td>change                               </td><td>100</td><td>-</td></tr>_x000D_
  <tr id="mdn1"><td>C</td><td>mousedown                            </td><td>100</td><td>-</td></tr>_x000D_
  <tr id="mdn2"><td>D</td><td>mousedown using requestAnimationFrame</td><td>100</td><td>-</td></tr>_x000D_
  <tr id="mmv1"><td>E</td><td>mousemove                            </td><td>100</td><td>-</td></tr>_x000D_
  <tr id="mmv2"><td>F</td><td>mousemove using requestAnimationFrame</td><td>100</td><td>-</td></tr>_x000D_
  <tr id="mup" ><td>G</td><td>mouseup                              </td><td>100</td><td>-</td></tr>_x000D_
  <tr id="cmb1"><td>H</td><td>mousedown/move combo                 </td><td>100</td><td>-</td></tr>_x000D_
  <tr id="cmb2"><td>I</td><td>mousedown/move/up combo              </td><td>100</td><td>-</td></tr>_x000D_
</table>_x000D_
<input type="range" min="100" max="999" value="100"/>_x000D_
<ol>_x000D_
  <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>_x000D_
  <li>To see browser differences between input and change event implementations, use the slider in different browsers and compare A and&nbsp;B.</li>_x000D_
  <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>_x000D_
  <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>_x000D_
  <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>_x000D_
  <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>_x000D_
  <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>_x000D_
</ol>
_x000D_
_x000D_
_x000D_

Examples related to javascript

need to add a class to an element How to make a variable accessible outside a function? Hide Signs that Meteor.js was Used How to create a showdown.js markdown extension Please help me convert this script to a simple image slider Highlight Anchor Links when user manually scrolls? Summing radio input values How to execute an action before close metro app WinJS javascript, for loop defines a dynamic variable name Getting all files in directory with ajax

Examples related to html

Embed ruby within URL : Middleman Blog Please help me convert this script to a simple image slider Generating a list of pages (not posts) without the index file Why there is this "clear" class before footer? Is it possible to change the content HTML5 alert messages? Getting all files in directory with ajax DevTools failed to load SourceMap: Could not load content for chrome-extension How to set width of mat-table column in angular? How to open a link in new tab using angular? ERROR Error: Uncaught (in promise), Cannot match any routes. URL Segment

Examples related to firefox

Drag and drop menuitems Class has been compiled by a more recent version of the Java Environment Only on Firefox "Loading failed for the <script> with source" Selenium using Python - Geckodriver executable needs to be in PATH Selenium using Java - The path to the driver executable must be set by the webdriver.gecko.driver system property How to use the gecko executable with Selenium Selenium 2.53 not working on Firefox 47 Postman addon's like in firefox Edit and replay XHR chrome/firefox etc? How to enable CORS on Firefox?

Examples related to input

Angular 4 - get input value React - clearing an input value after form submit Min and max value of input in angular4 application Disable Button in Angular 2 Angular2 - Input Field To Accept Only Numbers How to validate white spaces/empty spaces? [Angular 2] Can't bind to 'ngModel' since it isn't a known property of 'input' Mask for an Input to allow phone numbers? File upload from <input type="file"> Why does the html input with type "number" allow the letter 'e' to be entered in the field?

Examples related to onchange

Why can't I change my input value in React even with the onChange listener React Checkbox not sending onChange Set Label Text with JQuery android on Text Change Listener Jquery select change not firing onchange event on input type=range is not triggering in firefox while dragging select2 onchange event only works once Dropdown using javascript onchange How to fire a change event on a HTMLSelectElement if the new value is the same as the old? jQuery - on change input text