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

IT技术 javascript html firefox input onchange
2021-01-21 07:20:21

当我使用 时<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)">

6个回答

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

但是,oninput在 IE10 中不受支持,因此最好的办法是组合两个事件处理程序,如下所示:

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

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

请注意,使用此解决方案,在 chrome 中,您将收到两次对处理程序的调用(每个事件一次),因此如果您关心这一点,则需要防范它。
2021-03-20 07:20:21
或者$("#myelement").on("input change", function() { doSomething(); });使用 jQuery。
2021-03-28 07:20:21
我在移动 chrome (v34) 中没有触发“更改”事件时遇到了问题,但是在等式中添加“输入”已经为我修复了它,同时在其他地方没有不利影响。
2021-04-01 07:20:21
@Jaime:“如果你关心那个,那么你需要提防它。 ”请问我该怎么做?
2021-04-09 07:20:21
可悲的是,onchange()在像Android Chrome和 之类的移动网络上不起作用iOS Safari任何替代建议?
2021-04-10 07:20:21

概括:

我在这里提供了一种非 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. 在新的滑块位置初始鼠标按下(或触摸启动)
  3. 沿滑块 1 或 2 之后的任何后续鼠标(或触摸)移动
  4. 在经过滑块任一端 1 或 2 次之后的任何后续鼠标(或触摸)移动
  5. 最终鼠标向上(或触摸结束)

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

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

解决方案:

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

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

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

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

请注意 Jaime 的这条评论

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

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

更新

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

如果您有一个函数在 on 上触发input,则在change事件触发时出现问题的可能性极小

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

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

它是 jQuery,所以它不工作的可能性很低。我自己还没有看到它在任何地方都不起作用。它甚至适用于移动设备,这很棒。
2021-03-30 07:20:21
加 1 对于唯一适用于 Internet Explorer 的解决方案!!你也在其他浏览器上测试过吗?
2021-04-01 07:20:21

更新:我将这个答案留在这里作为如何使用鼠标事件在桌面(但不是移动)浏览器中使用范围/滑块交互的示例。但是,我现在也写了一个完全不同的,我相信,在这个页面的其他地方更好的答案,它使用不同的方法来为这个问题提供跨浏览器的桌面和移动解决方案。

原答案:

简介: 一种跨浏览器的纯 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'...会不断更新。

我在这里报告了两种策略,通过使用 mousedown、mousemove 和(可能)mouseup 事件,在所有使用 vanilla JavaScript(即没有 jQuery)的浏览器中获得相同的连续范围值报告。

策略 1:更短但效率更低

如果您更喜欢更短的代码而不是更高效的代码,您可以使用第一个解决方案,它使用 mousesdown 和 mousemove 但不使用 mouseup。这会根据需要读取滑块,但在任何鼠标悬停事件期间会继续不必要地触发,即使用户没有单击并因此没有拖动滑块。它基本上在 'mousedown' 和 'mousemove' 事件期间读取范围值,使用requestAnimationFrame.

var rng = document.querySelector("input");

read("mousedown");
read("mousemove");
read("keydown"); // include this to also allow keyboard control

function read(evtType) {
  rng.addEventListener(evtType, function() {
    window.requestAnimationFrame(function () {
      document.querySelector("div").innerHTML = rng.value;
      rng.setAttribute("aria-valuenow", rng.value); // include for accessibility
    });
  });
}
<div>50</div><input type="range"/>

策略 2:更长但更有效

如果您需要更高效的代码并且可以容忍更长的代码长度,那么您可以使用以下使用 mousedown、mousemove 和 mouseup 的解决方案。这也会根据需要读取滑块,但只要释放鼠标按钮,就会适当地停止读取它。本质的区别是它只在'mousedown'之后开始监听'mousemove',在'mouseup'之后停止监听'mousemove'。

var rng = document.querySelector("input");

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

// include the following line to maintain accessibility
// by allowing the listener to also be fired for
// appropriate keyboard events
rng.addEventListener("keydown", listener);
<div>50</div><input type="range"/>

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

以下代码更全面地展示了该策略的许多方面。演示中嵌入了说明:

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

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:对安德鲁 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。我个人无法检查任何移动可能性。但这看起来是对我的答案的一个很好的更新,将其扩大到包括移动设备。(我假设“输入”和“更改”接受桌面系统上的鼠标输入和移动系统上的触摸输入。)非常好的工作,应该广泛适用,值得认可和实施!
2021-03-14 07:20:21
进一步挖掘后,我意识到您的解决方案有几个缺点。首先,我认为 requestAnimationFrame 是不必要的。其次,代码会触发额外的事件(在某些浏览器中至少会触发 3 个事件,例如 mouseup)。第三,触发的确切事件在某些浏览器之间有所不同。因此,我根据您使用“输入”和“更改”事件组合的最初想法提供了一个单独的答案,为您提供原始灵感的功劳。
2021-03-16 07:20:21