概括:
我在这里提供了一个无 jQuery 跨浏览器桌面和移动设备的能力,可以一致地响应范围/滑块交互,这在当前浏览器中是不可能的。它本质上强制所有浏览器on("change"...
为它们的on("change"...
或事件模拟 IE11 的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 后的任何后续鼠标(或触摸)移动
- 在滑块任一端经过 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) ”。(感谢 MBourne。)
用法:
要使用此解决方案,onRangeChange
请在您自己的代码中包含上面摘要中的函数(简化/缩小)或下面的演示代码片段(功能相同但更不言自明)。调用它如下:
onRangeChange(myRangeInputElmt, myListener);
wheremyRangeInputElmt
是您想要的<input type="range">
DOM 元素,并且是您希望在-like 事件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