onchange event on input type=range is not triggeri

2019-01-01 10:19发布

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?

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

8条回答
旧时光的记忆
2楼-- · 2019-01-01 10:33

Andrew Willem's solutions are not mobile device compatible.

Here's a modification of his second solution that works in Edge, IE, Opera, FF, Chrome, iOS Safari and mobile equivalents (that I could test):

Update 1: Removed "requestAnimationFrame" portion, as I agree it's not necessary:

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

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

Update 2: Response to Andrew's 2nd Jun 2016 updated answer:

Thanks, Andrew - that appears to work in every browser I could find (Win desktop: IE, Chrome, Opera, FF; Android Chrome, Opera and FF, iOS Safari).

Update 3: if ("oninput in slider) solution

The following appears to work across all the above browsers. (I cannot find the original source now.) I was using this, but it subsequently failed on IE and so I went looking for a different one, hence I ended up here.

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

But before I checked your solution, I noticed this was working again in IE - perhaps there was some other conflict.

查看更多
与君花间醉酒
3楼-- · 2019-01-01 10:35

You could use the JavaScript "ondrag" event to fire continuously. It is better than "input" due to the following reasons:

  1. Browser support.

  2. Could differentiate between "ondrag" and "change" event. "input" fires for both drag and change.

In jQuery:

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

});

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

查看更多
孤独寂梦人
4楼-- · 2019-01-01 10:39

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.

查看更多
裙下三千臣
5楼-- · 2019-01-01 10:39

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.

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

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'.

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

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:

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>

查看更多
姐姐魅力值爆表
6楼-- · 2019-01-01 10:45

I'm posting this as an answer because it deserves to be it's own answer rather than a comment under a less useful answer. I find this method much better than the accepted answer since it can keep all the js in a separate file from the HTML.

Answer provided by Jamrelian in his comment under the accepted answer.

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

Just be aware of this comment by Jaime though

Just note that with this solution, in chrome you will get two calls to the handler (one per event), so if you care for that, then you need to guard against it.

As in it will fire the event when you have stopped moving the mouse, and then again when you release the mouse button.

查看更多
时光乱了年华
7楼-- · 2019-01-01 10:45

For a good cross-browser behavior, less and understandable code, best is to use the onchange attribute in combination of a form:

This is a html/javascript only solution, and can as well be used in-line.

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>

查看更多
登录 后发表回答