ドラッグ中に入力type = rangeのonchangeイベントがFirefoxでトリガーされない


252

で遊んだとき<input type="range">、スライダーがドラッグされている間にChromeなどがonchangeイベントをトリガーする新しい位置にスライダーをドロップした場合にのみ、Firefoxが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)">


4
範囲要素にフォーカスがある場合、矢印キーを使用してスライダーを移動できます。そしてその場合、また、onchange発砲しません。この問題を見つけたのは、その問題のトラブルシューティングでした。
Sildoreth、2015

回答:


452

どうやらChromeとSafariは間違っonchangeています。ユーザーがマウスを離したときにのみトリガーされます。継続的な更新を取得するには、oninputイベントを使用する必要があります。このイベントは、Firefox、Safari、Chromeのライブ更新をマウスとキーボードの両方からキャプチャします。

ただし、oninputはIE10ではサポートされていないため、次のように2つのイベントハンドラーを組み合わせることが最善策です。

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

詳細については、このBugzillaスレッドを確認してください。


113
または$("#myelement").on("input change", function() { doSomething(); });jQueryを使用します。
Alex Vang 2013年

19
このソリューションでは、Chromeではハンドラーへの2つの呼び出し(イベントごとに1つ)が発生するため、それを気にする場合は、それを防ぐ必要があることに注意してください。
ハイメ

3
モバイルクローム(v34)で「変更」イベントが発生しないという問題がありましたが、数式に「入力」を追加すると問題が解決し、他の場所では悪影響がありません。
brins0 2014

7
@ハイメ:「それを気にするなら、あなたはそれを警戒する必要があります。

2
残念ながら、およびのonchange()ようなモバイルWeb では機能しません。代替案はありますか?Android ChromeiOS Safari
Alston、2015

41

概要:

