Отключить прокрутку на `<input type = number>`


121

Можно ли отключить колесо прокрутки, изменяя число в поле ввода номера? Я испортил специфичный для webkit CSS, чтобы удалить счетчик, но я хотел бы полностью избавиться от этого поведения. Мне нравится использоватьtype=number потому что он вызывает красивую клавиатуру на iOS.


7
используйте тип ввода tel (type = "tel") вместо type = number. Появится всплывающая клавиатура с цифрами iOS.
— Praveen Vijayan

Что произойдет, если вы добавите onscrollобработчик только event.preventDefault()в нем?
— robertc

14
По моему скромному мнению, я даже не думаю, что это должна быть особенность. Если бы я имел право голоса в разработке браузера, я бы, вероятно, настоял на удалении этой функции. Это только раздражает, и я не знаю никого, кто бы им действительно воспользовался.
— Kirkland

3
Я полностью согласен с тобой, Киркланд. Прокрутка страницы с формой - это просто плохой UX, и когда элемент ввода числа попадает под вашу мышь, он начинает увеличиваться, и страница перестает прокручиваться. Совершенно дезориентирующий.
— kjs3

3
@PraveenVijayan: Хотя это обходной путь, он противоречит любой причине для использования новых типов ввода html5. В будущем может случиться так, что телефоны предоставят вам возможность выбирать номер из контактов или чего-либо еще, что будет выглядеть очень странно, если вы имели в виду, что это номер другого типа.
— awe

Ответы:


92

Предотвратить поведение по умолчанию события колеса мыши для элементов input-number, как это было предложено другими (вызов «blur ()» обычно не является предпочтительным способом сделать это, потому что это не то, что хочет пользователь).

НО. Я бы избегал постоянно прослушивать событие колесика мыши на всех элементах input-number и делал это только тогда, когда элемент находится в фокусе (когда проблема существует). В противном случае пользователь не сможет прокручивать страницу когда указатель мыши находится где-нибудь над элементом input-number.

Решение для jQuery:

// disable mousewheel on a input number field when in focus
// (to prevent Cromium browsers change the value when scrolling)
$('form').on('focus', 'input[type=number]', function (e) {
  $(this).on('wheel.disableScroll', function (e) {
    e.preventDefault()
  })
})
$('form').on('blur', 'input[type=number]', function (e) {
  $(this).off('wheel.disableScroll')
})

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


2
Отличный ответ. Думаю, если бы я делал это сегодня, я бы использовал Modernizr.touch для условного применения прослушивателя событий на устройствах без сенсорного экрана. В основном то, что сказал ниже user2863715.
— kjs3

Отличный ответ, но, по крайней мере, в webkit, это также предотвращает прокрутку окна, когда курсор находится над элементом ввода. Я думал, что события колесика мыши тоже должны пузыриться.
— Райан МакГири

При применении функции отключения прокрутки только к элементу, когда он находится в фокусе, это не должно препятствовать прокрутке, когда мышь находится над элементом ввода. Но это не позволяет прокручивать окно, когда элемент в фокусе. И да, почему событие
— колесика мыши не пузырится

6
Это хорошее исправление поведения, которого вообще не должно быть.
— Джонни

Это не работает на большинстве платформ типа unix gnome, osx, android (с помощью мыши), поскольку поведение прокрутки не требует, чтобы ввод был сфокусирован. В качестве альтернативы, я просто использовал текстовый тип ввода и наложил на него свое ограничение по количеству (потеря клавиш вверх / вниз, но также может быть добавлено с помощью js)
— zeachco


21

Один слушатель событий, чтобы управлять ими всеми

Это похоже на @Simon Перепелица «s ответа в чистом JS, но немного проще, так как она кладет слушатель событий на элементе документа и проверяет , если сфокусированный элемент ввод номера:

document.addEventListener("wheel", function(event){
    if(document.activeElement.type === "number"){
        document.activeElement.blur();
    }
});

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

document.addEventListener("wheel", function(event){
    if(document.activeElement.type === "number" &&
       document.activeElement.classList.contains("noscroll"))
    {
        document.activeElement.blur();
    }
});

с этим:

<input type="number" class="noscroll"/>

Если вход имеет класс noscroll, он не изменится при прокрутке, в противном случае все останется прежним.

