Reputation: 96
I'm wondering if there is any way to detect that numeric input's arrow has been clicked. OnChange event function is called then arrow has been clicked or input value has been changed with keyboard. I want to find a way to detect change when only arrow has been clicked.
Here is link of a little Demo and it's code
import React from "react";
export default function App() {
const [log, setLog] = React.useState("");
return (
<div className="App">
<input
type="number"
onChange={e => {
setLog(log + "+");
}}
/>
<br />
<span>{log}</span>
</div>
);
}
Upvotes: 2
Views: 4229
Reputation: 886
Use a combination of oninput and onkeydown.
let isKeyDown_flagForArrowInput = false // this flag is set from keydown when a user types a value.
function onKeyDown() {
isKeyDown_flagForArrowInput = true
}
function onInput(e) {
console.log('isKeyDown_FlagForArrowInput', this.isKeyDown_FlagForArrowInput)
if (this.isKeyDown_FlagForArrowInput) {
// INPUT Was triggered after a keydown i.e. user typed something
} else {
// INPUT Was triggered after an arrow key (or context menu paste or any other event that doesnt require interaction with the keyboard :D )
}
// reset the flag after input
this.isKeyDown_flagForArrowInput = false
}
Need to consider things like copy and paste from context menu (keyboard shortcuts will still trigger keydown).
But it gives the basic concept
Upvotes: 1
Reputation: 4987
There is no event fired when clicking on the arrows so here is what I can propose you, see this custom component on Stackblitz.
Here is the full code :
.css :
.custom-input {
display: flex;
}
.custom-input > .arrows {
margin-left: -21px;
}
/*+++++++++ ARROWS +++++++++++++*/
.arrows-component {
display: inline-block;
}
.arrows {
display: flex;
flex-direction: column;
align-items: center;
}
.arrows button {
background-color: transparent;
border: 1px solid transparent;
}
.arrows button:hover {
border: 1px solid rgba(0, 0, 0, .24);
}
.arrows button:focus {
border: 1px solid rgba(0, 0, 0, .24);
}
.arrow-top {
width: 0;
height: 0;
border-style: solid;
border-width: 0 3.5px 7px 3.5px;
border-color: transparent transparent #007bff transparent;
}
.arrow-bottom {
width: 0;
height: 0;
border-style: solid;
border-width: 7px 3.5px 0 3.5px;
border-color: #007bff transparent transparent transparent;
}
.js :
import React, { Component } from "react";
import { render } from "react-dom";
import "./style.css";
const App = () => {
return (
<div>
<CustomInput />
</div>
);
};
const CustomInput = () => {
const [currentValue, setCurrentValue] = React.useState(0);
const handleChanges = e => {
setCurrentValue(event.target.value.replace(/\D/, ""));
};
const topArrowClicked = (e) => {
setCurrentValue(prevState => prevState + 1);
}
const bottomArrowClicked = (e) => {
setCurrentValue(prevState => prevState - 1);
}
return (
<div className="custom-input">
<input
type="text"
value={currentValue}
pattern="[0-9]*"
onChange={e => handleChanges(e)}
/>
<span className="arrows">
<InputArrows topArrowClick={topArrowClicked} bottomArrowClick={bottomArrowClicked} />
</span>
</div>
);
};
const InputArrows = ({topArrowClick, bottomArrowClick}) => {
return (
<div className="arrows-component">
<div className="arrows">
<button onClick={topArrowClick}>
<div className="arrow-top" />
</button>
<button onClick={bottomArrowClick}>
<div className="arrow-bottom" />
</button>
</div>
</div>
);
};
render(<App />, document.getElementById("root"));
Here you have a full access to the method triggered when clicking on the top and bottom arrow, and you can implement whatever functionality you want (as a step or different behavior).
Upvotes: 0
Reputation: 11305
I think it is default arrow with customized design, so I don't want to add my arrows
No this is actually browser builtin input control if you inspect element
This is an example custom control using arrows made with css.
const { useState, useEffect } = React;
const NumberInput = ({value, onChange, onUpArrow, onDownArrow}) => {
return <div>
<input value={value} onChange={() => onChange(event.target.value)} type="text"/>
<div className="arrow arrow-up" onClick={onUpArrow}/>
<div className="arrow arrow-down" onClick={onDownArrow}/>
</div>
}
function App() {
const [log, setLog] = useState("");
const [value, setValue] = useState(0);
const onChange = (value) => {
setValue(value);
}
const onUpArrow = () => {
setValue(value => value + 1);
}
const onDownArrow = () => {
setValue(value => value - 1);
}
return (
<div className="App">
<NumberInput value={value} onChange={onChange} onUpArrow={onUpArrow} onDownArrow={onDownArrow} />
<span>{log}</span>
</div>
);
}
ReactDOM.render(
<App />,
document.getElementById('root')
);
.arrow {
border: solid black;
border-width: 0 3px 3px 0;
display: inline-block;
padding: 3px;
position: absolute;
cursor: pointer;
}
.arrow-up {
transform: rotate(-135deg);
-webkit-transform: rotate(-135deg);
margin-top: 5px;
margin-left: -15px;
}
.arrow-down {
transform: rotate(45deg);
-webkit-transform: rotate(45deg);
margin-top: 8px;
margin-left: -15px;
}
<script src="https://unpkg.com/react/umd/react.development.js"></script>
<script src="https://unpkg.com/react-dom/umd/react-dom.development.js"></script>
<script src="https://unpkg.com/babel-standalone@6/babel.min.js"></script>
<div id="root"></div>
Upvotes: 0
Reputation: 1270
I don't think you can attach an event on the arrows.
Though a workaround is possible, It assumes that if the difference between prev value and the current value is 1 then the arrow was clicked.
A couple of points to note:
import React from "react";
export default function App() {
const [log, setLog] = React.useState("");
const [prevVal, setPrevVal] = React.useState(0);
const setLogIfArrowClicked = e => {
const currentVal = e.target.value;
if (currentVal - prevVal === 1) {
setLog(`${log}+`);
} else if (currentVal - prevVal === -1) {
setLog(`${log}-`);
}
setPrevVal(currentVal);
};
return (
<div className="App">
<input type="number" onChange={setLogIfArrowClicked} />
<br />
<span>{log}</span>
</div>
);
}
Upvotes: 1