подія onchange на тип вводу = діапазон не спрацьовує у firefox під час перетягування


252

Коли я грав із <input type="range">, Firefox запускає подію зміни, лише якщо ми перекинемо повзунок на нове положення, де Chrome та інші запускають події зміни, коли повзунок перетягується.

Як я можу це зробити при перетягуванні у 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не стріляє. Саме у вирішенні цієї проблеми я знайшов це питання.
Сілдорет

Відповіді:


452

Мабуть, 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 для отримання додаткової інформації.


113
Або $("#myelement").on("input change", function() { doSomething(); });з jQuery.
Алекс Ван

19
Тільки зауважте, що за допомогою цього рішення, у хромі, ви отримаєте два дзвінки до обробника (один за подію), тож якщо ви піклуєтеся про це, то вам потрібно захистити його.
Хайме

3
У мене виникли проблеми з подією "зміни", не запускаючи мобільний хром (v34), але додавання "вводу" в рівняння виправило це для мене, не маючи жодного згубного впливу в іншому місці.
brins0

7
@Jaime: " якщо ти про це піклуєшся , то потрібно захиститись від цього ". Як я можу це зробити, будь ласка?

2
На жаль, onchange()не працює в мобільній мережі, як Android Chromeі iOS Safari. Будь-яка альтернативна пропозиція?
Альстон

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

  1. початкове натискання миші (або сенсорний старт) у поточному положенні слайдера
  2. початкове натискання миші (або сенсорний старт) у новому положенні слайдера
  3. будь-який наступний рух миші (або дотик) після 1 або 2 по повзунку
  4. будь-який наступний (миттєвий) рух миші після 1 або 2 повз будь-якого кінця повзунка
  5. остаточне миші (або сенсорне)

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

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

Рішення:

Ця 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) " . (Спасибі М.Бурне.)

Використання:

Щоб скористатися цим рішенням, включіть 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, тоді як рядок A змінюється лише для сценарію 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>

Кредит:

Хоча реалізація тут значною мірою власна, вона надихнула відповідь М.Бурна . Ця інша відповідь передбачає, що події "введення" та "зміни" можна об'єднати і що отриманий код буде працювати як у настільних, так і в мобільних браузерах. Однак код у цій відповіді призводить до вилучення прихованих "зайвих" подій, що саме по собі є проблематичним, а випущені події відрізняються між браузерами, подальша проблема. Моя реалізація тут вирішує ці проблеми.

Ключові слова:

Події повзунка діапазону вводу JavaScript змінюють порівнянність вхідного браузера крос-браузерний настільний мобільний no-jQuery


31

Я публікую це як відповідь, оскільки він заслуговує на те, щоб це була власна відповідь, а не коментар під менш корисною відповіддю. Я вважаю цей метод набагато кращим, ніж прийнятий відповідь, оскільки він може зберігати всі js в окремому файлі від HTML.

Відповідь, яку надав Джамреліан у своєму коментарі під прийнятою відповіддю.

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

Тільки будьте в курсі цього коментаря Хайме, хоча

Тільки зауважте, що за допомогою цього рішення, у хромі, ви отримаєте два дзвінки до обробника (один за подію), тому якщо ви дбаєте про це, то вам потрібно захистити його.

Так як в ньому буде запущена подія, коли ви перестали рухати мишу, а потім знову, коли ви відпустите кнопку миші.

Оновлення

Стосовно changeподії та inputподії, що спричиняє функціональність двічі, це, як правило, не проблема.

Якщо у вас функція увімкнена input, навряд чи виникне проблема, коли changeподія запуститься.

inputспрацьовує швидко під час перетягування повзунка вводу діапазону. Турбуватися про ще одну функціональну стрільбу в кінці, це як побоювання однієї краплі води порівняно з океаном води, який є inputподіями.

Причина навіть взагалі включати changeподію - це сумісність браузера (головним чином з IE).


плюс 1 Для єдиного рішення, яке працює з Internet Explorer !! Ви протестували його і в інших браузерах?
Джессіка

