简体   繁体   中英

How to set cursor position inside Textarea in ReactJs?

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.

我发现解决方案将selectionStartselectionEnd设置为相同的值。

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() }
    />

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

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')
    );

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> 

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>
    );
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM