概括:
我在这里提供了一种非 jQuery 跨浏览器桌面和移动设备的能力,可以一致地响应范围/滑块交互,这在当前浏览器中是不可能的。它实质上强制所有浏览器on("change"...
为其on("change"...
或on("input"...
事件模拟 IE11 的事件。新功能是...
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 之后的任何后续鼠标(或触摸)移动
- 在经过滑块任一端 1 或 2 次之后的任何后续鼠标(或触摸)移动
- 最终鼠标向上(或触摸结束)
下表显示了至少三种不同的桌面浏览器在响应上述哪种场景方面的行为不同:
解决方案:
该onRangeChange
函数为范围/滑块交互提供一致且可预测的跨浏览器响应。它强制所有浏览器按照下表进行操作:
在 IE11 中,代码基本上允许一切按照现状运行,即它允许"change"
事件以其标准方式运行,并且"input"
事件无关紧要,因为它无论如何都不会触发。在其他浏览器中,该"change"
事件被有效地静音(以防止触发额外的、有时不明显的事件)。此外,"input"
仅当范围/滑块的值发生变化时,事件才会触发其侦听器。对于某些浏览器(例如 Firefox),发生这种情况是因为在上述列表中的场景 1、4 和 5 中侦听器被有效地静音。
(如果您确实需要在场景 1、4 和/或 5 中激活侦听器,您可以尝试合并"mousedown"
/ "touchstart"
、"mousemove"
/"touchmove"
和/或"mouseup"
/"touchend"
事件。这样的解决方案超出了本答案的范围。)
移动浏览器中的功能:
我已经在桌面浏览器中测试过这段代码,但没有在任何移动浏览器中测试过。然而,在本页的另一个答案中, MBourne 表明我的解决方案“...似乎适用于我能找到的每个浏览器(Win 桌面:IE、Chrome、Opera、FF;Android Chrome、Opera 和 FF、iOS Safari) ”。(感谢姆伯恩。)
用法:
要使用此解决方案,请onRangeChange
在您自己的代码中包含以上摘要中的函数(简化/缩小)或以下演示代码片段(功能相同但更不言自明)。调用它如下:
onRangeChange(myRangeInputElmt, myListener);
哪里myRangeInputElmt
是你想要的<input type="range">
DOM 元素,myListener
是你想要在"change"
类似事件上调用的侦听器/处理程序函数。
如果需要,您的侦听器可以是无参数的,也可以使用event
参数,即根据您的需要,以下任一方法都可以:
var myListener = function() {...
或者
var myListener = function(evt) {...
(从input
元素中删除事件侦听器(例如使用removeEventListener
)在这个答案中没有解决。)
演示说明:
在下面的代码片段中,该函数onRangeChange
提供了通用解决方案。其余代码只是演示其使用的示例。任何以 开头的变量my...
都与通用解决方案无关,只是为了演示而存在。
演示显示的范围内/滑块值以及次标准数"change"
,"input"
和自定义"onRangeChange"
事件烧制(行分别为A,B和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 输入类型范围滑块事件更改输入浏览器兼容性跨浏览器桌面移动无jQuery