时,只有当我们将滑块放到一个新位置时,Firefox 才会触发 onchange 事件,在该位置 Chrome 和其他人会在拖动滑块时触发 onchange 事件。如何在 Firefox 中进行拖动?函数 showVal(newVal){ document.getElementById("valBox").innerHTML=newVal; }" /> 时,只有当我们将滑块放到一个新位置时,Firefox 才会触发 onchange 事件,在该位置 Chrome 和其他人会在拖动滑块时触发 onchange 事件。如何在 Firefox 中进行拖动?函数 showVal(newVal){ document.getElementById("valBox").innerHTML=newVal; }"> 时,只有当我们将滑块放到一个新位置时,Firefox 才会触发 onchange 事件,在该位置 Chrome 和其他人会在拖动滑块时触发 onchange 事件。如何在 Firefox 中进行拖动?函数 showVal(newVal){ document.getElementById("valBox").innerHTML=newVal; }" />
当我使用 <input type="range">
时,只有当我们将滑块放到一个新位置时,Firefox 才会触发 onchange 事件,在该位置 Chrome 和其他人会在拖动滑块时触发 onchange 事件。
如何在 Firefox 中进行拖动?
函数 showVal(newVal){ document.getElementById("valBox").innerHTML=newVal; }
onchange
也不会触发。在解决这个问题时,我发现了这个问题。
显然 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 thread 了解更多信息。
概括:
我在这里提供了一个无 jQuery 跨浏览器桌面和移动设备的能力,可以一致地响应范围/滑块交互,这在当前浏览器中是不可能的。它实质上强制所有浏览器为它们的 on("change"...
或 on("input"...
事件模拟 IE11 的 on("change"...
事件。新功能是...
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 之后的任何后续鼠标(或触摸)移动 任何后续鼠标(或touch) 在经过滑块任一端 1 或 2 后的移动最终鼠标向上(或触摸端)
下表显示了至少三种不同的桌面浏览器在响应上述哪种场景时的行为有何不同:
https://i.stack.imgur.com/L4Bw8.png
解决方案:
onRangeChange
函数为范围/滑块交互提供一致且可预测的跨浏览器响应。它强制所有浏览器按照下表运行:
https://i.stack.imgur.com/gZR33.png
在 IE11 中,代码基本上允许一切按现状运行,即它允许 "change"
事件以其标准方式运行,而 "input"
事件是无关紧要的,因为它永远不会触发。在其他浏览器中,"change"
事件被有效地静音(以防止触发额外的,有时是不明显的事件)。此外,仅当范围/滑块的值发生更改时,"input"
事件才会触发其侦听器。对于某些浏览器(例如 Firefox),这是因为侦听器在上述列表中的场景 1、4 和 5 中被有效地静音。
(如果您确实需要在场景 1、4 和/或 5 中激活侦听器,您可以尝试合并 "mousedown"
/"touchstart"
、"mousemove"
/"touchmove"
和/或 "mouseup"
/{ 6} 个事件。这样的解决方案超出了这个答案的范围。)
移动浏览器中的功能:
我已经在桌面浏览器中测试过这段代码,但没有在任何移动浏览器中测试过。但是,在 another answer on this page 中,MBourne 表明我的解决方案 "...似乎适用于我能找到的所有浏览器(Win 桌面:IE、Chrome、Opera、FF;Android Chrome、Opera 和 FF、iOS Safari )"。 (感谢 MBourne。)
用法:
要使用此解决方案,请在您自己的代码中包含上面摘要中的 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。
演示代码:
// 模拟 IE11 的“change”事件的主要函数: 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); } }); } // 示例用法: 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 = {输入:0,更改:0,自定义:0}; var myUpdate = function() { myNumChgEvtsCell.innerHTML = myNumEvts["change"]; myNumInpEvtsCell.innerHTML = myNumEvts["input" ]; myNumCusEvtsCell.innerHTML = myNumEvts["custom"]; }; ["输入", "更改"].forEach(function(myEvtType) { myRangeInputElmt.addEventListener(myEvtType, function() { myNumEvts[myEvtType] += 1; myUpdate(); }); }); var myListener = function(myEvt) { myNumEvts["custom"] += 1; myRangeValPar.innerHTML = "范围值:" + myEvt.target.value;我的更新(); }; onRangeChange(myRangeInputElmt, myListener);表{边框折叠:折叠; } th, td { 文本对齐:左;边框:纯黑1px;填充:5px 15px; }
范围值:50
row | 事件类型 | < th>事件数|
---|---|---|
A | 标准“改变”事件 | 0 | tr>
B | 标准“输入”事件 | 0 |
C< /td> | 新的自定义“onRangeChange”事件 | 0 |
信用:
虽然这里的实现主要是我自己的,但它的灵感来自 MBourne's answer。另一个答案表明可以合并“输入”和“更改”事件,并且生成的代码可以在桌面和移动浏览器中运行。但是,该答案中的代码会导致触发隐藏的“额外”事件,这本身就是有问题的,并且触发的事件在浏览器之间有所不同,这是另一个问题。我在这里的实现解决了这些问题。
关键词:
JavaScript 输入类型范围滑块事件改变输入浏览器兼容性跨浏览器桌面移动无jQuery
我将其发布为答案,因为它应该是它自己的答案,而不是在不太有用的答案下发表评论。我发现这种方法比接受的答案要好得多,因为它可以将所有 js 保存在与 HTML 不同的文件中。
Jamrelian 在接受的答案下的评论中提供的答案。
$("#myelement").on("input change", function() {
//do something
});
不过请注意 Jaime 的评论
请注意,使用此解决方案,在 chrome 中,您将收到对处理程序的两次调用(每个事件一次),因此如果您关心它,那么您需要防范它。
因为它会在您停止移动鼠标时触发事件,然后在您释放鼠标按钮时再次触发。
更新
对于导致功能触发两次的 change
事件和 input
事件,这几乎不是问题。
如果您在 input
上触发了某个函数,那么触发 change
事件时出现问题的可能性极小。
input
在您拖动范围输入滑块时快速触发。担心最后再触发一个函数调用就像担心一滴水与 input
事件的海洋相比。
甚至包含 change
事件的原因是为了浏览器兼容性(主要与 IE 兼容)。
更新:我在这里留下这个答案作为如何使用鼠标事件在桌面(但不是移动)浏览器中使用范围/滑块交互的示例。不过,我现在还在此页面的其他地方写了 a completely different and, I believe, better answer,它使用不同的方法来提供跨浏览器的桌面和移动解决方案来解决这个问题。
原答案:
总结:一种跨浏览器的纯 JavaScript(即非 jQuery)解决方案,允许在不使用在浏览器之间工作不一致的 on('input'...
和/或 on('change'...
的情况下读取范围输入值。
截至今天(2016 年 2 月下旬),仍然存在浏览器不一致的问题,所以我在这里提供了一个新的解决方法。
问题:当使用范围输入(即滑块)时,on('input'...
在 Mac 和 Windows Firefox、Chrome 和 Opera 以及 Mac Safari 中提供持续更新的范围值,而 on('change'...
仅在鼠标向上时报告范围值。相比之下,在 Internet Explorer (v11) 中,on('input'...
根本不起作用,并且 on('change'...
会不断更新。
我在这里报告 2 种策略,通过使用 mousedown、mousemove 和(可能)mouseup 事件,在所有使用 vanilla JavaScript(即没有 jQuery)的浏览器中获得相同的连续范围值报告。
策略1:更短但效率更低
如果您更喜欢更短的代码而不是更高效的代码,您可以使用第一个解决方案,它使用 mousesdown 和 mousemove 但不使用 mouseup。这会根据需要读取滑块,但在任何鼠标悬停事件期间会继续不必要地触发,即使用户没有单击并且因此没有拖动滑块也是如此。它本质上是在“mousedown”之后和“mousemove”事件期间读取范围值,使用 requestAnimationFrame
稍微延迟每个事件。
var rng = document.querySelector("输入");读(“鼠标按下”);读(“鼠标移动”);读(“keydown”); // 包括这个也允许键盘控制 function read(evtType) { rng.addEventListener(evtType, function() { window.requestAnimationFrame(function () { document.querySelector("div").innerHTML = rng.value; rng. setAttribute("aria-valuenow", rng.value); // 包括无障碍 }); }); }
策略 2:更长但更高效
如果您需要更高效的代码并且可以容忍更长的代码长度,那么您可以使用以下使用 mousedown、mousemove 和 mouseup 的解决方案。这也会根据需要读取滑块,但只要松开鼠标按钮就会适当地停止读取它。本质的区别是它只在“mousedown”之后开始监听“mousemove”,在“mouseup”之后停止监听“mousemove”。
var rng = document.querySelector("输入"); 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); }); // 包括以下行以保持可访问性 // 通过允许监听器也被触发 // 适当的键盘事件 rng.addEventListener("keydown", listener);
演示:更全面地解释上述变通方法的必要性和实施
下面的代码更充分地展示了这个策略的许多方面。演示中嵌入了解释:
变量选择,输入,听,不听,动画,显示,onInp,onChg,onDn1,onDn2,onMv1,onMv2,onUp,onMvCombo1,onDnCombo1,onUpCombo2,onMvCombo2,onDnCombo2;选择=函数(选择){返回文档。查询选择器(选择); }; inp = select("输入");听=函数(evtTyp,cb){返回输入。 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); // 随机文本 }; }; onInp = 显示(“inp”); onChg = 显示(“chg”); onDn1 = 显示(“mdn1”); onDn2 = function() {anim(show("mdn2")); }; onMv1 = 显示(“mmv1”); onMv2 = function() {anim(show("mmv2")); }; onUp = 显示(“mup”); onMvCombo1 = function() {anim(show("cmb1")); }; onDnCombo1 = function() {anim(show("cmb1"));听(“鼠标移动”,onMvCombo1);}; onUpCombo2 = function() { unlisten("mousemove", onMvCombo2);}; onMvCombo2 = function() {anim(show("cmb2")); }; onDnCombo2 = function() {anim(show("cmb2"));听(“鼠标移动”,onMvCombo2);};听(“输入”,onInp);听(“改变”,onChg);听(“mousedown”,onDn1);听(“mousedown”,onDn2);听(“鼠标移动”,onMv1);听(“鼠标移动”,onMv2);听(“mouseup”,onUp);听(“mousedown”,onDnCombo1);听(“mousedown”,onDnCombo2);听(“鼠标”,onUpCombo2);表{边框折叠:折叠;字体:10pt Courier;} th, td {border:solid black 1px; padding: 0 0.5em;} input {margin: 2em;} li {padding-bottom: 1em;}
点击“整页”正确查看演示。
事件 | 范围值 | 随机更新指标 | |
---|---|---|---|
A td> | 输入 | 100 | - |
B | 改变 | 100 | - |
C | mousedown | 100 | - |
D | mousedown using requestAnimationFrame | 100 td> | - |
E | mousemove | 100 | - |
F | mousemove using requestAnimationFrame | 100 | - |
G | mouseup | 100 | - |
H | mousedown/move combo | 100 | - |
I | mousedown/move/upcombo | 100 | - |
touchmove
应该足够了,我认为在这种情况下可以像 mousemove
一样对待它。
Andrew Willem 的解决方案与移动设备不兼容。
这是对他的第二个解决方案的修改,适用于 Edge、IE、Opera、FF、Chrome、iOS Safari 和移动设备(我可以测试):
更新 1:删除了“requestAnimationFrame”部分,因为我同意没有必要:
var listener = function() {
// do whatever
};
slider1.addEventListener("input", function() {
listener();
slider1.addEventListener("change", listener);
});
slider1.addEventListener("change", function() {
listener();
slider1.removeEventListener("input", listener);
});
更新 2:对 Andrew 2016 年 6 月 2 日更新答案的回应:
谢谢,安德鲁 - 这似乎适用于我能找到的所有浏览器(Win 桌面:IE、Chrome、Opera、FF;Android Chrome、Opera 和 FF、iOS Safari)。
更新 3:if ("oninput in slider) 解决方案
以下似乎适用于上述所有浏览器。 (我现在找不到原始来源。)我正在使用它,但它随后在 IE 上失败了,所以我去寻找一个不同的来源,因此我最终来到了这里。
if ("oninput" in slider1) {
slider1.addEventListener("input", function () {
// do whatever;
}, false);
}
但在我检查您的解决方案之前,我注意到这在 IE 中再次起作用 - 也许还有其他一些冲突。
为了获得良好的跨浏览器行为和可理解的代码,最好将 onchange 属性与表单结合使用:
函数 showVal(){ valBox.innerHTML = inVal.value; }
同样使用oninput,直接改变值。
函数 showVal(){ valBox.innerHTML = inVal.value; }
如果您像我一样并且无法弄清楚为什么范围类型输入在任何移动浏览器上都不起作用,我将其发布为答案。如果您在笔记本电脑上开发移动应用程序并使用响应模式来模拟触摸,您会注意到当您激活触摸模拟器时范围甚至不会移动。当您停用它时,它开始移动。我继续尝试了 2 天,尝试了我能找到的关于该主题的每一段代码,但无法让它为我的生活服务。我在这篇文章中提供了一个可行的解决方案。
移动浏览器和混合应用
移动浏览器使用名为 Webkit for iOS 和 WebView for Android 的组件运行。 WebView/WebKit 使您能够嵌入 Web 浏览器,该浏览器没有任何 chrome 或 firefox(浏览器)控件,包括窗口框架、菜单、工具栏和滚动条到您的活动布局中。换句话说,移动浏览器缺少很多在普通浏览器中常见的 Web 组件。这是范围类型输入的问题。如果用户的浏览器不支持范围类型,它将回退并将其视为文本输入。这就是激活触摸模拟器时无法移动范围的原因。
在此处阅读有关浏览器兼容性的更多信息
jQuery 滑块
jQuery 提供了一个滑块,它以某种方式与触摸模拟一起使用,但它不稳定且不是很平滑。这对我来说并不令人满意,它可能也不适合你,但如果你将它与 jqueryUi 结合起来,你可以让它更顺利地工作。
最佳解决方案:Range Touch
如果您在笔记本电脑上开发混合应用程序,则可以使用一个简单易用的库来启用范围类型输入以处理触摸事件。
此库称为 Range Touch。
演示
有关此问题的更多信息,请在此处查看此线程
Recreating the HTML5 range input for Mobile Safari (webkit)?
另一种方法 - 只需在元素上设置一个标志,指示应处理哪种类型的事件:
function setRangeValueChangeHandler(rangeElement, handler) {
rangeElement.oninput = (event) => {
handler(event);
// Save flag that we are using onInput in current browser
event.target.onInputHasBeenCalled = true;
};
rangeElement.onchange = (event) => {
// Call only if we are not using onInput in current browser
if (!event.target.onInputHasBeenCalled) {
handler(event);
}
};
}
您可以使用 JavaScript "ondrag" 事件连续触发。由于以下原因,它比“输入”更好:
浏览器支持。可以区分“ondrag”和“change”事件。拖动和更改都会触发“输入”。
在 jQuery 中:
$('#sample').on('drag',function(e){
});
参考:http://www.w3schools.com/TAgs/ev_ondrag.asp
$("#myelement").on("input change", function() { doSomething(); });
与 jQuery。onchange()
不适用于像Android Chrome
和iOS Safari
这样的移动网络。有什么替代建议吗?