ここでは、jQueryを使用しないクロスブラウザーのデスクトップおよびモバイル機能を提供し、現在のブラウザーでは不可能な、範囲/スライダーの相互作用に一貫して応答します。基本的に、すべてのブラウザーにIE11のon("change"...イベントon("change"...またはそれらのイベントのいずれかをエミュレートするように強制し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月上旬の時点で、範囲/スライダーの使用に対するブラウザーの反応は異なります。5つのシナリオが関連します。

  1. 現在のスライダー位置での最初のマウスダウン(またはタッチスタート)
  2. 新しいスライダー位置での最初のマウスダウン(またはタッチスタート)
  3. スライダーに沿って1または2を押した後のマウス(またはタッチ)の動き
  4. スライダーのいずれかの端を1または2過ぎた後のマウス(またはタッチ)の動き
  5. 最後のマウスアップ(またはタッチエンド)

次の表は、対応する上記のシナリオのどれに関して、少なくとも3つの異なるデスクトップブラウザーの動作が異なるかを示しています。

どのイベントにいつ応答するかに関するブラウザの違いを示す表

解決:

この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では、行BとCの値は両方ともシナリオ2と3で変化しますが、行Aはシナリオ5でのみ変化します。
  • Firefoxでは、行Aの値はシナリオ5でのみ変更され、行Bは5つすべてのシナリオで変更され、行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入力タイプの範囲スライダーイベントは、入力ブラウザーの互換性を変更します。クロスブラウザーデスクトップモバイルno-jQuery


31

あまり役に立たない回答の下のコメントではなく、独自の回答であるに値するので、これを回答として投稿します。HTMLとは別のファイルにすべてのjsを保持できるため、この方法は受け入れられた回答よりもはるかに優れています。

承認された回答の下でのコメントでのJamrelianによる回答。

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

Jaimeによるこのコメントに注意してください。

このソリューションでは、Chromeではハンドラーへの2つの呼び出し(イベントごとに1つ)が発生することに注意してください。そのため、それを気にする場合は、それを防ぐ必要があります。

マウスの移動を停止するとイベントが発生し、マウスボタンを離すとイベントが発生します。

更新

changeイベントとinput、機能を2回トリガーするイベントに関して、これはほとんど問題ではありません。

で発火する関数inputがある場合、changeイベントが発火するときに問題が発生することはほとんどありません。

input範囲入力スライダーをドラッグすると、すばやく起動します。最後にもう1つの関数呼び出しの発火を心配することは、inputイベントである水の海と比較して、1滴の水を心配するようなものです。

changeイベントをまったく含める理由は、ブラウザーの互換性(主にIEとの)のためです。


プラス1 Internet Explorerで機能する唯一のソリューションです。他のブラウザでもテストしましたか?
ジェシカ

1
これはjQueryなので、機能しない可能性はかなり低いです。私はそれが自分でどこでも動かないのを見たことがありません。それは素晴らしいモバイルでも動作します。
Daniel Tonon、2015

30

更新:マウスイベントを使用して、デスクトップ(ただしモバイルではない)ブラウザーで範囲/スライダー操作を使用する方法の例として、この回答をここに残します。ただし、今はまったく別の方法でも書いているので、このページの他の場所で、この問題に対して異なるブラウザーのデスクトップとモバイルのソリューションを提供するために別のアプローチを使用する方が良いと思います

元の答え:

要約:使用せず範囲入力値を読み取ることを可能にするクロスブラウザ、普通の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イベントを使用して、バニラJavaScript(つまりjQueryなし)を使用するすべてのブラウザーで同一の連続範囲値レポートを取得する2つの戦略を報告します。

戦略1:短いが効率が悪い

より効率的なコードよりも短いコードを好む場合は、mousesdownとmousemoveを使用し、mouseupを使用しないこの最初のソリューションを使用できます。これにより、必要に応じてスライダーが読み取られますが、ユーザーがクリックしていないためスライダーをドラッグしていない場合でも、マウスオーバーイベント中に不必要に発火し続けます。基本的には、「マウスダウン」後と「マウスムーブ」イベント中の両方で範囲値を読み取り、それぞれを使用して少し遅延させます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"/>

デモ:上記の回避策の必要性と実装の詳細な説明

次のコードは、この戦略のさまざまな側面をより完全に示しています。説明はデモに埋め込まれています。


本当に良い情報。たぶん、いつかタッチイベントを追加することもできますか?touchmove十分なはずですしmousemove、この場合と同じように扱うことができると思います。
ニック

@nick、モバイルブラウザー機能を提供する別のソリューションについては、このページの他の回答を参照してください。
アンドリューウィレムス2016年

彼らはすべてのマウスの中心で、キーボードナビゲーションがイベントを発生しませんので、これは、アクセス可能な答えではありません
LocalPCGuy

@LocalPCGuy、あなたは正しい。私の答えは、スライダーの組み込みキーボード制御性を壊しました。キーボード制御を復元するようにコードを更新しました。私のコードが組み込みのアクセシビリティを破壊する他の方法を知っているなら、私に知らせてください。
アンドリューウィレムス2018年

7

Andrew Willemのソリューションは、モバイルデバイスと互換性がありません。

Edge、IE、Opera、FF、Chrome、iOS Safari、および同等のモバイル(私がテストできた)で機能する彼の2番目のソリューションの変更点を次に示します。

更新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日更新された回答への応答:

ありがとう、Andrew-私が見つけたすべてのブラウザー(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で再び機能していることに気付きました-おそらく他の競合があったのでしょう。


1
あなたのソリューションがMac FirefoxとWindows IE11の2つの異なるデスクトップブラウザーで動作することを確認しました。私は個人的にモバイルの可能性を確認することができませんでした。しかし、これは私の回答の素晴らしいアップデートのようであり、モバイルデバイスを含めるように広げています。(「入力」と「変更」は、デスクトップシステムでのマウス入力とモバイルシステムでのタッチ入力の両方を受け入れると想定しています。)広く適用でき、認識して実装する価値のある非常に優れた作業です。
Andrew Willems

1
さらに掘り下げた後、私はあなたのソリューションにいくつかの欠点があることに気づきました。まず、requestAnimationFrameは不要だと思います。次に、コードは追加のイベントを発生させます(一部のブラウザでは、マウスアップなどで少なくとも3つのイベント)。3番目に、発生する正確なイベントは、ブラウザによって異なります。したがって、「入力」イベントと「変更」イベントの組み合わせを使用するというあなたの当初の考えに基づいて、別の回答を提供しました。
アンドリューウィレムス2016年

2

ブラウザー間の適切な動作と理解しやすいコードを得るには、フォームを組み合わせて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>


0

さらに別のアプローチ-処理するイベントのタイプを通知する要素にフラグを設定するだけです。

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

-3

JavaScriptの「ondrag」イベントを使用して、継続的に起動できます。次の理由により、「入力」よりも優れています。

  1. ブラウザのサポート。

  2. 「オンドラッグ」イベントと「変更」イベントを区別できます。「入力」はドラッグと変更の両方で発生します。

jQueryの場合:

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

});

リファレンス:http : //www.w3schools.com/TAgs/ev_ondrag.asp

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.