37

This question was asked before but provided solution is just for jQuery, as I'm facing same problem in ReactJs.

Is it possible to disable the scroll wheel changing the number in an input number field? I removed spinner arrows using CSS but mouse wheel still working and messing the functionality.

I want input type number because it gives numeric keyboard on mobile/touch devices.

Faisal Janjua
  • 703
  • 1
  • 8
  • 15

9 Answers9

49

Simplest answer:

<input type="number" onWheel={(e) => e.target.blur()} />

e is short for event.

This also works:

<input type="number" onWheel={() => document.activeElement.blur()} />

Either of these can be used either in a functional or in a class component.

Zedd
  • 2,069
  • 2
  • 15
  • 35
  • 1
    For TypeScript you will need to cast it to the `HTMLElement` type... `(e.target as HTMLElement).blur();` – IROEGBU May 19 '23 at 11:25
17

You can blur the field on onWheel handler. Something like this

<input type='number' onWheel={ event => event.currentTarget.blur() } />
Amjad sibili
  • 580
  • 1
  • 7
  • 15
6

I had the same problem, except I was working on desktop version only. To blur the focus on the input as suggested in other answers works to stop the scrolling. But it wasn't what I wanted. I still want the user to be able to change the input with the keyboard.

So to disable scrolling on <input type=number> in React I added an onFocus property as follows:

<input
    //...some input properties...//
    type="number"
    onFocus={(e) => e.target.addEventListener("wheel", function (e) { e.preventDefault() }, { passive: false })}
/>

It worked fine for me. I hope it helps others.

Esfit
  • 61
  • 1
  • 5
3

I solved this using a functional component that wraps the input element and adds an event listener for "wheel" and prevents default behavior. I find this preferable to using blur() which may have undesirable UX.

// Simply a wrapper for <input type="number"/> that disables scrolling to increment

import { useEffect, useRef } from "react";

export const NumberInput = (props) => {
  const quantityInputRef = useRef(null);

  useEffect(() => {
    const ignoreScroll = (e) => {
      e.preventDefault();
    };
    quantityInputRef.current && quantityInputRef.current.addEventListener("wheel", ignoreScroll);
  }, [quantityInputRef]);

  return <input ref={quantityInputRef} type="number" {...props} />;
};

In production, I actually use this component to disable scroll-incrementing for the <TextField> component from material-ui instead of <input>. I've used the native input in my example because that's what the question was asking about.

ShaneSauce
  • 209
  • 2
  • 9
3

I don't think this is the best solution if you only want a numeric keyboard since there is a property that actually let you set whatever keyboard type you want, e.g. inputMode="numeric"

Changing global events is not good practice and neither is blurring out of the field.

Francois Vanderseypen
  • 1,432
  • 1
  • 12
  • 22
1

In react version you should use ref. Take a look at the example below :

import React, { Component, createRef } from "react";

class MyInput extends Component {
  constructor(props) {
    super(props);
    this.inputRef = createRef();
  }

  onWheel = () => {
    this.inputRef.current.blur();
  };

  render() {
    return (
      <div>
        My input number :
        <input type="number" ref={this.inputRef} onWheel={this.onWheel} />
      </div>
    );
  }
}

export default MyInput;

codesandbox here

Sephyre
  • 326
  • 2
  • 13
  • 1
    Thanks @Sephyre for above code, This worked fine but due to blur() method it is not user friendly. As I roll mouse wheel it looses focus and for input I have to click again in the input field to add a number. – Faisal Janjua Aug 03 '20 at 07:00
  • Look at this fiddle, this experience is really nice and smooth, https://jsfiddle.net/x6o7dzuv/ – Faisal Janjua Aug 03 '20 at 07:14
  • 1
    Add this to onWheel function `setTimeout(() => this.inputRef.current.focus(), 100);` – Sephyre Aug 03 '20 at 07:44
1
Prevent onWheel's event

docs

event blur

example in react


const inputElem = useRef();

onWheel={e=> {
  e.preventDefault();
  inputElem.current.blur();
 } 
}
  • in react code
import { useRef } from "react";
import "./styles.css";

export default function App() {
  const inputElem = useRef();
  const handleOnWheel = (e) => {
    // if not use preventDefault, it is working 
    e.preventDefault();
    // The blur event fires when an element has lost focus. The event does not bubble, 
    inputElem.current.blur();
  };
  return (
    <div className="App">
      <form>
        <input
          ref={inputElem}
          type="number"
          placeholder="type num"
          // prevent scroll on number input
          onWheel={handleOnWheel}
        />
        <button type="submit">send</button>
      </form>
    </div>
  );
}

Yewin
  • 160
  • 1
  • 7
0

If you want to remove this scroll feature from all the number input fields of your project as whole or alternatively you could only import it into your component and this is the solution that worked for me.

First you create a custom hook like this one in your desired location in your project.

import { useEffect } from 'react';
const DisableNumInputScroll = () => {
   const handleWheel = (event) => {
     const { type } = event.target;
     if(type === 'number'){
       event.preventDefault();
     }
   }
   useEffect(() => {
      document.addEventListener('wheel', handleWheel, { passive: false });

      return () => {
        document.removeEventListener('wheel', handleWheel);
      };
    }, []);

   return null;
};

export default DisableNumInputScroll;

Basically this adds a wheel event listener to the document and when the event occurs, it is checked if target of that event element is of type number. If yes, it will stop the default behavior which is responsible for the scroll increasing the number in input tag.

You can use this custom hook in your main App.js file like so,

import DisableNumInputScroll from './DisableNumInputScroll';

const App = () => {
  return (
    <>
      <DisableNumInputScroll />
      {/* Rest of your application */}
    </>
  );
};

export default App;
-1

this can also be achieved using css.

input[type=number]::-webkit-inner-spin-button, 
input[type=number]::-webkit-outer-spin-button
{ 
  -webkit-appearance: none; 
  margin: 0; 
}
Ifee
  • 1
  • 1