295

で遊んだとき<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)">

4

9 に答える 9

520

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

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

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

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

于 2013-09-28T13:02:39.473 に答える
48

まとめ:

ここでは、現在のブラウザーでは不可能な範囲/スライダーの相互作用に一貫して応答する、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. 新しいスライダー位置での最初のマウスダウン (またはタッチスタート)
  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);

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 なし

于 2016-06-03T21:48:38.493 に答える
33

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

元の答え:

概要: ブラウザー間で一貫性を持たずに範囲入力値を読み取れるようにするクロスブラウザーのプレーンな 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 なし) を使用してすべてのブラウザーで同一の連続範囲値レポートを取得するための 2 つの戦略を報告します。

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

より効率的なコードよりも短いコードを好む場合は、mouseup ではなく mousesdown と mousemove を使用するこの最初のソリューションを使用できます。これは必要に応じてスライダーを読み取りますが、ユーザーがクリックしておらず、スライダーをドラッグしていない場合でも、マウスオーバー イベント中に不必要に発火し続けます。基本的に、「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"/>

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

次のコードは、この戦略のさまざまな側面をより完全に示しています。説明はデモンストレーションに組み込まれています。

var select, inp, listen, unlisten, anim, show, onInp, onChg, onDn1, onDn2, onMv1, onMv2, onUp, onMvCombo1, onDnCombo1, onUpCombo2, onMvCombo2, onDnCombo2;

select   = function(selctr)     { return document.querySelector(selctr);      };
inp = select("input");
listen   = function(evtTyp, cb) { return inp.   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); // random text
  };
};

onInp      =                  show("inp" )                                      ;
onChg      =                  show("chg" )                                      ;
onDn1      =                  show("mdn1")                                      ;
onDn2      = function() {anim(show("mdn2"));                                   };
onMv1      =                  show("mmv1")                                      ;
onMv2      = function() {anim(show("mmv2"));                                   };
onUp       =                  show("mup" )                                      ;
onMvCombo1 = function() {anim(show("cmb1"));                                   };
onDnCombo1 = function() {anim(show("cmb1"));   listen("mousemove", onMvCombo1);};
onUpCombo2 = function() {                    unlisten("mousemove", onMvCombo2);};
onMvCombo2 = function() {anim(show("cmb2"));                                   };
onDnCombo2 = function() {anim(show("cmb2"));   listen("mousemove", onMvCombo2);};

listen("input"    , onInp     );
listen("change"   , onChg     );
listen("mousedown", onDn1     );
listen("mousedown", onDn2     );
listen("mousemove", onMv1     );
listen("mousemove", onMv2     );
listen("mouseup"  , onUp      );
listen("mousedown", onDnCombo1);
listen("mousedown", onDnCombo2);
listen("mouseup"  , onUpCombo2);
table {border-collapse: collapse; font: 10pt Courier;}
th, td {border: solid black 1px; padding: 0 0.5em;}
input {margin: 2em;}
li {padding-bottom: 1em;}
<p>Click on 'Full page' to see the demonstration properly.</p>
<table>
  <tr><th></th><th>event</th><th>range value</th><th>random update indicator</th></tr>
  <tr id="inp" ><td>A</td><td>input                                </td><td>100</td><td>-</td></tr>
  <tr id="chg" ><td>B</td><td>change                               </td><td>100</td><td>-</td></tr>
  <tr id="mdn1"><td>C</td><td>mousedown                            </td><td>100</td><td>-</td></tr>
  <tr id="mdn2"><td>D</td><td>mousedown using requestAnimationFrame</td><td>100</td><td>-</td></tr>
  <tr id="mmv1"><td>E</td><td>mousemove                            </td><td>100</td><td>-</td></tr>
  <tr id="mmv2"><td>F</td><td>mousemove using requestAnimationFrame</td><td>100</td><td>-</td></tr>
  <tr id="mup" ><td>G</td><td>mouseup                              </td><td>100</td><td>-</td></tr>
  <tr id="cmb1"><td>H</td><td>mousedown/move combo                 </td><td>100</td><td>-</td></tr>
  <tr id="cmb2"><td>I</td><td>mousedown/move/up combo              </td><td>100</td><td>-</td></tr>
