How to limit a textarea to 4 rows of bullet points? - javascript

I was wondering if it is possible to limit a user to only enter 4 lines in a text area. I have tried using maxRows, but that isn't working as I thought. Notice how I have put maxLength to 9999999, as I don't mind how much text is entered on each bullet point, I just want to limit it to a maximum of 4 new line characters/bullet points. If anyone has a solution to how I could accomplish this, that would be great.
<TextField
onKeyUp={handleInput}
inputProps={{
maxLength: 9999999
}}
sx={{ ...fieldCSS, width: '100%', marginTop: '6px' }}
multiline
rows={4}
value={details}
onChange={(event: React.ChangeEvent<HTMLInputElement>) => {
setDetails(event.target.value);
setCalled({ ...called, detail: true });
}}
error={!canSubmitDetails && called.detail}
helperText={detailsHelperText}
/>
See in the image below, I dont want the user being able to enter that 4th bullet point.
EDIT: after adding the suggested answer with a minor edit but event.stopPropagation(); isnt working
const handleKeyDown = (event: React.KeyboardEvent<HTMLInputElement>) => {
const { code, target } = event;
const { value } = target as HTMLInputElement;
if (code === 'Enter') {
if ([...value].filter((word) => word === '\n').length >= DETAIL_MAX_LINES) {
event.stopPropagation();
}
}
};
Cheers,
Has400

You can use the onKeyDown event to check if the user is pressing the enter key. If so, you can check if the number of lines is equal to 4. If so, you can prevent the default behavior of the enter key.
const handleKeyDown = (event: React.KeyboardEvent<HTMLTextAreaElement>) => {
if (event.key === 'Enter') {
const lines = event.currentTarget.value.split('\n');
if (lines.length === 4) {
event.preventDefault();
}
}
};
<TextField
onKeyDown={handleKeyDown}
...
/>
EDIT: It doesn’t look like the enter key method is working, so try using the onChange event to check if the number of lines is greater than 4.
const handleChange = (event: React.ChangeEvent<HTMLInputElement>) => {
const { value } = event.target;
if ([...value].filter((word) => word === '\n').length >= 4) {
event.stopPropagation();
}
};
<TextField
onChange={handleChange}
...

Related

Use react to focus input element with caret/text-cursor at end of text using onKeyDown

