Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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

When I played with <input type="range">, Firefox triggers an onchange event only if we drop the slider to a new position where Chrome and others triggers onchange events while the slider is dragged.

How can I make it happen on dragging in Firefox?

function showVal(newVal){      document.getElementById("valBox").innerHTML=newVal;  }
<span id="valBox"></span>  <input type="range" min="5" max="10" step="1" onchange="showVal(this.value)">
like image 331
Prasanth K C Avatar asked Aug 31 '13 05:08

Prasanth K C


People also ask

How do I use Onchange input field?

Definition and UsageThe onchange attribute fires the moment when the value of the element is changed. Tip: This event is similar to the oninput event. The difference is that the oninput event occurs immediately after the value of an element has changed, while onchange occurs when the element loses focus.

Is there any difference between browser's and react's Onchange event?

Javascript onchangeUnlike React, the browser fires onchange event after focus from input element is taken off. So when focus is set on an input element and something is typed, onchange won't be fired until and unless the input element is out of focus.

Is Onchange a mouse event?

The onchange event occurs when the value of an element has been changed. For radiobuttons and checkboxes, the onchange event occurs when the checked state has been changed. Tip: This event is similar to the oninput event.


2 Answers

Apparently Chrome and Safari are wrong: onchange should only be triggered when the user releases the mouse. To get continuous updates, you should use the oninput event, which will capture live updates in Firefox, Safari and Chrome, both from the mouse and the keyboard.

However, oninput is not supported in IE10, so your best bet is to combine the two event handlers, like this:

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

Check out this Bugzilla thread for more information.

like image 170
Frederik Avatar answered Sep 20 '22 18:09

Frederik


SUMMARY:

I provide here a no-jQuery cross-browser desktop-and-mobile ability to consistently respond to range/slider interactions, something not possible in current browsers. It essentially forces all browsers to emulate IE11's on("change"... event for either their on("change"... or on("input"... events. The new function is...

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);}); } 

...where r is your range input element and f is your listener. The listener will be called after any interaction that changes the range/slider value but not after interactions that do not change that value, including initial mouse or touch interactions at the current slider position or upon moving off either end of the slider.

Problem:

As of early June 2016, different browsers differ in terms of how they respond to range/slider usage. Five scenarios are relevant:

  1. initial mouse-down (or touch-start) at the current slider position
  2. initial mouse-down (or touch-start) at a new slider position
  3. any subsequent mouse (or touch) movement after 1 or 2 along the slider
  4. any subsequent mouse (or touch) movement after 1 or 2 past either end of the slider
  5. final mouse-up (or touch-end)

The following table shows how at least three different desktop browsers differ in their behaviour with respect to which of the above scenarios they respond to:

table showing browser differences with respect to which events they respond to and when

Solution:

The onRangeChange function provides a consistent and predictable cross-browser response to range/slider interactions. It forces all browsers to behave according to the following table:

table showing behaviour of all browsers using the proposed solution

In IE11, the code essentially allows everything to operate as per the status quo, i.e. it allows the "change" event to function in its standard way and the "input" event is irrelevant as it never fires anyway. In other browsers, the "change" event is effectively silenced (to prevent extra and sometimes not-readily-apparent events from firing). In addition, the "input" event fires its listener only when the range/slider's value changes. For some browsers (e.g. Firefox) this occurs because the listener is effectively silenced in scenarios 1, 4 and 5 from the above list.

(If you truly require a listener to be activated in either scenario 1, 4 and/or 5 you could try incorporating "mousedown"/"touchstart", "mousemove"/"touchmove" and/or "mouseup"/"touchend" events. Such a solution is beyond the scope of this answer.)

Functionality in Mobile Browsers:

I have tested this code in desktop browsers but not in any mobile browsers. However, in another answer on this page MBourne has shown that my solution here "...appears to work in every browser I could find (Win desktop: IE, Chrome, Opera, FF; Android Chrome, Opera and FF, iOS Safari)". (Thanks MBourne.)

Usage:

To use this solution, include the onRangeChange function from the summary above (simplified/minified) or the demo code snippet below (functionally identical but more self-explanatory) in your own code. Invoke it as follows:

onRangeChange(myRangeInputElmt, myListener); 

where myRangeInputElmt is your desired <input type="range"> DOM element and myListener is the listener/handler function you want invoked upon "change"-like events.

Your listener may be parameter-less if desired or may use the event parameter, i.e. either of the following would work, depending on your needs:

var myListener = function() {... 

or

var myListener = function(evt) {... 

(Removing the event listener from the input element (e.g. using removeEventListener) is not addressed in this answer.)

Demo Description:

In the code snippet below, the function onRangeChange provides the universal solution. The rest of the code is simply an example to demonstrate its use. Any variable that begins with my... is irrelevant to the universal solution and is only present for the sake of the demo.

The demo shows the range/slider value as well as the number of times the standard "change", "input" and custom "onRangeChange" events have fired (rows A, B and C respectively). When running this snippet in different browsers, note the following as you interact with the range/slider:

  • In IE11, the values in rows A and C both change in scenarios 2 and 3 above while row B never changes.
  • In Chrome and Safari, the values in rows B and C both change in scenarios 2 and 3 while row A changes only for scenario 5.
  • In Firefox, the value in row A changes only for scenario 5, row B changes for all five scenarios, and row C changes only for scenarios 2 and 3.
  • In all of the above browsers, the changes in row C (the proposed solution) are identical, i.e. only for scenarios 2 and 3.

Demo Code:

// 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>

Credit:

While the implementation here is largely my own, it was inspired by MBourne's answer. That other answer suggested that the "input" and "change" events could be merged and that the resulting code would work in both desktop and mobile browsers. However, the code in that answer results in hidden "extra" events being fired, which in and of itself is problematic, and the events fired differ between browsers, a further problem. My implementation here solves those problems.

Keywords:

JavaScript input type range slider events change input browser compatability cross-browser desktop mobile no-jQuery

like image 44
Andrew Willems Avatar answered Sep 19 '22 18:09

Andrew Willems