Протестируйте здесь с помощью JSFiddle


Я вижу, это было отклонено. Если вы заметили проблему, пожалуйста, оставьте комментарий, проголосовав против. Если я что-то пропустил, то хотел бы узнать, что это такое. Спасибо.
— Питер Ракманьи

1
Для современного браузера используйте событие wheelвместо mousewheel. Ссылка: developer.mozilla.org/en-US/docs/Web/API/Element/wheel_event PS. Я не тот, кто голосует против.
— vee

19
input = document.getElementById("the_number_input")
input.addEventListener("mousewheel", function(event){ this.blur() })

http://jsfiddle.net/bQbDm/2/

Для примера jQuery и кросс-браузерного решения см. Связанный вопрос:

Слушатель событий HTML5 для прокрутки ввода чисел - только Chrome


2
Спасибо, Сеймон, мне удалось сделать это для ввода всех чисел с помощью jQuery следующим образом: $(':input[type=number]' ).live('mousewheel',function(e){ $(this).blur(); });я использовал размытие вместо предотвращения по умолчанию, чтобы он не блокировал прокрутку страницы!
— Тибо Колсон,

Хорошее дополнение к размытию, я не знал, что вы все еще можете разрешить прокрутку страницы. Я обновил свой ответ.
— Симон Перепелица

13
FYI, live()устарел. Теперь вы должны использовать on(): $(':input[type=number]').on('mousewheel',function(e){ $(this).blur(); });
— Саид Зебардаст

3
@SaeidZebardast Ваш комментарий заслуживает ответа
— snumpy

Для современного браузера используйте событие wheelвместо mousewheel. Ссылка: developer.mozilla.org/en-US/docs/Web/API/Element/wheel_event
— vee

16

Вы можете просто использовать атрибут HTML onwheel .

Этот параметр не влияет на прокрутку других элементов страницы.

И добавьте слушателя для всех входов, которые не работают во входах, динамически создаваемых позже.

Кроме того, вы можете удалить стрелки ввода с помощью CSS.

input[type="number"]::-webkit-outer-spin-button, 
input[type="number"]::-webkit-inner-spin-button {
    -webkit-appearance: none;
    margin: 0;
}
input[type="number"] {
    -moz-appearance: textfield;
}
<input type="number" onwheel="this.blur()" />


1
Спасибо брат!! он отлично работал в Angular - Материал 6
— Насируддин Сайед


Мы можем найти этот CSS во многих источниках. В любом случае CSS - это только бонус. Настоящее решение вопроса - это атрибут HTML onwheel. ;-)
— Maikon Matheus

5
Мне нравится это решение! Спасибо. В моем случае все onBlur()оказалось не так, как ожидалось. return falseВместо этого я установил простое, чтобы действительно «ничего не делать на колесе». <input type="number" onwheel="return false;">
— Дэниел

14

@Semyon Perepelitsa

Для этого есть лучшее решение. Размытие удаляет фокус с ввода, и это побочный эффект, который вам не нужен. Вместо этого вы должны использовать evt.preventDefault. Это предотвращает поведение ввода по умолчанию при прокрутке пользователем. Вот код:

input = document.getElementById("the_number_input")
input.addEventListener("mousewheel", function(evt){ evt.preventDefault(); })

6
Это работает, но событие не всплывает, поэтому страница не прокручивается. Есть ли способ сохранить поведение по умолчанию (прокрутка страницы) без размытия?
— GuiGS

Для современного браузера используйте событие wheelвместо mousewheel. Ссылка: developer.mozilla.org/en-US/docs/Web/API/Element/wheel_event
— vee

7

Сначала вы должны остановить событие колесика мыши:

  1. Отключение с помощью mousewheel.disableScroll
  2. Перехватывая это с e.preventDefault();
  3. Убрав фокус с элемента el.blur();

Первые два подхода останавливают прокрутку окна, а последний удаляет фокус с элемента; Оба эти результата являются нежелательными.

Один из обходных путей - использовать el.blur()и перефокусировать элемент после задержки:

$('input[type=number]').on('mousewheel', function(){
  var el = $(this);
  el.blur();
  setTimeout(function(){
    el.focus();
  }, 10);
});

