[javascript] Disable scrolling on `<input type=number>`

Is it possible to disable the scroll wheel changing the number in an input number field? I've messed with webkit-specific CSS to remove the spinner but I'd like to get rid of this behavior altogether. I like using type=number since it brings up a nice keyboard on iOS.

This question is related to javascript css html forms

The answer is


@Semyon Perepelitsa

There is a better solution for this. Blur removes the focus from the input and that is a side affect that you do not want. You should use evt.preventDefault instead. This prevents the default behavior of the input when the user scrolls. Here is the code:

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

If you want a solution that doesn’t need JavaScript, combining some HTML functionality with a CSS pseudo-element does the trick:

_x000D_
_x000D_
span {_x000D_
  position: relative;_x000D_
  display: inline-block; /* Fit around contents */_x000D_
}_x000D_
_x000D_
span::after {_x000D_
  content: "";_x000D_
  position: absolute;_x000D_
  top: 0; right: 0; bottom: 0; left: 0; /* Stretch over containing block */_x000D_
  cursor: text; /* restore I-beam cursor */_x000D_
}_x000D_
_x000D_
/* Restore context menu while editing */_x000D_
input:focus {_x000D_
  position: relative;_x000D_
  z-index: 1;_x000D_
}
_x000D_
<label>How many javascripts can u fit in u mouth?_x000D_
  <span><input type="number" min="0" max="99" value="1"></span>_x000D_
</label>
_x000D_
_x000D_
_x000D_

This works because clicking on the contents of a <label> that’s associated with a form field will focus the field. However, the “windowpane” of the pseudo-element over the field will block mousewheel events from reaching it.

The drawback is that the up/down spinner buttons no longer work, but you said you had removed those anyway.


In theory, one could restore the context menu without requiring the input to be focused first: :hover styles shouldn’t fire when the user scrolls, since browsers avoid recalculating them during scrolling for performance reasons, but I haven’t thoroughly cross-browser/device tested it.


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

The provided answers do not work in Firefox (Quantum). The event listener needs to be changed from mousewheel to wheel:

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

This code works on Firefox Quantum and Chrome.


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

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

For jQuery example and a cross-browser solution see related question:

HTML5 event listener for number input scroll - Chrome only


Easiest solution is to add onWheel={ event => event.currentTarget.blur() }} on input itself.


While trying to solve this for myself, I noticed that it's actually possible to retain the scrolling of the page and focus of the input while disabling number changes by attempting to re-fire the caught event on the parent element of the <input type="number"/> on which it was caught, simply like this:

e.target.parentElement.dispatchEvent(e);

However, this causes an error in browser console, and is probably not guaranteed to work everywhere (I only tested on Firefox), since it is intentionally invalid code.

Another solution which works nicely at least on Firefox and Chromium is to temporarily make the <input> element readOnly, like this:

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

One side effect that I've noticed is that it may cause the field to flicker for a split-second if you have different styling for readOnly fields, but for my case at least, this doesn't seem to be an issue.

Similarly, (as explained in James' answer) instead of modifying the readOnly property, you can blur() the field and then focus() it back, but again, depending on styles in use, some flickering might occur.

Alternatively, as mentioned in other comments here, you can just call preventDefault() on the event instead. Assuming that you only handle wheel events on number inputs which are in focus and under the mouse cursor (that's what the three conditions above signify), negative impact on user experience would be close to none.


Typescript Variation

Typescript needs to know that you're working with an HTMLElement for type safety, else you'll see lots of Property 'type' does not exist on type 'Element' type of errors.

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

Most answers blur the number element even if the cursor isn't hovering over it; the below does not

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

https://jsfiddle.net/s06puv3j/1/


I have an alternative suggestion. The problem I see with most of the common recommendation of firing a blur event is that it has unexpected side-effects. It's not always a good thing to remove a focus state unexpectedly.

Why not this instead?

<input type="number" onwheel="return false;" />

It's very simple and straight-forward, easy to implement, and no side-effects that I can think of.


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

You can simply use the HTML onwheel attribute.

This option have no effects on scrolling over other elements of the page.

And add a listener for all inputs don't work in inputs dynamically created posteriorly.

Aditionaly, you can remove the input arrows with CSS.

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


One event listener to rule them all

This is similar to @Simon Perepelitsa's answer in pure js, but a bit simpler, as it puts one event listener on the document element and checks if the focused element is a number input:

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

If you want to turn off the value scrolling behaviour on some fields, but not others just do this instead:

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

with this:

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

If an input has the noscroll class it wont change on scroll, otherwise everything stays the same.

Test here with JSFiddle


For anyone working with React and looking for solution. I’ve found out that easiest way is to use onWheelCapture prop in Input component like this:

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


First you must stop the mousewheel event by either:

  1. Disabling it with mousewheel.disableScroll
  2. Intercepting it with e.preventDefault();
  3. By removing focus from the element el.blur();

The first two approaches both stop the window from scrolling and the last removes focus from the element; both of which are undesirable outcomes.

One workaround is to use el.blur() and refocus the element after a delay:

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

Examples related to javascript

need to add a class to an element How to make a variable accessible outside a function? Hide Signs that Meteor.js was Used How to create a showdown.js markdown extension Please help me convert this script to a simple image slider Highlight Anchor Links when user manually scrolls? Summing radio input values How to execute an action before close metro app WinJS javascript, for loop defines a dynamic variable name Getting all files in directory with ajax

Examples related to css

need to add a class to an element Using Lato fonts in my css (@font-face) Please help me convert this script to a simple image slider Why there is this "clear" class before footer? How to set width of mat-table column in angular? Center content vertically on Vuetify bootstrap 4 file input doesn't show the file name Bootstrap 4: responsive sidebar menu to top navbar Stylesheet not loaded because of MIME-type Force flex item to span full row width

Examples related to html

Embed ruby within URL : Middleman Blog Please help me convert this script to a simple image slider Generating a list of pages (not posts) without the index file Why there is this "clear" class before footer? Is it possible to change the content HTML5 alert messages? Getting all files in directory with ajax DevTools failed to load SourceMap: Could not load content for chrome-extension How to set width of mat-table column in angular? How to open a link in new tab using angular? ERROR Error: Uncaught (in promise), Cannot match any routes. URL Segment

Examples related to forms

How do I hide the PHP explode delimiter from submitted form results? React - clearing an input value after form submit How to prevent page from reloading after form submit - JQuery Input type number "only numeric value" validation Redirecting to a page after submitting form in HTML Clearing input in vuejs form Cleanest way to reset forms Reactjs - Form input validation No value accessor for form control TypeScript-'s Angular Framework Error - "There is no directive with exportAs set to ngForm"