Reputation: 746
Is there a way to programmatically set the cursor position inside a textarea?
My use-case is tagging users.
When a user enters @tar, I'm displaying suggestions, a user will click on one of them (for example @tarik) but the focus will be lost, which is expected. I need to return focus on specific position which I know how to calculate.
I'm using Textarea from react-textarea-autosize
I'm defining my ref in the constructor:
this.textareaInput = React.createRef();
I'm assigning a component to ref:
<Textarea
ref={this.textareaInput}
rows={1}
value={comment}
onChange={this.handleInputChanged}
tabIndex={tabIndex || 0}
/>
And with this line, I should be able to manipulate with cursor position, but I'm not.
this.textareaInput.current._ref.focus();
this.textareaInput.current._ref.selectionStart = 1;
or
this.textareaInput.current._ref.focus();
this.textareaInput.current._ref.selectionEnd = 3;
None of this examples works.
NOTE: Hardcoded values are used just to test the code.
I've tried to use setSelectionRange
method too, no success.
Upvotes: 2
Views: 12801
Reputation: 108
It worked for me like this: (Using Hooks)
function ChatInput(props) {
const { TextArea } = Input;
const [value, setValue] = useState('');
const [caret, setCaret] = useState({
start: 0,
end: 0
});
const changeHandler = (e) => { setValue(e.target.value); }
// updates the cursor position onselect
const handleSelect = (e) => {
setCaret({ start: e.target.selectionStart, end: e.target.selectionEnd });
}
//calling to this function you can insert text in the cursor position
const insertText = (text) => {
setValue(
value.substring(0, caret.start)
+ text +
value.substring(caret.end, value.length)
)
}
return (
<div className="input">
{/* this is the target textarea which is a controlled component */}
<TextArea
placeholder='Enter message'
value={value}
onChange={changeHandler}
onSelect={(e) => handleSelect(e)}
/>
</div>
);
}
Upvotes: 0
Reputation: 3899
onChangeHandler = event =>{
if (typeof(this.input)==='object'&&this.input!==null) {
const selectionStart = this.input.selectionStart
if (typeof(selectionStart)==='number') {
this.setState({
value:event.target.value,
selectionStart:selectionStart,
},()=>console.log("cursor Point is in"+this.state.selectionStart))
return
}
}
}
onClickHandler = () =>{
if (typeof(this.input)==='object'&&this.input!==null) {
const selectionStart = this.input.selectionStart
this.setState({
selectionStart: selectionStart,
})
}
}
<textarea
ref={el=>this.input=el}
value={this.state.value}
onChange={(event)=>this.onChangeHandler(event)}
onClick={()=>this.onClickHandler() }
/>
Upvotes: 1
Reputation: 1619
Same problem, solved in this way:
// in the constructor
this.focusCommentsInput = this.focusCommentsInput.bind(this);
this.textInput = React.createRef();
// the function to call
focusCommentsInput(){
this.textInput.current.focus();
}
// the Textarea inputRef instead of ref
<Textarea minRows={1} maxRows={10} className="input-textarea" placeholder={"Comments"} inputRef={this.textInput} />
// the action onClick
<div className="comments" onClick={ this.focusCommentsInput }>
</div>
Upvotes: 0
Reputation: 746
I found solution setting both selectionStart
and selectionEnd
on the same value.
Upvotes: 0
Reputation: 3993
Try this:
HTML:
<div id="container">
<!-- This element's contents will be replaced with your component. -->
</div>
React-jsx:
class TodoApp extends React.Component {
constructor(props){
super(props);
this.state = {
scriptString: ''
};
}
handleKeyDown(event) {
if (event.keyCode === 9) { // tab was pressed
event.preventDefault();
var val = this.state.scriptString,
start = event.target.selectionStart,
end = event.target.selectionEnd;
this.setState({"scriptString": val.substring(0, start) + '\t' + val.substring(end)}, () => this.refs.input.selectionStart = this.refs.input.selectionEnd = start + 1);
}
}
onScriptChange(event) {
this.setState({scriptString: event.target.value});
}
render() {
return (
<textarea rows="30" cols="100" autofocus="true"
ref={this.textareaInput}
rows={1}
onFocus={ this.onFocus }
onKeyDown={this.handleKeyDown.bind(this)}
onChange={this.onScriptChange.bind(this)}
value={this.state.scriptString}/>
);
}
}
ReactDOM.render(
<TodoApp />,
document.getElementById('container')
);
Upvotes: 0
Reputation:
I think the problem is that custom <Textarea>
block doesn't have a .focus()
method.
You can try this code:
import TextareaAutosize from 'react-textarea-autosize'
class MyComponent extends React.Component {
componentDidMount() {
this.inputRef.focus()
}
inputRef = ref => (this.inputRef = ref)
render() {
return <TextareaAutosize inputRef={this.inputRef} />
}
}
Found on GitHub
Upvotes: 0