まとめ:
ここでは、現在のブラウザーでは不可能な範囲/スライダーの相互作用に一貫して応答する、jQuery を使用しないデスクトップとモバイルのクロスブラウザー機能を提供します。これは基本的に、すべてのブラウザーに、またはイベントon("change"...
のいずれかに対して IE11 のイベントをエミュレートするように強制します。新機能は…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);});
}
... wherer
は range 入力要素で、f
はリスナーです。リスナーは、範囲/スライダーの値を変更する操作の後に呼び出されますが、その値を変更しない操作 (現在のスライダー位置での最初のマウス操作またはタッチ操作、またはスライダーのいずれかの端から移動したときなど) の後では呼び出されません。
問題:
2016 年 6 月初旬の時点で、範囲/スライダーの使用に対する応答方法はブラウザーによって異なります。関連するシナリオは次の 5 つです。
- 現在のスライダー位置での最初のマウスダウン (またはタッチスタート)
- 新しいスライダー位置での最初のマウスダウン (またはタッチスタート)
- スライダーに沿って 1 回または 2 回移動した後のマウス (またはタッチ) の動き
- スライダーのいずれかの端を 1 または 2 通過した後のマウス (またはタッチ) の動き
- 最後のマウスアップ (またはタッチエンド)
次の表は、少なくとも 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);
wheremyRangeInputElmt
は目的の<input type="range">
DOM 要素で、は-like イベントで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 は 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 入力タイプ 範囲スライダー イベント 入力の変更 ブラウザーの互換性 クロスブラウザー デスクトップ モバイル jQuery なし