1
Це jQuery, тому шанси його не працювати досить низькі. Я сам не бачив, щоб він ніде не працював. Він навіть працює на мобільних, що чудово.
Даніель Тонон

30

ОНОВЛЕННЯ: Цю відповідь я залишаю тут як приклад того, як використовувати події миші для використання взаємодії діапазону / слайдера у настільних (але не мобільних) браузерах. Однак я зараз також написав зовсім іншу і, я вважаю, кращу відповідь в іншому місці на цій сторінці, що використовує інший підхід до забезпечення вирішення цієї проблеми між браузером настільних і мобільних пристроїв .

Оригінальна відповідь:

Короткий зміст: простого браузера, простого JavaScript (тобто не-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 стратегії, щоб отримати однакове звітування про значення безперервного діапазону у всіх браузерах, використовуючи ванільний JavaScript (тобто не jQuery), використовуючи події mousedown, mousemove та (можливо) mouseup.

Стратегія 1: Коротше, але менш ефективно

Якщо ви віддаєте перевагу коротший код над більш ефективним кодом, ви можете використовувати це перше рішення, яке використовує перемикання миші та переміщення миші, а не мишання. Він читає слайдер за потребою, але продовжує стріляти без необхідності під час будь-яких подій на миші, навіть коли користувач не натиснув і, таким чином, не перетягує повзунок. Він по суті зчитує значення діапазону як після '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"/>

Демонстрація: Повніше пояснення необхідності та впровадження вищезазначених робіт

Наступний код більш повно демонструє численні аспекти цієї стратегії. Пояснення закладені в демонстрації:


Дійсно хороша інформація. Можливо, коли-небудь додавати подій на дотик? touchmoveповинно бути достатньо, і я думаю, що до цього можна ставитися так само, як і mousemoveв цьому випадку.
нік

@nick, дивіться іншу відповідь на цій сторінці щодо іншого рішення, яке забезпечує функціональність браузера для мобільних пристроїв.
Ендрю Віллемс

Це не доступна відповідь, оскільки навігація на клавіатурі не призведе до запуску подій, оскільки всі вони орієнтовані на мишу
LocalPCGuy

@LocalPCGuy, ти маєш рацію. Моя відповідь порушила вбудовану клавіатуру керування повзунками. Я оновив код, щоб відновити управління клавіатурою. Якщо ви знаєте про будь-які інші способи, який мій код порушує вбудовану доступність, повідомте мене.
Ендрю Віллемс

7

Рішення Ендрю Віллема не сумісні з мобільними пристроями.

Ось модифікація його другого рішення, яке працює в 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: Відповідь на оновлену відповідь Андрія 2 червня 2016 року:

Дякую, Ендрю - це, здається, працює в кожному браузері, який я міг знайти (Win desktop: 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. Я особисто не міг перевірити жодних мобільних можливостей. Але це виглядає як чудове оновлення моєї відповіді, яке розширює його на мобільні пристрої. (Я припускаю, що "введення" та "зміна" приймають обидва введення миші на настільній системі, а сенсорний вхід на мобільній системі.) Дуже приємна робота, яка повинна бути широко застосована і варто отримати розпізнавання та реалізацію!
Ендрю Віллемс

1
Після подальшого копання я зрозумів, що ваше рішення має кілька недоліків. По-перше, я вважаю, що запитAnimationFrame непотрібний. По-друге, код запускає додаткові події (принаймні 3 події, наприклад, миша в деяких браузерах). По-третє, точні розгорнуті події відрізняються між деякими браузерами. Таким чином, я дав окрему відповідь, виходячи з вашої початкової ідеї використовувати поєднання подій "введення" та "змінити", надаючи вам подяку за оригінальне натхнення.
Ендрю Віллемс

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

Ви можете використовувати події "ondrag" JavaScript для постійного запуску. Це краще, ніж "введення" через наступні причини:

  1. Підтримка браузера.

  2. Може розрізняти подію "ондраг" та "змінити". "Вхід" запускається як для перетягування, так і для зміни.

У jQuery:

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

});

Довідка: http://www.w3schools.com/TAgs/ev_ondrag.asp

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.