I have a controlled input that has a value initially showing.
I have set that input to autoFocus but the cursor appears at the beginning of the input when I am wanting it to appear at the end. I understand this might be because the autoFocus is added before the value is but I'm not 100% sure.
What would be the best way to accomplish the cursor initializing at the end of the input field?
var Test = React.createClass({
getInitialState: function() {
return {
teamId: 'fdsfds'
};
},
render: function() {
return (
<input type="text" autoFocus value={this.state.teamId} onChange={this.setTeamId} />
);
},
setTeamId: function(event) {
this.setState({ teamId: id });
},
});
ReactDOM.render(
<Test />,
document.getElementById('container')
);
https://jsfiddle.net/69z2wepo/34486/
One solution:
<input
type="text"
autoFocus
value={this.state.teamId}
onChange={this.setTeamId}
onFocus={function(e) {
var val = e.target.value;
e.target.value = '';
e.target.value = val;
}}
/>
https://jsfiddle.net/o3s05zz4/1/
Adaptation of this answer: https://stackoverflow.com/a/2345915/1589521
This actually works:
componentDidMount() {
const input = this.input;
const length = input.value.length;
input.focus();
input.setSelectionRange(length, length);
}
render() {
return (
<input ref={ref => this.input = ref} ... >
)
}
PS. If you need to support IE8 and below you'll need to use IE-specific checks.
This way the text of the input will be selected, ready to edit
<input
type="text"
defaultValue="Untitled"
autoFocus
onFocus={e => e.currentTarget.select()}
/>
Setting the input value inside componentDidMount seems to do the trick, but it feels like a hack:
componentDidMount: function(){
this.inputElement.value = this.state.teamId;
},
render: function() {
var that = this;
return <input ref={function(ref){that.inputElement = ref;}} type="text" autoFocus value={this.state.teamId} onChange={this.setTeamId} />;
},
https://jsfiddle.net/yuk13tuu/
In TypeScript:
private inputDOM: HTMLInputElement | null;
public componentDidMount() {
if (this.inputDOM != null) {
this.inputDOM.value = '';
this.inputDOM.value = this.state.newRegionName;
}
}
public render() {
return <input ref={(ref: HTMLInputElement | null) => this.inputDOM = ref} type="text" autoFocus={true} value={this.state.inputValue} />;
}
Looks like the html attribute autofocus doesn't take any parameters to specify where the cursor should start. See mdn documentation.
Sitepoint has a great tutorial explaining your options for setting cursor position from within an input box.
As for the reacty side of things, you'll simply put your jQuery (or other cursor related code) in the componentDidMount lifecycle method.
Related
I'm trying to make a input box component that has instant feedback using Formik. I want the input box to turn green when the user input matches a predefined string (the "answer"), gray if the input matches the prefix of the answer (including the empty string) and red otherwise. This string is stored as a property of the initial values, values.answer. The Formik validate function checks if the input equals values.answer and sets values.correct = true. I then created a css class corresponding to a green input box and set the className of the input conditional on the value of values.correct. The problem is it only seems to update (i.e turn green with a correct input) when I click out of focus of the input box (i.e onBlur). I would like it to work onChange. How would I do this?
Here is the relevant code sandbox: https://codesandbox.io/s/instant-feedback-box-lub0g?file=/src/Frame.js
Cool problem, but you've overcomplicated your code a little bit 😉 Some feedback:
touched is set to true during onBlur by default. You can override this by using setTouched(), but I found it simpler to just use values instead of touched in your form
try to keep values as minimal as possible, it's only meant to access input values so there's no need for hint and answer to be assigned to it
the purpose of the validation function is to return an errors object and not to set values, so remove assignments like values.correct = true
You don't need to store isDisabled in state, you can derive it from formik.submitCount and formik.isSubmitting
const Note = () => {
const [showFrame, setShowFrame] = useState({ 1: true });
const onCorrectSubmission = (frameId) => {
setShowFrame({ ...showFrame, [frameId]: true });
};
const text =
"What is the sum of the first three natural numbers? (give answer as a word, i.e one, two etc.)";
const hint = "The first three natural numbers are 1, 2, and 3";
const answer = "six";
return (
<div>
<h1>Induction</h1>
{showFrame[1] ? (
<Frame
id={1}
text={text}
hint={hint}
answer={answer}
onCorrectSubmission={onCorrectSubmission}
/>
) : null}
{showFrame[2] ? (
<Frame
id={2}
text={text}
hint={hint}
answer={answer}
onCorrectSubmission={onCorrectSubmission}
/>
) : null}
</div>
);
};
const Frame = ({
id,
text,
hint,
answer,
values,
onCorrectSubmission,
...props
}) => {
const validate = (values) => {
const errors = {};
if (!answer.startsWith(values.cloze)) {
errors.cloze = hint;
} else if (values.cloze !== answer) {
errors.cloze = true;
}
return errors;
};
const formik = useFormik({
initialValues: {
cloze: ""
},
validate,
onSubmit: (values) => {
onCorrectSubmission(id + 1);
}
});
const isFinished = formik.isSubmitting || formik.submitCount > 0;
return (
<form enablereinitialize={true} onSubmit={formik.handleSubmit}>
<p>{text}</p>
<input
id="cloze"
name="cloze"
type="text"
autoComplete="off"
{...formik.getFieldProps("cloze")}
disabled={isFinished}
className={`input
${!answer.startsWith(formik.values.cloze) ? "invalid-input" : ""}
${formik.values.cloze && !formik.errors.cloze ? "valid-input" : ""}
`}
/>
{formik.values.cloze && formik.errors.cloze ? (
<div>{formik.errors.cloze}</div>
) : null}
<button disabled={!!formik.errors.cloze || isFinished} type="submit">
Submit
</button>
</form>
);
};
export default Frame;
Live Demo
I have a webpage written in React (but it should not be strictly relevant to that question) that is composed by several inputs, let's call them Name, Surname and Code.
To work quickly, the insertion of the code is done with a Barcode Scanner that works as external keyboard. My idea is that if some field is focused, the keypress is inserted in the focused input but, in case no input is focused, I want to automatically focus and fill the Code input.
Is there a way to that it easily?
let inputName = document.querySelector('input[name="name"]');
let inputSurname = document.querySelector('input[name="surname"]');
let inputCode = document.querySelector('input[name="code"]');
let focusedInput = null;
[inputName, inputSurname, inputCode].forEach((input) => {
input.addEventListener('blur', () => {
focusedInput = null;
});
input.addEventListener('focus', () => {
focusedInput = input;
});
});
document.body.addEventListener('keypress', () => {
if (focusedInput == null) {
inputCode.focus();
}
});
<div>
<label>Name</label>
<input type="text" name="name" />
</div>
<div>
<label>Surname</label>
<input type="text" name="surname" />
</div>
<div>
<label>Code</label>
<input type="text" name="code" />
</div>
const surnameInput = document.getElementById('surname-input');
... (= do for all inputs)
let activeInput;
surnameInput.onFocus = () => { activeInput = surnameInput };
...
surnameInput.OnBlur = () => { activeInput = undefined };
...
document.addEventListener('keypress', (ev) => {
const input = activeInput ?? codeInput;
input.value += valueOftheKey;
}
You'd obviously have to evaluate if the key that was pressed has a value which you can add to the input, but I think this should give you an Idea of what to do. I haven't tried it out though, so it might not completely work.
Also: I'm not sure if it's the most efficient way, but it's an option.
EDIT: Answer by Kostas is better ;) except for the null...you should use undefined
I have a bug when I set the value from the state and change the value in a input.
when you write in the input and you try to correct the text the cursor move to the end.
This is my element
<div className="campo">
<p>Nombre</p>
<div className="campo-input">
<input
type="text"
name="name"
value={this.state.name}
onChange={this.handleInputChangeUpperCase}
/>
</div>
</div>
This is my function
handleInputChangeUpperCase = (e) => {
let { name, value } = e.target;
this.setState({ [name]: value.toUpperCase() });
};
Working Snippet:
class App extends React.Component {
state = {
name: "Ronaldo",
};
// This is the function
handleInputChangeUpperCase = (e) => {
let { name, value } = e.target;
this.setState({
[name]: value.toUpperCase(),
});
};
render() {
// This is the Element
return (
<div className="campo">
<p> Nombre </p>
<div className="campo-input">
<input
type="text"
name="name"
value={this.state.name}
onChange={this.handleInputChangeUpperCase}
/>
{this.state.name}
</div>
</div>
);
}
}
ReactDOM.render(<App />, document.getElementById("root"));
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.6.3/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.6.3/umd/react-dom.production.min.js"></script>
<div id="root"></div>
Based on the original answer: https://stackoverflow.com/a/49648061/7427111
class App extends React.Component {
state = {
name: "Ronaldo",
};
handleInputChangeUpperCase = (e) => {
// Here is the addition
const pointer = e.target.selectionStart;
const element = e.target;
window.requestAnimationFrame(() => {
element.selectionStart = pointer;
element.selectionEnd = pointer;
});
let { name, value } = e.target;
this.setState({
[name]: value.toUpperCase(),
});
};
render() {
return (
<div className="campo">
<p> Nombre </p>
<div className="campo-input">
<input
type="text"
name="name"
value={this.state.name}
onChange={this.handleInputChangeUpperCase}
/>
{this.state.name}
</div>
</div>
);
}
}
ReactDOM.render(<App />, document.getElementById("root"));
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.6.3/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.6.3/umd/react-dom.production.min.js"></script>
<div id="root"></div>
To control where the cursor is in an input field, use selectionStart, which indicates where your selected text begins. As long as you don't set selectionEnd, you'll just move the cursor to the desired position.
For instance, let's move to the position "1", just after the first character. For input "123", that should be "1|23"...
document.getElementById('text-area').selectionStart = 1;
document.getElementById('text-area').focus()
<input id="text-area" type="text" value="123"/>
For your situation, if you want the cursor to move to the start of the string, set .selectionStart = 0. If you want it to go back to where it was originally, save the value with var tempstart = ...selectionStart, and then restore it after you do the data change, .selectionStart = tempstart.
Source: Developer.Mozilla.org: HTMLInputElement.setSelectionRange()
If you need just toUpperCase as formatting then you can use css instead. It seems the simplest solution is described here https://stackoverflow.com/a/3724990/12868043
That behaviour seems to be happening with .toUpperCase();
handleInputChangeUpperCase = e => {
this.setState({ [e.target.name]: e.target.value });
};
If you don't require the text to be upper case immediately on input, I would apply the method outside of the setState
I want to fill the inputs value of a form with default values once the modal is opened
I did it with pure javascript using document.getElementById(textId).value='some value as follow:
for(var i=0; i<details_data.length;i++){
let textId='text'+i;
let amountId='amount'+i;
document.getElementById(textId).value=details_data[i].text
}
This worked fine. but I want to know how to do it with React since I don't believe this is a best practice.
what i tried is to set the input value like this:
<input name='text' id={textId} value={el.text} onChange={details_handler.bind(index)}/>
But this woudn't let me change the value of the input. it just set the default value, and when i type in the input the value woudn't change as I want.
This is my code
const [details_data,set_details_data]=useState([
{'text':'','amount':''}
])
// called this function on `onChange` and store the data in `details_data`
function details_handler(e){
let name=e.target.name;
let value=e.target.value;
details_data[this][name]=value;
set_details_data(details_data);
}
JSX:
(In my case user can add inputs as many as he wants,That's why I put the inputs in a the mapping loop)
{
details_data.map((el,index) => {
let textId='text'+index;
let amountId='amount'+index;
return (
<div key={index}>
<input name='text' id={textId} value={el.text} onChange={details_handler.bind(index)}/>
<input name='amount' id={amountId} onChange={details_handler.bind(index)}/>
</div>
);
})
}
useEffect(() => {
if(detailsProps) {
set_details_data(detailsProps);
}
}, [detailsProps])
where your detailsProps (data from the api) will look something like this
detailsProps = [
{'text':'text1','amount':'100'},
{'text':'text2','amount':'200'}
]
onChange Function
const details_handler = (event, index) => {
const items = [...details_data];
items[index][event.target.name] = event.target.value;
set_details_data(items);
}
your view
{
details_data.map((el,index) => {
return (
<div key={index}>
<input name='text' value={el.text} onChange={(e) => details_handler(e, index)}/>
<input name='amount' value={el.amount} onChange={(e) => details_handler(e, index)}/>
</div>
);
})
}
my check boxes are not getting checked, when created dynamically. I am not able to find the problem. Though, when I hard-code the values for check box id and label for, it just works.
var category_list = this.props.categories_list.map(function(name, i) {
// debugger
return (
<div className="group-chkbx list-group-item">
<input key={i+11} type="checkbox" id={name.category_id} name="category" />
<label htmlFor={name.category_id}>{name.name}</label>
</div>
)
});
After a lot of research one of my colleague helped me out with a solution. The htmlFor and id must be same, but cannot be only numeric. The Ids that I'm using are purely numeric. When I added alphabet as a prefix, it just started working like charm. Thanks all for showing interest and helping out here.
There's nothing that would set the checked prop on them, anyway. When should they be checked?
(Also, remember that components in arrays (such as what .map returns) should have unique key props.)
If your checkboxes are not getting checked, most probably is that some other functionality is preventing it.
Here and example of how to get the checkbox values:
class WithChecks extends Component {
constructor(props){
super(props);
this.getValue = this.getValue.bind(this);
}
getValue(e){
const chk = e.target;
console.log(chk.checked);
console.log(chk.value);
}
render() {
const arr = ['a', 'b', 'c', 'd'];
return (
<div>
{
arr.map((value, index) => {
return (
<div key={index}>
<input type="checkbox"
id={'chk' + index}
onChange={this.getValue}
name="category"
value={value} />
<label htmlFor={'chk' + index}>{value}</label>
</div>
);
})
}
</div>
);
}
}
Maybe this can help to clarify.
The checked property of the input will control whether it is checked. Usually I use local state (or something from global redux state to control what is checked). Little Example:
class Something extends React.Component {
constructor(props) {
super(props);
this.state = {
checked: 0
}
this.handleChange = this.handleChange.bind(this);
}
handleChange(e) {
// Do Stuff
}
render() {
return (
<div>
{
this.props.categories_list.map(function(name, i) {
return (
<div className="group-chkbx list-group-item" key={i}>
<input checked={i === this.state.checked} onChange={this.handleChange} type="checkbox" id={name.category_id} name="category" />
<label htmlFor={name.category_id}>{name.name}</label>
</div>
)
});
}
</div>
);
}
}