3
Красивое решение, но при тестировании я обнаружил, что у этого решения есть побочный эффект в виде кражи фокуса. Я настроил это, чтобы иметь: тест, чтобы увидеть, был ли элемент в фокусе: var hadFocus = el.is(':focus');перед размытием, а затем охранник, чтобы установить тайм-аут только в том случае, если hasFocs был истинным.
— Роб Доусон

3

Для всех, кто работает с React и ищет решение. Я обнаружил, что самый простой способ - использовать свойство onWheelCapture в компоненте Input следующим образом:

onWheelCapture={e => { e.target.blur() }}


2

Предоставленные ответы не работают в Firefox (Quantum). Слушатель событий необходимо изменить с колеса мыши на колесо:

$(':input[type=number]').on('wheel',function(e){ $(this).blur(); });

Этот код работает в Firefox Quantum и Chrome.


2

Машинописный вариант

TypeScript должен знать, что вы работаете с HTMLElement для обеспечения безопасности типов, иначе вы увидите много Property 'type' does not exist on type 'Element'типов ошибок.

document.addEventListener("mousewheel", function(event){
  let numberInput = (<HTMLInputElement>document.activeElement);
  if (numberInput.type === "number") {
    numberInput.blur();
  }
});

1

Пытаясь решить эту проблему для себя, я заметил, что на самом деле можно сохранить прокрутку страницы и фокус ввода при отключении изменения числа, попытавшись повторно запустить событие catch на родительском элементе того, <input type="number"/>на котором оно было поймано. , просто так:

e.target.parentElement.dispatchEvent(e);

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

Другое решение, которое хорошо работает, по крайней мере, в Firefox и Chromium, - это временно создать <input>элемент readOnly, например:

function handleScroll(e) {
  if (e.target.tagName.toLowerCase() === 'input'
    && (e.target.type === 'number')
    && (e.target === document.activeElement)
    && !e.target.readOnly
  ) {
      e.target.readOnly = true;
      setTimeout(function(el){ el.readOnly = false; }, 0, e.target);
  }
}
document.addEventListener('wheel', function(e){ handleScroll(e); });

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

Точно так же (как объяснено в ответе Джеймса) вместо изменения readOnlyсвойства вы можете blur()заполнить поле, а затем focus()вернуть его обратно, но опять же, в зависимости от используемых стилей, может возникнуть некоторое мерцание.

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


1

Если вам нужно решение, для которого не нужен JavaScript, можно использовать сочетание некоторых функций HTML с псевдоэлементом CSS:

span {
  position: relative;
  display: inline-block; /* Fit around contents */
}

span::after {
  content: "";
  position: absolute;
  top: 0; right: 0; bottom: 0; left: 0; /* Stretch over containing block */
  cursor: text; /* restore I-beam cursor */
}

/* Restore context menu while editing */
input:focus {
  position: relative;
  z-index: 1;
}
<label>How many javascripts can u fit in u mouth?
  <span><input type="number" min="0" max="99" value="1"></span>
</label>

Это работает, потому что нажатие на содержимое <label> , связанное с полем формы, приведет к фокусированию поля. Однако «оконное стекло» псевдоэлемента над полем будет блокировать доступ событий колесика мыши к нему.

Недостатком является то, что кнопки счетчика вверх / вниз больше не работают, но вы сказали, что все равно их удалили.


Теоретически можно восстановить контекстное меню, не требуя, чтобы ввод был сначала сфокусирован: :hoverстили не должны срабатывать, когда пользователь прокручивает, поскольку браузеры избегают их пересчета во время прокрутки из соображений производительности, но я не полностью кроссбраузерность / устройство протестировал это.


0
function fixNumericScrolling() {
$$( "input[type=number]" ).addEvent( "mousewheel", function(e) {
    stopAll(e);     
} );
}

function stopAll(e) {
if( typeof( e.preventDefault               ) != "undefined" ) e.preventDefault();
if( typeof( e.stopImmediatePropagation     ) != "undefined" ) e.stopImmediatePropagation();
if( typeof( event ) != "undefined" ) {
    if( typeof( event.preventDefault           ) != "undefined" ) event.preventDefault();
    if( typeof( event.stopImmediatePropagation ) != "undefined" ) event.stopImmediatePropagation();
}

return false;
}

0

Самое простое решение - добавить onWheel={ event => event.currentTarget.blur() }}сам ввод.

Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.