时,只有当我们将滑块放到一个新位置时,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; }" />
ChatGPT解决这个技术问题 Extra ChatGPT

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

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

如何在 Firefox 中进行拖动?

函数 showVal(newVal){ document.getElementById("valBox").innerHTML=newVal; }

如果范围元素具有焦点,您可以使用箭头键移动滑块。在这种情况下,onchange 也不会触发。在解决这个问题时,我发现了这个问题。

y
yousoumar

显然 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 了解更多信息。


$("#myelement").on("input change", function() { doSomething(); }); 与 jQuery。
请注意,使用此解决方案,在 chrome 中,您将收到对处理程序的两次调用(每个事件一次),因此如果您关心它,那么您需要防范它。
我在移动 chrome (v34) 中没有触发“更改”事件时遇到问题,但是在等式中添加“输入”对我来说已经解决了它,同时在其他地方没有不利影响。
@Jaime:“如果你关心它,那么你需要提防它。”请问我该怎么做?
遗憾的是,onchange() 不适用于像 Android ChromeiOS Safari 这样的移动网络。有什么替代建议吗?
y
yousoumar

概括:

我在这里提供了一个无 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

< th>事件数 tr>
row事件类型
A标准“改变”事件0
B标准“输入”事件0
C< /td>新的自定义“onRangeChange”事件0

信用:

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

关键词:

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


D
Daniel Tonon

我将其发布为答案,因为它应该是它自己的答案,而不是在不太有用的答案下发表评论。我发现这种方法比接受的答案要好得多,因为它可以将所有 js 保存在与 HTML 不同的文件中。

Jamrelian 在接受的答案下的评论中提供的答案。

$("#myelement").on("input change", function() {
    //do something
});

不过请注意 Jaime 的评论

请注意,使用此解决方案,在 chrome 中,您将收到对处理程序的两次调用(每个事件一次),因此如果您关心它,那么您需要防范它。

因为它会在您停止移动鼠标时触发事件,然后在您释放鼠标按钮时再次触发。

更新

对于导致功能触发两次的 change 事件和 input 事件,这几乎不是问题。

如果您在 input 上触发了某个函数,那么触发 change 事件时出现问题的可能性极小。

input 在您拖动范围输入滑块时快速触发。担心最后再触发一个函数调用就像担心一滴水与 input 事件的海洋相比。

甚至包含 change 事件的原因是为了浏览器兼容性(主要与 IE 兼容)。


加 1 对于唯一适用于 Internet Explorer 的解决方案!你在其他浏览器上也测试过吗?
它是 jQuery,所以它不工作的机会非常低。我还没有看到它在我自己的任何地方都不起作用。它甚至可以在手机上运行,这很棒。
A
Andrew Willems

更新:我在这里留下这个答案作为如何使用鼠标事件在桌面(但不是移动)浏览器中使用范围/滑块交互的示例。不过,我现在还在此页面的其他地方写了 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); // 包括无障碍 }); }); }

50

策略 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);

50

演示:更全面地解释上述变通方法的必要性和实施

下面的代码更充分地展示了这个策略的许多方面。演示中嵌入了解释:

变量选择,输入,听,不听,动画,显示,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输入100-
B改变100-
Cmousedown 100-
Dmousedown using requestAnimationFrame100-
Emousemove 100-
Fmousemove using requestAnimationFrame100-
Gmouseup 100-
Hmousedown/move combo 100-
Imousedown/move/upcombo 100-
    < li>“范围值”列显示范围类型输入的“值”属性的值,即滑块。 “随机更新指示器”列显示随机文本,作为事件是否被主动触发和处理的指示器。
  1. 要查看输入和更改事件实现之间的浏览器差异,请在不同浏览器中使用滑块并比较 A和 B。
  2. 要查看“mousedown”上“requestAnimationFrame”的重要性,请单击滑块上的新位置并比较 C (不正确)和 D (正确)。
  3. < li>要查看 'mousemove' 上的 'requestAnimationFrame' 的重要性,请单击并拖动但不要松开滑块,然后比较 E (通常落后 1 )和 F (正确)。
  4. 要查看为什么需要初始 mousedown(即查看为什么单独使用 mousemove 是不够的),请单击并按住但不要拖动滑块并比较 E (不正确)、F (不正确)和 H (正确)。
  5. 要查看鼠标事件组合如何为范围类型输入的持续更新提供解决方法,请以任何方式使用滑块并注意A 或 B 中的任何一个都会不断更新当前浏览器中的范围值。然后,在仍然使用滑块的同时,请注意 H 和 I 提供与 A 或 B 相同的持续更新的范围值读数。
  6. 要查看 mouseup 事件如何减少解决方法中不必要的计算,请使用以任何方式滑动并比较 H 和  I。它们都提供正确的范围值读数。但是,然后确保释放鼠标(即未单击)并将其移动到滑块上而不单击,并注意第三表列中 H 而不是 I 的持续更新。