I have two input fields in my component:
const MyComponent = () => {
const onKeyDown = (event: React.KeyboardEvent<HTMLInputElement>) => {
if (event.key === 'ArrowLeft') {
ref.current?.setSelectionRange(value.length, value.length)
ref.current?.focus()
}
}
const [value, setValue] = useState('1234')
const ref = useRef<HTMLInputElement>(null)
return (
<>
<input
onChange={({target}) => setValue(target.value)}
value={value}
ref={ref}
type={'text'}/>
<input
onKeyDown={onKeyDown}
type={'text'}/>
</>
)
}
When i hit the left arrow-key on the second input, the first input should be focused, and the cursor should be at the end of the input text.
But the cursor is at the wrong place. Why is this not working?
Was the cursor moving one position to the left of the last character?
Interestingly, when using onKeyUp (the release of the key) rather than onKeyDown the issue seems to go away. I've listed that solution followed by a couple other examples with explanations below.
Solution
import { useRef, useState } from "react";
const MyComponent = () => {
const [value, setValue] = useState("1234");
const ref = useRef(null);
const onKeyUp = (event) => {
if (event.key === "ArrowLeft") {
const textInput = ref.current;
const len = value.length;
textInput.setSelectionRange(len, len);
textInput.focus();
}
};
return (
<>
<input
onChange={({ target }) => setValue(target.value)}
value={value}
ref={ref}
type={"text"}
/>
<input onKeyUp={onKeyUp} type={"text"} />
</>
);
};
export default MyComponent;
https://codesandbox.io/s/cursor-end-of-input-onkeyup-x6ekke
Explanation
My guess is that because onKeyUp naturally follows onKeyDown, when we receive the onKeyDown event in React, per your example code, the following sequence occurs (or generally speaking something like this is happening):
Inside onKeyDown...
Our cursor is moved to the very end of the text input.
ref.current?.setSelectionRange(value.length, value.length)
The text input receives focus.
ref.current?.focus()
Then, the release of the left arrow key causes onKeyUp event to run in the DOM (we haven't done anything to handle this in React) while the focus is now on the text input as a result of step 2 above. The default behavior pressing/releasing the left arrow key while the input has focus is to move the cursor one position to the left, placing it one position from the end of the input text.
Other Examples/Solutions
If you stick with the use of onKeyDown, here are a couple other examples.
event.preventDefault()
const onKeyDown = (event) => {
if (event.key === "ArrowLeft") {
event.preventDefault();
const textInput = ref.current;
const len = value.length;
textInput.setSelectionRange(len, len);
textInput.focus();
}
};
setTimeout()
const onKeyDown = (event) => {
if (event.key === "ArrowLeft") {
setTimeout(() => {
const textInput = ref.current;
const len = value.length;
textInput.setSelectionRange(len, len);
textInput.focus();
}, 0);
}
};
https://codesandbox.io/s/cursor-end-of-input-example-h1yrdr
My guess is that these workarounds effectively block the browser from firing the native key down and up events altogether or delay our handler from running until after the native events have fired, respectively.

Enter key for Route another page

I have a input for searchbox. I must make like; Write my words fors search then after i press enter it must need go another page with input value. So i can access that value with query string. So how can i route another page with value of input after i press enter ? Thank you for help! I Just add That codes for catch enter press.
useEffect(() => {
const listener = (event) => {
if (event.code === "Enter" || event.code === "NumpadEnter") {
alert("Enter key was pressed. Run your function.");
event.preventDefault();
}
};
document.addEventListener("keydown", listener);
return () => {
document.removeEventListener("keydown", listener);
};
}, []);
You don't necessarily have to set an event listener, using onKeyDown event handler will also do. Enter key has a code of 13, so we just have to detect that.
Keep your value in a state (here, myValue), detect that you've pressed Enter key (here, using keyPressHandler method), and finally, pass the parameter to your route.
import {useHistory} from "react-router-dom"
function App() {
let history = useHistory();
const [myValue, setMyValue] = useState("");
const handleChange = ({ target: { value } }) => {
setMyValue(value);
};
const keyPressHandler = (e) => {
if (e.which === 13) {
// alert("You pressed enter!");
history.push("/process/" + myValue);
}
};
return (
<div className="App">
<input value={myValue} onKeyDown={keyPressHandler} onChange={handleChange} />
</div>
);
}
UPDATE:
According to MDN Web Docs, e.which is non-standard [Source] and e.keyCode is deprecated [Source], so you should be using e.key instead like:
const keyPressHandler = (e) => {
if (e.key=== 'Enter') {
// alert("You pressed enter!");
history.push("/process/" + myValue);
}
};
Working CodeSandbox Link

Handling both clicks and keypresses in React number input not working

I have part of some React code below, where I have a number input, I'd like to prevent people being able to click below 0, which I've done with the min attribute. However I've seen that it's possible to still manually key in negative numbers. After some googling I've seen that people can handle this with a keyPress check and looking at the character codes but for some reason I can't quite get this to work.
Are both handlers clashing with each other? My goal is to allow click increments to work for positive numbers and prevent negative and decimal numbers being allowed in the input.
It's a controlled input, where value is coming from state.
const handleChange = (e) => {
console.log(e.target.value);
setCount(e.target.value);
};
const handleKeyPress = (e) => {
console.log(e.target.value);
console.log(e.charCode);
if (e.charCode >= 48 && e.charCode <= 57) {
setCount(e.target.value);
}
};
return (
<section>
<h3>Input test</h3>
<form onSubmit={handleSubmit}>
<label htmlFor="amount">paragraphs:</label>
<input
type="number"
name="amount"
id="amount"
min="0"
value={count}
onKeyPress={handleKeyPress}
onChange={handleChange}
/>
You can format your value to absolute integer in your handleChange function and it would remove the zero and decimal separator on re-render:
const handleChange = (e) => {
setCount(Math.abs(parseInt(e.target.value)));
};
Or if you absolutely want to prevent "0" and "." characters to be entered, you can use preventDefault in your handleKeyPress function:
const handleKeyPress = (e) => {
if (e.charCode === 45 || e.charCode === 46) {
return e.preventDefault()
}
};
In this case you would like to use onKeyPress as a validator only. If validation fails, you can call e.preventDefault(). This way it will prevent to trigger onChange:
const handleKeyPress = (e) => {
if (e.charCode >= 48 && e.charCode <= 57) return
e.preventDefault()
};

How to stop an event from being further processed in React

I have a text input in my React app which I don't want to take inputs which are greater than 100. For example, If the entered value is 105, an alert is created and the event is terminated i.e changing input value is not gonna happen. Now I couldn't find a way to do this inside onChange function. Any help would be highly appreciated.
<input onChange={handleChange} name="t"/>
handleChange = e => {
if(e.target.value > 100){
alert("High")
//Here I want to stop event so that changing text in the input doesn't happen
}
}
Make it a controlled input and only set the value if a condition is met.
const App = () => {
const [value, setValue] = React.useState("");
const handler = (e) => {
const value = Number(e.target.value);
value <= 100 && setValue(value);
};
return (
<input onInput={handler} type="number" value={value} />
);
}
ReactDOM.render(<App/>, document.getElementById("root"));
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.8.3/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.8.3/umd/react-dom.production.min.js"></script>
<div id="root"></div>
If I'm understanding you properly, if a specific condition is not met, you want to prevent the input from reflecting the text the user just entered.
In order to accomplish this, you'll need to control your input's value via state.
That means doing something like this:
<input onChange={handleChange} name="t" value={this.state.tInput}/>
handleChange = e => {
if(parseInt(e.target.value) > 100) {
alert("High")
// set input state here to previous value to re-render and remove unwanted input values from input
return this.setState(({tInput}) => ({tInput}))
}
return this.setState({tInput: e.target.value})
}
handleChange = e => {
if(e.target.value > 100){
alert("High");
e.target.value = "";
}
else {
// your normal processing
}
}
should work.
Explanation:
Your code would simply not be executed in case the if condition is true.
The line e.target.value = "" doesn't actually "not show" the users input (as asked for in comment), but rather overrides it with empty string.
Mention:
This solution has nothing to do with React, but rather works in any context.

Send cursor to the end of input value in React

I am dynamically passing a value to my input field when clicking delete (in order to edit the last input entry).
I can see that in Chrome once the input value gets rendered the cursor shows up a the beginning of the word, while in Safari and Firefox goes at the end of the value but the last letter gets deleted.
How can I always see the cursor at the end without deleting the last letter(unless I hit backspace twice)?
tagEvent(e) {
const tag = this.text.value;
const tagGroup = tag.split(" ");
const tiles = this.props.tiles;
const hasTiles = Object.keys(tiles).length > 0;
if(e.keyCode === 32 || e.keyCode === 13){
e.preventDefault();
tagGroup.map(tag => this.props.addTile(tag));
this.tagForm.reset();
}
if(e.keyCode === 8 && hasTiles && tag === '' ) {
this.props.editLastTile();
this.tagForm.reset();
}
}
render() {
return (
<div className="input-wrapper">
<form ref={(input) => this.tagForm = input}>
<input ref={(input) => this.text = input}
type="text"
name="new-item"
placeholder="type and press space"
autoComplete="off"
defaultValue={this.props.value}
onKeyDown={(e) => this.tagEvent(e)} />
</form>
</div>
)
}
Here a Pen with the full code
Thanks a lot for the help!
Another simple solution:
<input ref={ref => ref && ref.focus()}
onFocus={(e)=>e.currentTarget.setSelectionRange(e.currentTarget.value.length, e.currentTarget.value.length)}
/>
ref triggers focus, and that triggers onFocus to calculate the end and set the cursor accordingly.
You can explicitly set cursor position, to do so add this to Input:
componentDidUpdate(prevProps) {
if (prevProps.value !== this.props.value) {
this.text.selectionStart = this.text.value.length;
this.text.selectionEnd = this.text.value.length;
}
}
To prevent removing last character add a e.preventDefault() after if(e.keyCode === 8 && hasTiles && tag === '' ) {
Edited Pen
For those of you coming here trying to use this with react hooks 🙌
A simple texfield component that toggles the type of the input to password/text, this is the typical case where you would like to allow users to see their password by clicking on a button to toggle the type and see the value.
function TextField() {
const [type, setType] = useState('text');
const inputRef = useRef(null);
const onToggle = useCallback(() => {
setType(current => type === 'text' ? 'password' : 'text');
// Setting focus here
inputRef.current.focus();
}, []);
useEffect(() => {
// Moving cursor to the end
inputRef.current.selectionStart = inputRef.current.value.length;
inputRef.current.selectionEnd = inputRef.current.value.length;
}, [type]);
return (
<div>
<input
ref={inputRef}
type={type}
/>
<button onClick={onToggle}>toggle type</button>
</div>
);
}

Categories