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

2020/10/09 05:21 · javascript ·  · 0评论

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

显然Chrome和Safari是错误的:onchange仅应在用户释放鼠标时才触发。要获取连续更新,您应该使用该oninput事件,该事件将从鼠标和键盘捕获Firefox,Safari和Chrome中的实时更新。

但是,oninputIE10不支持此功能,因此最好的选择是将两个事件处理程序结合起来,如下所示:

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

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

概要:

我在这里提供了无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) “(感谢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中,在上面的情况2和3中,行A和C的值都发生了变化,而行B则从不发生变化。
  • 在Chrome和Safari中,场景2和3中的B和C行中的值均发生变化,而场景5中的A行仅发生变化。
  • 在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的回答的启发另一个答案表明可以将“ input”和“ change”事件合并,并且所产生的代码将在台式机和移动浏览器中均有效。但是,该答案中的代码导致触发了隐藏的“额外”事件,这本身就是有问题的,并且触发的事件在浏览器之间有所不同,这是另一个问题。我在这里的实现解决了这些问题。

关键字:

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

更新:我在此留下此答案,作为一个示例,说明如何使用鼠标事件在台式机(而非移动设备)浏览器中使用范围/滑块交互。但是,我现在也写了一个完全不同的文章,并且我相信,此页上的其他地方会提供更好的答案,它使用不同的方法来提供跨浏览器的台式机和移动设备解决方案。

原始答案:

简介:一种跨浏览器的纯JavaScript(即no-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事件在使用香草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"/>

演示:有关上述变通办法的必要性和实现的完整说明

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

我将其发布为答案,因为它应该是自己的答案,而不是用处不大的答案下的评论。我发现此方法比接受的答案好得多,因为它可以将所有js与HTML放在单独的文件中。

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

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

不过请注意Jaime的评论

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

如上图所示,当您停止移动鼠标,然后再释放鼠标按钮时,它将触发该事件。

更新资料

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

如果您的功能触发了input,则change事件触发时极不可能出现问题

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

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

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(“在滑块上输入”)解决方案

以下似乎适用于所有上述浏览器。(我现在找不到原始来源。)我正在使用它,但是随后在IE上失败了,所以我一直在寻找另一个,因此我就到这里了。

if ("oninput" in slider1) {
    slider1.addEventListener("input", function () {
        // do whatever;
    }, false);
}

但是在检查您的解决方案之前,我注意到它在IE中又可以正常工作-也许还有其他冲突。

为了实现良好的跨浏览器行为和易于理解的代码,最好是结合使用以下形式的onchange属性:

function showVal(){
  valBox.innerHTML = inVal.value;

}
<form onchange="showVal()">
  <input type="range" min="5" max="10" step="1" id="inVal">
  </form>

<span id="valBox">
  </span>

oninput相同,值直接更改。

function showVal(){
  valBox.innerHTML = inVal.value;

}
<form oninput="showVal()">
  <input type="range" min="5" max="10" step="1" id="inVal">
  </form>

<span id="valBox">
  </span>

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

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”事件连续触发。由于以下原因,它比“输入”要好:

  1. 浏览器支持。

  2. 可以区分“ ondrag”事件和“ change”事件。拖动和更改都会触发“输入”。

在jQuery中:

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

});

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

本文地址:http://javascript.askforanswer.com/shuruleixingfanweishangdeonchangeshijianzaituodongshibuhuizaifirefoxzhongchufa.html
文章标签: ,   ,   ,   ,  
版权声明:本文为原创文章,版权归 javascript 所有,欢迎分享本文,转载请保留出处!

文件下载

老薛主机终身7折优惠码boke112

上一篇:
下一篇:

评论已关闭!