Onchange event on type type = диапазон не запускается в 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)">

SCRIPT

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

Ответ 1

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

Ответ 2

РЕЗЮМЕ:

Я предоставляю здесь возможность работы с несколькими браузерами без 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 или 2 вдоль слайдера
  • любое последующее движение мыши (или касания) после 1 или 2 мимо любого конца слайдера
  • конечная кнопка мыши (или сенсорная панель)

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

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

Решение:

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

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

В 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 значение в строке А изменяется только для сценария 5, изменения строки В для всех пяти сценариев, а строка С изменяется только для сценариев 2 и 3.
  • Во всех вышеперечисленных браузерах изменения в строке C (предлагаемое решение) идентичны, то есть только для сценариев 2 и 3.

Демо-код:

// main function for emulating IE11 "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>

Ответ 3

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

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

Описание: кросс-браузерное простое решение 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'... постоянно обновляется).

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

Стратегия 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"/>

Ответ 4

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

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

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

Просто знайте об этом комментарии Хайме, хотя

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

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

Обновить

По отношению к событию change и input вызывающему двойное срабатывание функции, это практически не проблема.

Если у вас отключена функция на input, крайне маловероятно, что при возникновении события change возникнет проблема.

input срабатывает быстро при перетаскивании ползунка ввода диапазона. Беспокойство по поводу еще одного вызова функции в конце похоже на беспокойство об одной капле воды по сравнению с океаном воды, который является input событиями.

Причина даже включения события change вообще ради совместимости браузера (в основном с IE).

Ответ 5

Решения 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: ответ Andrew 2 июня 2016 года обновленный ответ:

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

Обновление 3: если ( "oninput in slider" )

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

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

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

Ответ 6

Для хорошего кросс-браузерного поведения и понятного кода лучше всего использовать атрибут 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>

Ответ 7

Еще один подход - просто установить флаг на элементе, указывающий, какой тип события должен быть обработан:

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

Ответ 8

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

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

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

В jQuery:

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

});

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