I have a textarea in React that I want to turn into a "notepad". Which means I want the "tab" key to indent instead of unfocus. I looked at this answer, but I can't get it to work with React. Here is my code:
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 line doesn't work. The caret position is always at the end of the line 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" ref="input" onKeyDown={this.handleKeyDown.bind(this)} onChange={this.onScriptChange.bind(this)} value={this.state.scriptString}/> ) }
When I run this code, even if I press the "tab" key in the middle of the string, my cursor always appears at the end of the string instead. Anyone knows how to correctly set the cursor position?
To set the cursor at the end of a textarea: Use the setSelectionRange() method to set the current text selection position to the end of the textarea. Call the focus() method on the textarea element. The focus method will move the cursor to the end of the element's value.
To get the mouse position in React: Set the onMouseMove prop on an element or add an event listener on the window object. Provide an event handler function. Access relevant properties on the event object.
When you click a button you move the focused element so there is no cursor in the text anymore. So you would have to track the last location of the cursor and then just set the value of the text area to be the current value + the extra text at the location of the last know cursor position.
In order to get the position of the cursor the only thing you need is onSelectionChange https://reactnative.dev/docs/textinput#onselectionchange. The function will return the start and the end values.
You have to change the cursor position after the state has been updated(setState()
does not immediately mutate this.state
)
In order to do that, you have to wrap this.refs.input.selectionStart = this.refs.input.selectionEnd = start + 1;
in a function and pass it as the second argument to setState
(callback).
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 }); } }
jsfiddle
For anyone looking for a quick React Hooks (16.8+) cursor position example:
import React, { useRef } from 'react'; export default () => { const textareaRef = useRef(); const cursorPosition = 0; return <textarea ref={textareaRef} onBlur={() => textareaRef.current.setSelectionRange(cursorPosition, cursorPosition)} /> }
In this example, setSelectionRange
is used to set the cursor position to the value of cursorPosition
when the input is no longer focused.
For more information about useRef
, you can refer to React's official doc's Hook Part.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With