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

新手上路,请多包涵

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

如何在 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)">

原文由 Prasanth K C 发布,翻译遵循 CC BY-SA 4.0 许可协议

阅读 892
2 个回答

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

但是,IE10 不支持 oninput ,所以最好的办法是结合两个事件处理程序,如下所示:

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

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

原文由 Frederik 发布,翻译遵循 CC BY-SA 4.0 许可协议

概括:

我在这里提供了一种非 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);});
}

…其中 r 是您的范围输入元素, f 是您的听众。将在任何更改范围/滑块值的交互之后调用侦听器,但不会在不更改该值的交互之后调用,包括在当前滑块位置或离开滑块任一端的初始鼠标或触摸交互。

问题:

截至 2016 年 6 月上旬,不同的浏览器在响应范围/滑块使用方面有所不同。五个场景是相关的:

  1. 当前滑块位置的初始鼠标按下(或触摸启动)
  2. 在新的滑块位置初始按下鼠标(或触摸开始)
  3. 沿滑块移动 1 或 2 之后的任何后续鼠标(或触摸)移动
  4. 经过滑块任一端 1 或 2 次后的任何后续鼠标(或触摸)移动
  5. 最终鼠标弹起(或触摸结束)

下表显示了至少三种不同的桌面浏览器在响应上述场景时的行为有何不同:

显示浏览器在响应事件和时间方面的差异的表格

解决方案:

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

显示使用建议解决方案的所有浏览器行为的表格

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

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

移动浏览器中的功能:

我已经在桌面浏览器中测试了这段代码,但没有在任何移动浏览器中测试过。然而,在 此页面上的另一个答案中, MBourne 表明我的解决方案 “……似乎适用于我能找到的所有浏览器(Win 桌面:IE、Chrome、Opera、FF;Android Chrome、Opera 和 FF、iOS Safari) “ 。 (感谢 MBourne。)

用法:

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

 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" events.

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

 var myListener = function() {...

要么

var myListener = function(evt) {...

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

演示说明:

在下面的代码片段中,函数 onRangeChange 提供了通用解决方案。其余代码只是一个演示其使用的示例。任何以 my... 开头的变量与通用解决方案无关,仅用于演示。

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

  • 在 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

原文由 Andrew Willems 发布,翻译遵循 CC BY-SA 4.0 许可协议

撰写回答
你尚未登录,登录后可以
  • 和开发者交流问题的细节
  • 关注并接收问题和回答的更新提醒
  • 参与内容的编辑和改进,让解决方法与时俱进
推荐问题