I would like to find a way to focus on the next field when I click enter in the input using React.js
#autobind
handleKeyPress(event){
if(event.key === 'Enter'){
this.refs.email.focus();
}
}
#autobind
handleKeyPressEmail(event){
if(event.key === 'Enter'){
this.refs.zip_code.focus();
}
}
<input
onKeyPress={this.handleKeyPress}
ref = 'name'
/>
<input
onKeyPress={this.handleKeyPressEmail}
ref = 'email'
/>
<input
ref = 'zip_code'
/>
This is the best way I have found so far, however I don't want to repeat myself by creating a function everytime I want that to happen. Is there a better and cleaner way to implement this?
If <form> is present:
function handleEnter(event) {
if (event.keyCode === 13) {
const form = event.target.form;
const index = Array.prototype.indexOf.call(form, event.target);
form.elements[index + 1].focus();
event.preventDefault();
}
}
...
<form>
<input onKeyDown={handleEnter} />
<input onKeyDown={handleEnter} />
<input />
</form>
CodePen
Without <form>:
function useFocusNext() {
const controls = useRef([]);
const handler = (event) => {
if (event.keyCode === 13) {
// Required if the controls can be reordered
controls.current = controls.current
.filter((control) => document.body.contains(control))
.sort((a, b) =>
a.compareDocumentPosition(b) & Node.DOCUMENT_POSITION_FOLLOWING
? -1 : 1
);
const index = controls.current.indexOf(event.target);
const next = controls.current[index + 1];
next && next.focus();
// IE 9, 10
event.preventDefault();
}
};
return useCallback((element) => {
if (element && !controls.current.includes(element)) {
controls.current.push(element);
element.addEventListener('keydown', handler);
}
}, []);
};
...
const focusNextRef = useFocusNext();
<input ref={focusNextRef} />
<input ref={focusNextRef} />
<button ref={focusNextRef}>Submit</button>
CodePen
You can use componentDidMount and auto bind refs through a for-in loop.
http://codepen.io/jzmmm/pen/PzZgRX?editors=0010
constructor() {
super();
this._handleKeyPress = this._handleKeyPress.bind(this);
}
// Loop through the ref's object, and bind each of them to onkeypress
componentDidMount() {
for (let x in this.refs) {
this.refs[x].onkeypress = (e) =>
this._handleKeyPress(e, this.refs[x]);
}
}
// This checks ENTER key (13), then checks if next node is an INPUT
// Then focuses next input box
_handleKeyPress(e, field) {
if (e.keyCode === 13) {
e.preventDefault(); // Prevent form submission if button present
let next = this.refs[field.name].nextSibling;
if (next && next.tagName === "INPUT") {
this.refs[field.name].nextSibling.focus();
}
}
}
render() {
return (
<form>
<input type="text" name="name" ref='name' />
<input type="text" name="email" ref='email' />
<input type="text" name="zip_code" ref='zip_code' />
</form>
);
}
Without <form> and TypeScript version.
Skip disabled inputs.
const onKeyPress: React.KeyboardEventHandler<HTMLInputElement> = useCallback(
(e) => {
if (e.key === "Enter") {
const inputs = Array.from(
// Get table or tbody whatever that contains all inputs. The number of parentElements depends on the structure of your html
e.currentTarget?.parentElement?.parentElement?.parentElement?.querySelectorAll(
"input"
) ?? []
).filter((e) => !e.disabled)
const index = inputs.indexOf(e.currentTarget)
inputs[index + 1]?.focus()
e.preventDefault()
}
},
[]
)
return <input type="number" onKeyPress={onKeyPress} />
This is how I managed to make it simpler:
#autobind
handleKeyPress(value, event){
if(event.key === 'Enter'){
this.refs[event].focus();
}
}
<input
onKeyPress={(event) => this.handleKeyPress('email', event)}
ref = 'name'
/>
<input
onKeyPress={(event) => this.handleKeyPress('zip_code', event)}
ref = 'email'
/>
<input
ref = 'zip_code'
/>
Related
I create a project in React. I would like that when the user clicks on the "submit" button it shows him an error message if in the input "FormDebut" he enters "text" type characters.
But I can't do this :'( , could you help me please? Thanks in advance !
FormulaireReservations.js :
export default function FormulaireReservation() {
return (
<div className="Formulaire">
<Logo/>
<h2><center>Formulaire de réservation</center></h2>
<FormDebut/>
<center><input type="submit" value="Submit" /></center>
</div>
);
}
Component FormDebut.js :
const [value, setValue] = React.useState("");
const onChange = (e) => {
const formattedValue = formatInput(e.target.value);
if (formattedValue.length < 6) {
setValue(formattedValue);
}
}
const formatInput = (input) => {
if (input.length > 2 && input[2] !== ":") {
return input.substring(0, 2) + ":" + input.slice(2);
}
return input;
}
return (
<div className="Form">
<div>Début : </div>
<input placeholder="00:00" type="text" onChange={onChange} value={value}/>
</div>
)
}
export default FormDebut; ```
Test this out in a new document, and then from there you can implement it into your work. You can have a parameter were user has to input a number between 1-10 if they do not then you can display an error message. If number is between 1-10 then you can redirect them. isNAN (is not a number) is a condition and is used for validating the input of the user. The function will check if the input is not a number, it will check if the input is less than 1 or more than 10, if all these conditions are true then it will display an error.
<p>Please input a number between 1 and 10:</p>
<input id="numb">
<button type="button" onclick="myFunction()">Submit</button>
<p id="demo"></p>
<script>
function myFunction() {
// Get the value of the input field with id="numb"
let x = document.getElementById("numb").value;
// If x is Not a Number or less than one or greater than 10
let text;
if (isNaN(x) || x < 1 || x > 10) {
text = "Input not valid";
} else {
text = "Input OK";
}
document.getElementById("demo").innerHTML = text;
}
</script>
I'm not sure if I understood your requirements, but seems that when you click submit button you want to check if the input is numeric value. If that's the case, this should work:
https://codesandbox.io/s/infallible-browser-74r5d7?file=/src/App.js
I moved the value state into the Form itself and passing it as a prop to its child. I also added a new state variable called error that is passed to the FormDebut. On click of the submit button, I strip the : from the value and check if I can parse it to a number. If not, I set the error, otherwise I clear out the error.
Hope that helps
Here is something you can modify your components:
in your FormulaireReservations.js:
export default function FormulaireReservation() {
const [value, setValue] = React.useState("");
const [errorMsg, setErrorMsg] = React.useState("");
const handleSubmission = () => {
if (isNaN(value)) {
setErrorMsg('Input should not be a text')
}
}
React.useEffect(() => {
setErrorMsg('')
}, [value])
return (
<div className="Formulaire">
<Logo />
<h2><center>Formulaire de réservation</center></h2>
<FormDebut value={value} setValue={setValue} msg={errorMsg} />
<center><input type="button" onClick={handleSubmission} value="Submit" /></center>
</div>
);
}
and in your FormDebut.js :
function FormDebut({ value, setValue, msg }) {
const onChange = (e) => {
const formattedValue = formatInput(e.target.value);
if (formattedValue.length < 6) {
setValue(formattedValue);
}
}
const formatInput = (input) => {
if (input.length > 2 && input[2] !== ":") {
return input.substring(0, 2) + ":" + input.slice(2);
}
return input;
}
return (
<div className="Form">
<div>Début : </div>
<input placeholder="00:00" type="text" onChange={onChange} value= {value} />
{ msg && <p>{msg}</p> }
</div>
)}export default FormDebut;
So, basically, the value and errorMsg state is passed to another component that contains the input field. Each time changes on the input field will also update the state declared in the parent component. When a user clicks on submit button, it calls a function that is checking if the value is a number or not. Based on that the errorMsg state is updated. And in the FormDebut component, error msg will be shown just after the input field if any msg was set.
I have an issue of updating state values of a particular key. Using multiple radio button and textbox.
Here is my state
this.state = {
PStudent:[{"flag_sts":1,"id":8472229,"remark":null,"status":"P","studentid":"12010013"},
{"flag_sts":1,"id":8472218,"remark":null,"status":"P","studentid":"12108051"},
{"flag_sts":1,"id":8472219,"remark":null,"status":"P","studentid":"12108052"}
],
};
On change value on radio:
const handleChange = (e,studentid) =>{
this.setState({
data: this.state.PStudent.map(item=>{
if (item.studentid !== e.target.name) {
return item;
}else{
return{
studentid: studentid,
status : e.target.value
}
}
})
})
}
And this is sending parameter form radio:
{(Object.keys(this.state.PStudent).length > 0) ? (
this.state.PStudent.map((v)=>(
<tr>
<td>{v.studentid}</td>
<td><input type="radio" name={v.studentid} value="P" onChange={(e)=>handleChange(e,v.studentid)} defaultChecked={(v.status == "P") ? true:false} /> </td>
<td><input type="radio" name={v.studentid} value="A" onChange={(e)=>handleChange(e,v.studentid)} defaultChecked={(v.status == "A") ? true:false} /> </td>
<td><input type="text" name="remarks" value="" /> </td>
</tr>
))
) : ''}
Would you like to help me how to update some value of particular key? In this case i would like to update value from key 'status' by radio button and key 'remarks' by text box. And object from PStudent will auto updated with the new value after do handleChange() by radio.
Thank you for your consider.
You may want to make use of the dynamic key and index here.
The dynamic key would allow you to reuse the same function for the value change.
The index can be used to identify the object's index in the array.
const handleChange = (e, index, field) =>{
const newPStudent = _.cloneDeep(this.state.PStudent); // or you can use this.state.PStudent.map(i => I);
newPStudent[index][field] = e.target.value
this.setState({PStudent: newPStudent})
}
{(Object.keys(this.state.PStudent).length > 0) ? (
this.state.PStudent.map((v, index)=>(
<tr>
<td>{v.studentid}</td>
<td><input type="radio" name={v.studentid} value="P" onChange={(e)=>handleChange(e, index, 'status')} defaultChecked={(v.status == "P") ? true:false} /> </td>
<td><input type="radio" name={v.studentid} value="A" onChange={(e)=>handleChange(e, index, 'status')} defaultChecked={(v.status == "A") ? true:false} /> </td>
<td><input type="text" name="remarks" value="" onChange={(e)=>handleChange(e, index, 'remark')}/> </td>
</tr>
))
) : ''}
If you are using underscore.js in your project, it's best to use _.cloneDeep() as it creates an independent copy of the object.
You can use functional version of setState as:
Live Demo
handleChange = (e, studentid) => {
const status = e.target.value;
this.setState((state) => {
return {
PStudent: state.PStudent.map((item) => {
if (item.studentid !== e.target.name) return item;
else return { ...item, status };
})
};
});
};
I'm trying to write easy validation code and I have trouble. I've created element div '._error-alert' and I cant remove it if the input isn't empty.
When I press submit appears my element '._error-alert' but it doesnt disapear when I try to type something there. I'll be very grateful if u help or at least show me the other path to solve it
const form = document.querySelector('.validation__form'),
reqItems = document.querySelectorAll('._req'),
emailTest = /^(([^<>()\[\]\.,;:\s#\"]+(\.[^<>()\[\]\.,;:\s#\"]+)*)|(\".+\"))#(([^<>()\.,;\s#\"]+\.{0,1})+[^<>()\.,;:\s#\"]{2,})$/,
onlyTextTest = /^[a-zA-Z0-9#]+$/,
onlyNums = /^[0-9]+$/;
const inputTest = (example, input) => example.test(input.value);
const formAddError = (input) => {
if (input.classList.contains('_req')) {
const createBlock = document.createElement('div');
createBlock.classList.add('_error-alert');
input.parentElement.insertAdjacentElement("beforeend", createBlock);
createBlock.innerText = `Invalid ${input.getAttribute("name")}!`;
}
input.parentElement.classList.add('_error');
input.classList.add('_error');
};
const formRemoveError = (input) => {
input.parentElement.classList.remove('_error');
input.classList.remove('_error');
};
// validates form if function validateForm didn't have any errors and removes my created elements '._error-alert'
const sendValidatedForm = (e) => {
e.preventDefault();
let error = validateForm(form);
if (error === 0) {
console.log('fine');
form.reset();
document.querySelectorAll('._error-alert').forEach((errorAlert) => {
errorAlert.remove();
});
}
};
form.addEventListener('submit', sendValidatedForm);
// there I want to check input and remove '._error-alert' if input isnt wrong
const checkInput = () => {
reqItems.forEach((reqInput, index) => {
reqInput.addEventListener('input', () => {
formRemoveError(reqInput);
});
});
};
checkInput();
const validateForm = (form) => {
let error = 0;
reqItems.forEach(reqInput => {
reqInput.value.trim();
formRemoveError(reqInput);
if (reqInput.getAttribute("name") == "email") {
if (!inputTest(emailTest, reqInput)) {
formAddError(reqInput);
error++;
}
} else if (reqInput.getAttribute("name") == "phone") {
if (!inputTest(onlyNums, reqInput) && reqInput.value.length < 8) {
formAddError(reqInput);
error++;
}
} else if (reqInput.getAttribute("name") == "name") {
if (!inputTest(onlyTextTest, reqInput)) {
formAddError(reqInput);
error++;
}
}
});
console.log(error);
return error;
};
<form action="" class="validation__form">
<div class="validation__input-list">
<div class="validation__input-item">
<input type="text" class="validation__input-input _req" name="name" placeholder="Name">
</div>
<div class="validation__input-item">
<input type="text" class="validation__input-input" name="surname" placeholder="Surname">
</div>
<div class="validation__input-item">
<input type="text" class="validation__input-input _req" name="phone" placeholder="Phone">
</div>
<div class="validation__input-item">
<input type="text" class="validation__input-input _req" name="email" placeholder="Email">
</div>
<div class="validation__input-item">
<input type="text" class="validation__input-input" name="password" placeholder="Password">
</div>
</div>
<button class="validation__form-btn">Submit</button>
</form>
Set the css visibility property of the element to hidden.
const error_element = document.getElementsByClassName('_error-alert')
error_element.style.visibility = 'hidden'
I got some inputs to create/update a formular,
1) if value={question}, everything works except that I can not delete the last character (= first character of the input)
2) if I dont mention value, it's all good except when I want to change questions orders with Chevron icon button, database is well changed but input value is still displayed at the last place.
<input
type="text"
value={question}
placeholder="blabla"
onChange={event => {
event.preventDefault();
const value = event.target.value;
setUpdatedQuestion(value);
}}
/>
I tried to add if (event.target.value == "" || event.target.value) to onChange but does not work either
OK I found something, but it is a McGyver tip, not very clean : adding one space before all new add question ahah. But then I can't see my placeholder anymore :/
FYI : question is coming from a questions.map, setUpdatedQuestion do update questions, newOrder also do update questions
//in QuestionsContent.js (questions is a questions tab)
const QuestionsContent = props => {
return (
<form onSubmit={onSubmit}>
{isLoading === false && questions.length > 0
? questions.map((question, i) => {
return (
<div key={i}>
<QuestionLine
{...question}
questions={questions}
setQuestions={setQuestions}
setNewOrder={setNewOrder}
/>
</div>
);
})
: null}
<button
onClick={event => {
event.preventDefault();
setAddQuestion({
question: null,
type: "texte"
});
}}
>
Add a question
</button>
</form>
);
};
//in QuestionLine.js:
const QuestionLine = ({
question,
setNewOrder,
setQuestions,
questions
}) => {
const [updatedQuestion, setUpdatedQuestion] = useState("");
// * UPDATE QUESTION *******************************
useEffect(() => {
if (updatedQuestion !== "") {
const tab = [];
for (let j = 0; j < questions.length; j++) {
if (j === i) {
const newObject = {
question: updatedQuestion,
type: type
};
console.log("adding updatedQuestion ===>", newObject);
tab.push(newObject);
} else {
tab.push(questions[j]);
}
}
setQuestions(tab);
setUpdatedQuestion("");
}
}, [updatedQuestion]);
return (
<div >
{/* QUESTION */}
<input
type="text"
value={question}
placeholder="blabla"
onChange={event => {
event.preventDefault();
const value = event.target.value;
setUpdatedQuestion(value);
}}
/>
</div>
);
};
thanks for your precious help
The problem probably comes from the condition in the useEffect : when you want to delete the last character of the string, the state of updatedQuestion is empty and therefore the condition is not executed
I have an input field within ReactJS:
<input
type="number"
onKeyPress={handleKeyPress}
maxLength={3}
min="0"
max="999"
step=".1"
onPaste={handlePaste}
pattern="[0-9]*"
/>
Here are the associated functions:
function handleKeyPress(e) {
console.log(e.target.value);
if (e.target.value.length >= e.target.maxLength) {
e.target.value = e.target.value.slice(0, e.target.maxLength);
e.preventDefault();
return;
}
const key = e.key;
if (!allowChars(key)) {
e.preventDefault();
}
}
function allowChars(charValue) {
const acceptedKeys = '0123456789.';
return acceptedKeys.indexOf(charValue) > -1;
}
function handlePaste(event) {
event.preventDefault();
}
When I log e.target.value, and type .... into the input field, the decimals do not register. Why is this? And how can I get them to register?
Thanks.
Your pattern attribute doesn't allow .. Change it to pattern="[0-9.]*".
There's probably no need to use pattern, though. Since it's type="number", it should only allow valid numbers, which makes the pattern redundant.
That's because you use the onKeyPress event, which will fire both for the key-down and key-up.
So when the key down is fired, your initial value is blank.
You can use the onChange event to trigger the handler only after the change accours:
onChange={handleKeyPress}
Running example:
function handleKeyPress(e) {
debugger
console.log(e.target.value);
if (e.target.value.length >= e.target.maxLength) {
e.target.value = e.target.value.slice(0, e.target.maxLength);
e.preventDefault();
return;
}
const key = e.key;
if (!allowChars(key)) {
e.preventDefault();
}
}
function allowChars(charValue) {
const acceptedKeys = '0123456789.';
return acceptedKeys.indexOf(charValue) > -1;
}
function handlePaste(event) {
event.preventDefault();
}
const App = () => (
<div >
<input
type="number"
onChange={handleKeyPress}
maxLength={3}
min="0"
max="999"
step=".1"
onPaste={handlePaste}
pattern="[0-9]*"
/>
</div>
);
ReactDOM.render(<App />, document.getElementById('root'));
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react-dom.min.js"></script>
<div id="root"></div>
Try setting your input tag with the step="any".