真是好资料。有时甚至可以添加触摸事件? touchmove 应该足够了,我认为在这种情况下可以像 mousemove 一样对待它。
@nick,请在此页面上查看我的其他答案,了解提供移动浏览器功能的不同解决方案。
这不是一个可访问的答案,因为键盘导航不会触发事件,因为它们都是以鼠标为中心的
@LocalPCGuy,你是对的。我的回答破坏了滑块的内置键盘可控性。我已更新代码以恢复键盘控制。如果您知道我的代码破坏内置可访问性的任何其他方式,请告诉我。
M
MBourne

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 中再次起作用 - 也许还有其他一些冲突。


我确认您的解决方案适用于两种不同的桌面浏览器:Mac Firefox 和 Windows IE11。我个人无法检查任何移动可能性。但这看起来像是对我的答案的一个很好的更新,将其扩展到包括移动设备。 (我假设“输入”和“更改”在桌面系统上接受鼠标输入,在移动系统上接受触摸输入。)非常好的工作,应该广泛适用,值得得到认可和实施!
进一步挖掘后,我意识到您的解决方案有几个缺点。首先,我认为 requestAnimationFrame 是不必要的。其次,代码会触发额外的事件(在某些浏览器中,至少会触发 3 个事件,例如 mouseup)。第三,某些浏览器触发的确切事件不同。因此,我根据您使用“输入”和“更改”事件组合的最初想法提供了一个单独的答案,为您提供了最初的灵感。
M
Mark Skelton

为了获得良好的跨浏览器行为和可理解的代码,最好将 onchange 属性与表单结合使用:

函数 showVal(){ valBox.innerHTML = inVal.value; }

同样使用oninput,直接改变值。

函数 showVal(){ valBox.innerHTML = inVal.value; }


G
Grogu

如果您像我一样并且无法弄清楚为什么范围类型输入在任何移动浏览器上都不起作用,我将其发布为答案。如果您在笔记本电脑上开发移动应用程序并使用响应模式来模拟触摸,您会注意到当您激活触摸模拟器时范围甚至不会移动。当您停用它时,它开始移动。我继续尝试了 2 天,尝试了我能找到的关于该主题的每一段代码,但无法让它为我的生活服务。我在这篇文章中提供了一个可行的解决方案。

移动浏览器和混合应用

移动浏览器使用名为 Webkit for iOSWebView for Android 的组件运行。 WebView/WebKit 使您能够嵌入 Web 浏览器,该浏览器没有任何 chrome 或 firefox(浏览器)控件,包括窗口框架、菜单、工具栏和滚动条到您的活动布局中。换句话说,移动浏览器缺少很多在普通浏览器中常见的 Web 组件。这是范围类型输入的问题。如果用户的浏览器不支持范围类型,它将回退并将其视为文本输入。这就是激活触摸模拟器时无法移动范围的原因。

在此处阅读有关浏览器兼容性的更多信息

jQuery 滑块

jQuery 提供了一个滑块,它以某种方式与触摸模拟一起使用,但它不稳定且不是很平滑。这对我来说并不令人满意,它可能也不适合你,但如果你将它与 jqueryUi 结合起来,你可以让它更顺利地工作。

最佳解决方案:Range Touch

如果您在笔记本电脑上开发混合应用程序,则可以使用一个简单易用的库来启用范围类型输入以处理触摸事件。

此库称为 Range Touch

演示

有关此问题的更多信息,请在此处查看此线程

Recreating the HTML5 range input for Mobile Safari (webkit)?


N
Nikita

另一种方法 - 只需在元素上设置一个标志,指示应处理哪种类型的事件:

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

P
Pang

您可以使用 JavaScript "ondrag" 事件连续触发。由于以下原因,它比“输入”更好:

浏览器支持。可以区分“ondrag”和“change”事件。拖动和更改都会触发“输入”。

在 jQuery 中:

$('#sample').on('drag',function(e){

});

参考:http://www.w3schools.com/TAgs/ev_ondrag.asp


关注公众号,不定期副业成功案例分享
关注公众号

不定期副业成功案例分享

领先一步获取最新的外包任务吗?

立即订阅