Faisal Janjua
Faisal Janjua

Reputation: 866

Disable scrolling on `<input type=number>` in React

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.

Upvotes: 51

Views: 57526

Answers (15)

codePublic
codePublic

Reputation: 1

  <input
    id={id}
    type="number"
    // onFocus={(e) => { e.target.addEventListener('wheel', (wheelEvent) =>{ passive: false })}}  // allows - numbers change on scroll & prevent page scroll while hovering & changing numbers with scroll
    onFocus={(e) => e.target.addEventListener('wheel', (e) => e.preventDefault(), { passive: false })}  // dosent allow - numbers change on scroll 
    onWheel={(event) => window.scrollBy(0, event.deltaY)} // allows scrolling, cause preventDefault stops scrolling when hovered on input.
    {...inputProps}
  />

Upvotes: 0

Brian Min
Brian Min

Reputation: 297

Full Typescript React solution:

import { WheelEvent } from 'react';

<input onWheel={(e: WheelEvent<HTMLInputElement>) => e.currentTarget.blur()} />

Upvotes: 0

lsheva
lsheva

Reputation: 11

onWheel={(event) => event.currentTarget.blur()} plays well with typescript. event.target is not guaranteed to be the input element event listener attached to.

Upvotes: 1

R Simple
R Simple

Reputation: 9

It is not a native behavior of html input. Try to look for mousewheel listeners in your page. It should be some of imported libraries that add this changing by scrolling.

You can see that there is no changing by scrolling by default - https://developer.mozilla.org/en-US/docs/Web/HTML/Element/input/number

Upvotes: 0

manu
manu

Reputation: 1219

Another very simple way of achieving this, if you don't care about the controls either, is by putting the step property to 0.

<input type="number" step={0} />

and use CSS to hide the controls: https://www.w3schools.com/howto/howto_css_hide_arrow_number.asp

Upvotes: 1

Muhammad Rosyid
Muhammad Rosyid

Reputation: 196

you can use attribute onWheel like this:

onWheel={(e) => e.currentTarget.blur()}

Upvotes: 0

Toast64
Toast64

Reputation: 1

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;

Upvotes: 0

Yewin
Yewin

Reputation: 373

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

Upvotes: 1

Esfit
Esfit

Reputation: 116

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.

Upvotes: 10

Ifee
Ifee

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

Upvotes: -4

Zeddrix Fabian
Zeddrix Fabian

Reputation: 2566

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.

Upvotes: 66

yakin.rojinegro
yakin.rojinegro

Reputation: 31

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.

Upvotes: 3

ShaneSauce
ShaneSauce

Reputation: 221

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.

Upvotes: 4

Amjad sibili
Amjad sibili

Reputation: 1149

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

<input type='number' onWheel={ event => event.currentTarget.blur() } />

Upvotes: 30

Sephyre
Sephyre

Reputation: 426

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

Upvotes: 2

Related Questions