输入类型=范围上的 onchange 事件在 Firefox 中不触发,同时拖动

2022-08-30 00:00:11

当我玩时,Firefox只有在我们将滑块放到一个新位置时才会触发onchange事件,其中Chrome和其他人在拖动滑块时触发onchange事件。<input type="range">

我怎样才能在 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)">

答案 1

显然,Chrome和Safari是错误的:应该只在用户释放鼠标时触发。要获得持续更新,您应该使用该事件,该事件将从鼠标和键盘捕获Firefox,Safari和Chrome中的实时更新。onchangeoninput

但是,在 IE10 中不受支持,因此最好的办法是将两个事件处理程序组合在一起,如下所示:oninput

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

查看此Bugzilla线程以获取更多信息。


答案 2

总结:

我在这里提供了一个no-jQuery跨浏览器桌面和移动功能,以始终如一地响应范围/滑块交互,这在当前的浏览器中是不可能的。它基本上强制所有浏览器模拟IE11的事件。新功能是...on("change"...on("change"...on("input"...

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

...其中 是您的范围输入元素,并且是您的侦听器。侦听器将在更改范围/滑块值的任何交互之后调用,但在不更改该值的交互(包括当前滑块位置或移出滑块任一端的初始鼠标或触摸交互)之后不会调用侦听器。rf

问题:

截至 2016 年 6 月初,不同的浏览器在响应范围/滑块使用情况方面存在差异。有五种方案是相关的:

  1. 初始鼠标按下(或触摸开始)在当前滑块位置
  2. 在新的滑块位置初始鼠标按下(或触摸开始)
  3. 任何后续的鼠标(或触摸)移动后沿滑块移动 1 或 2
  4. 任何后续的鼠标(或触摸)移动后 1 或 2 通过滑块的任一端
  5. 最终鼠标向上(或触摸端)

下表显示了至少三个不同的桌面浏览器在响应上述方案方面的行为有何不同:

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

溶液:

该函数为范围/滑块交互提供一致且可预测的跨浏览器响应。它强制所有浏览器按照下表运行:onRangeChange

table showing behaviour of all browsers using the proposed solution

在IE11中,代码基本上允许所有内容按照现状运行,即它允许事件以其标准方式运行,并且事件无关紧要,因为它永远不会触发。在其他浏览器中,该事件被有效地静音(以防止触发额外的,有时不明显的事件)。此外,仅当范围/滑块的值发生更改时,事件才会触发其侦听器。对于某些浏览器(例如Firefox),发生这种情况是因为监听器在上面列表中的场景1,4和5中被有效地静音。"change""input""change""input"

(如果您确实需要在方案 1、4 和/或 5 中激活侦听器,则可以尝试合并“mousedown”/“touchstart”、“mousemove”/“touchmove”和/或 “mouseup”/“touchend”事件。这样的解决方案超出了这个答案的范围。

移动浏览器中的功能:

我已经在桌面浏览器中测试了此代码,但未在任何移动浏览器中测试。但是,在此页面上的另一个答案中,MBourne已经表明我在这里的解决方案“......似乎可以在我能找到的每个浏览器中工作(Win桌面:IE,Chrome,Opera,FF;Android Chrome,Opera和FF,iOS Safari)”。(感谢MBourne。

用法:

要使用此解决方案,请在您自己的代码中包含上述摘要中的函数(简化/缩小)或下面的演示代码片段(功能相同但更加不言自明)。按如下方式调用它:onRangeChange

onRangeChange(myRangeInputElmt, myListener);

where 是所需的 DOM 元素,并且是要在类事件上调用的侦听器/处理程序函数。myRangeInputElmt<input type="range">myListener"change"

如果需要,您的侦听器可能是无参数的,或者可以使用该参数,即以下任一方法都可以工作,具体取决于您的需要:event

var myListener = function() {...

var myListener = function(evt) {...

(从输入元素中删除事件侦听器(例如,使用 removeEventListener)在此答案中未得到解决。

演示说明:

在下面的代码片段中,该函数提供了通用解决方案。代码的其余部分只是一个示例来演示其用法。任何以 开头的变量都与通用解决方案无关,并且仅出于演示而存在。onRangeChangemy...

该演示显示了范围/滑块值以及标准和自定义事件触发的次数(分别为 A、B 和 C 行)。在不同的浏览器中运行此代码段时,在与范围/滑块交互时请注意以下事项:"change""input""onRangeChange"

  • 在 IE11 中,行 A 和 C 中的值在上面的方案 2 和 3 中都会更改,而行 B 永远不会更改。
  • 在 Chrome 和 Safari 中,行 B 和 C 中的值在方案 2 和 3 中都会更改,而行 A 仅在方案 5 中更改。
  • 在 Firefox 中,行 A 中的值仅针对方案 5 更改,行 B 在所有五个方案中更改,行 C 仅对方案 2 和 3 更改。
  • 在上述所有浏览器中,行 C 中的更改(建议的解决方案)是相同的,即仅适用于方案 2 和 3。

演示代码:

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

信用:

虽然这里的实现很大程度上是我自己的,但它的灵感来自MBourne的答案。另一个答案表明,“输入”和“更改”事件可以合并,并且生成的代码可以在桌面和移动浏览器中使用。但是,该答案中的代码会导致触发隐藏的“额外”事件,这本身就是有问题的,并且触发的事件在浏览器之间有所不同,这是另一个问题。我在这里的实现解决了这些问题。

关键字:

JavaScript 输入类型范围滑块事件更改输入 浏览器兼容性 跨浏览器 桌面移动 no-jQuery