</table>
<input type="range" min="100" max="999" value="100"/>
<ol>
  <li>The 'range value' column shows the value of the 'value' attribute of the range-type input, i.e. the slider. The 'random update indicator' column shows random text as an indicator of whether events are being actively fired and handled.</li>
  <li>To see browser differences between input and change event implementations, use the slider in different browsers and compare A and&nbsp;B.</li>
  <li>To see the importance of 'requestAnimationFrame' on 'mousedown', click a new location on the slider and compare C&nbsp;(incorrect) and D&nbsp;(correct).</li>
  <li>To see the importance of 'requestAnimationFrame' on 'mousemove', click and drag but do not release the slider, and compare E&nbsp;(often 1&nbsp;pixel behind) and F&nbsp;(correct).</li>
  <li>To see why an initial mousedown is required (i.e. to see why mousemove alone is insufficient), click and hold but do not drag the slider and compare E&nbsp;(incorrect), F&nbsp;(incorrect) and H&nbsp;(correct).</li>
  <li>To see how the mouse event combinations can provide a work-around for continuous update of a range-type input, use the slider in any manner and note whichever of A or B continuously updates the range value in your current browser. Then, while still using the slider, note that H and I provide the same continuously updated range value readings as A or B.</li>
  <li>To see how the mouseup event reduces unnecessary calculations in the work-around, use the slider in any manner and compare H and&nbsp;I. They both provide correct range value readings. However, then ensure the mouse is released (i.e. not clicked) and move it over the slider without clicking and notice the ongoing updates in the third table column for H but not&nbsp;I.</li>
</ol>

于 2016-02-27T22:20:16.737 に答える
4

クロスブラウザーでの適切な動作とわかりやすいコードを実現するには、次のフォームと組み合わせて 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>

于 2016-09-15T07:37:07.793 に答える
1

あなたが私のようで、範囲タイプの入力がどのモバイルブラウザーでも機能しない理由がわからない場合に備えて、これを回答として投稿しています。ラップトップでモバイル アプリを開発し、レスポンシブ モードを使用してタッチをエミュレートする場合、タッチ シミュレーターを有効にしても範囲が動かないことに気付くでしょう。無効にすると動き始めます。私は 2 日間、このテーマで見つけたすべてのコードを試してみましたが、私の人生でそれを機能させることができませんでした。この投稿では、実用的なソリューションを提供します。

モバイル ブラウザとハイブリッド アプリ

モバイル ブラウザーは、iOS の WebkitおよびAndroid の WebViewと呼ばれるコンポーネントを使用して実行されます。WebView/WebKit を使用すると、ウィンドウ フレーム、メニュー、ツールバー、スクロール バーなどのクロムまたは Firefox (ブラウザー) コントロールを持たない Web ブラウザーをアクティビティ レイアウトに埋め込むことができます。つまり、モバイル ブラウザーには、通常のブラウザーに通常見られる多くの Web コンポーネントが欠けています。これは範囲型入力の問題です。ユーザーのブラウザーが範囲タイプをサポートしていない場合、フォールバックしてテキスト入力として扱います。これが、タッチ シミュレーターがアクティブなときに範囲を移動できない理由です。

ブラウザの互換性について詳しくはこちら

jQuery スライダー

jQueryは、タッチ シミュレーションで何とか動作するスライダーを提供しますが、途切れ途切れで、あまりスムーズではありません。私にとっては満足のいくものではありませんでしたし、おそらくあなたにとってもそうではないでしょうが、jqueryUi と組み合わせると、よりスムーズに動作するようになります。

ベストソリューション:レンジタッチ

ラップトップでハイブリッド アプリを開発する場合は、範囲型入力を有効にしてタッチ イベントを操作できるシンプルで使いやすいライブラリがあります。

このライブラリはRange Touchと呼ばれます。

デモ

この問題の詳細については、こちらのスレッドを確認してください

Mobile Safari (webkit) の HTML5 範囲入力を再作成していますか?

于 2021-01-10T23:59:21.373 に答える
1

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

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);
        }
    };
}
于 2018-12-21T12:59:56.497 に答える