событие onchange на входе type=range не запускается в firefox при перетаскивании

когда я играл с <input type="range">, Firefox запускает событие onchange, только если мы опустим ползунок в новое положение, где Chrome и другие запускают события onchange во время перетаскивания ползунка.

как я могу сделать это при перетаскивании в firefox?

HTML-код

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

скрипт

function showVal(newVal){
  document.getElementById("valBox").innerHTML=newVal;
}

7 ответов


по-видимому, 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 thread дополнительные информация.


UPDATE: я оставляю этот ответ здесь в качестве примера того, как использовать события мыши для использования взаимодействия диапазона/слайдера в настольных (но не мобильных) браузерах. Однако теперь я тоже написал совершенно другой и, я считаю, лучший ответ в другом месте на этой странице, которая использует другой подход к предоставлению кросс-браузера-и-мобайл решение этой проблемы.

Оригинал ответ:

Summary: кросс-браузер, простой JavaScript (т. е. no-jQuery) решение, позволяющее считывать входные значения диапазона без использования on('input'... и/или on('change'..., которые работают несогласованно между браузерами.

на сегодняшний день (конец февраля 2016 года) по-прежнему существует несоответствие браузера, поэтому я предоставляю новую работу здесь.

проблема: при использовании ввода диапазона, т. е. слайдера,on('input'... обеспечивает непрерывно обновляемые значения диапазона в Mac и Windows Firefox, Chrome и Opera, а также Mac Safari, в то время как on('change'... сообщает только значение диапазона при наведении мыши. Напротив, в Internet Explorer (v11), on('input'... не работает вообще, а on('change'... постоянно обновляется.

я сообщаю здесь 2 стратегии для получения идентичных непрерывных отчетов о значениях диапазона во всех браузерах с использованием vanilla JavaScript (т. е. без jQuery) с помощью событий mousedown, mousemove и (возможно) mouseup.

Стратегия 1: Короче, но менее эффективным!--20-->

если вы предпочитаете более короткий код более эффективному, вы можете использовать это 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. Это также считывает слайдер по мере необходимости, но соответствующим образом прекращает его чтение, как только кнопка мыши отпущена. Существенная разница в том, что только начинает слушать "mousemove" после "mousedown", и он перестает слушать "mousemove" после "mouseup".

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"/>

Demo: более полное объяснение необходимости и реализации вышеуказанных обходных путей

следующий код более полно демонстрирует многочисленные аспекты этой стратегии. Объяснения встроены в демонстрацию:

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>

я публикую это как ответ, потому что он заслуживает быть собственным ответом, а не комментарием под менее полезным ответом. Я нахожу этот метод намного лучше, чем принятый ответ, поскольку он может хранить все js в отдельном файле из HTML.

ответ, предоставленный Джамрелианом в его комментарии под принятым ответом.

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

просто имейте в виду этот комментарий Хайме, хотя

просто обратите внимание, что с этим решением в chrome вы получите два вызова обработчика (по одному на событие), поэтому, если вы заботитесь об этом, вам нужно защититься от него.

как в нем будет срабатывать событие, когда вы остановили перемещение мыши, а затем снова, когда вы отпустите кнопку мыши.


резюме:

я предоставляю здесь нет-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 года различные браузеры отличаются с точки зрения того, как они реагируют на использование диапазона/слайдера. Пять сценариев уместно:

  1. начальная мышь-вниз (или touch-start) в текущей позиции слайдера
  2. начальная мышь-вниз (или touch-start) в новой позиции слайдера
  3. любое последующее движение мыши (или касания) после 1 или 2 вдоль ползунка
  4. любое последующее движение мыши (или касания) после 1 или 2 мимо любого конца слайдера
  5. окончательный mouse-up (или touch-end)

в следующей таблице показано, как по крайней мере три различные настольные браузеры отличаются своим поведением в отношении того, на какой из вышеперечисленных сценариев они отвечают:

table showing browser differences with respect to which events they respond to and when

устранение:

на onRangeChange функция обеспечивает последовательный и предсказуемый кросс-браузерный ответ на взаимодействие диапазона / слайдера. Это заставляет все браузеры вести себя в соответствии со следующей таблицей:

table showing behaviour of all browsers using the proposed solution

в IE11, код по сути, позволяет всему работать в соответствии с статус-кво, т. е. позволяет "change" событие для работы стандартным образом и "input" событие не имеет значения, поскольку оно никогда не срабатывает. В других браузерах,"change" событие эффективно глушится (чтобы предотвратить дополнительные, а иногда и не очевидные события от стрельбы). Кроме того,"input" событие запускает свой прослушиватель только при изменении значения диапазона / слайдера. Для некоторых браузеров (например, Firefox) это происходит потому, что слушатель заткнул в сценарии 1, 4 и 5 из списка выше.

(Если вам действительно требуется, чтобы слушатель был активирован в любом сценарии 1, 4 и / или 5, Вы можете попробовать включить "mousedown"/"touchstart", "mousemove"/"touchmove" и/или "mouseup"/"touchend" событий. Такое решение выходит за рамки данного ответа.)

функциональность в мобильных браузерах:

я тестировал этот код в настольных браузерах, но не на мобильных устройствах броузеры. Однако, в еще один ответ на этой странице MBourne показал, что мое решение здесь "...кажется, работает в каждом браузере, который я мог найти (Win desktop: 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 значения в строках A и C меняются в сценариях 2 и 3 выше, а строка B никогда не изменяется.
  • в Chrome и Safari значения в строках B и C меняются в сценариях 2 и 3 в то время как строка изменяется только для сценария 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 тип ввода диапазон слайдер события изменить входной браузер совместимость кросс-браузер desktop mobile no-jQuery


Андрей Виллем не совместимые устройства.

вот модификация его второго решения, которое работает в Edge, IE, Opera, FF, Chrome, iOS Safari и мобильных эквивалентах (которые я мог бы проверить):

Update 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: Ответ на Эндрю 2 июня 2016 обновленный ответ:

Спасибо, Андрей-это появляется для работы в каждом браузере, который я мог найти (Win desktop: IE, Chrome, Opera, FF; Android Chrome, Opera и FF, iOS Safari).

обновление 3: Если ("oninput в слайдере) решение

следующий, кажется, работает во всех вышеперечисленных браузерах. (Сейчас я не могу найти первоисточник.) Я использовал это, но впоследствии это не удалось на IE, и поэтому я пошел искать другой, поэтому я оказался здесь.

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

но прежде чем я проверил ваше решение, я заметил, что это снова работает в IE-возможно, был какой-то другой конфликт.


для хорошего кросс-браузерного поведения, менее понятного кода лучше всего использовать атрибут onchange в сочетании с формой:

это решение только для html / javascript и может также использоваться в строке.

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

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

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

вы можете использовать событие JavaScript "ondrag" для непрерывного запуска. Это лучше, чем "вход" по следующим причинам:

  1. поддержка браузеров.

  2. может различать события" ondrag "и" change". "input" срабатывает как для перетаскивания, так и для изменения.

в jQuery:

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

});

ссылка: http://www.w3schools.com/TAgs/ev_ondrag.asp