Question

I'm using ReactJS to render text values into contenteditable DOM nodes. Eg:

var data = [{
  value: 'Hello '
},{
  value: 'World!'
}];

var component = React.createClass({
  render: function () {
    var pieces = this.props.data.map(function (piece) {
      return (
        <span contentEditable="false">
          <span contentEditable="true">{piece.value}</span>;
        </span>
      );
    });

    return <div contentEditable="true">{pieces}</div>;
  }
});

React.renderComponent(<component data={data} />, someContainer);

Resulting in DOM that looks like this (for many reasons):

<div contenteditable="true">
  <span contentEditable="false">
    <span contentEditable="true">Hello </span>;
  </span>
  <span contentEditable="false">
    <span contentEditable="true">World!</span>;
  </span>
</div>

When a user interacts with these editable spans by, for example, backspacing from the second one into the first one, I can update the data by removing the last character from the first model, but I need to somehow tell React to position the cursor at the end of the first span[contenteditable="true"]

My Question:

In my .render() function I have a this.state.caretPosition property telling me the position the cursor should be (this only exists on ONE component). render() is supposed to just return a (virtual) DOM Node. How do I tell ReactJS to position the caret inside the returned DOM Node at that position?

Example: jsbin

Était-ce utile?

La solution

I was able to get your example working with the methods described in these how-to-set-caretcursor-position-in-contenteditable-element-div, select-range-in-contenteditable-div. The same methods used there should probably work for you. You will most likely need to run this in the componentDidMount() method. Hope that helps. Here is a rough idea of what it would look like:

componentDidMount: function () {
    var el = document.getElementById("a3");
    var range = document.createRange();
    var sel = window.getSelection();

    range.setStart(el.childNodes[0], 1);
    range.collapse(true);
    sel.removeAllRanges();
    sel.addRange(range);
    el.childNodes[0].focus();
  },

Edit: Seeing as your structure is quite nested you'll have to play around a bit with which nodes you are actually selecting to get the desired effect. The focus() call is needed to actually show the applied cursor position if the cursor is not already in the editable element.

Autres conseils

In React 15.6.1 you can manually set caret position like that:

class CursorForm extends Component {

  constructor(props) {
    super(props);
    this.state = {value: ''};
  }

  handleChange = event => {
    // Custom set cursor on zero text position in input text field
    event.target.selectionStart = 0 
    event.target.selectionEnd = 0

    this.setState({value: event.target.value})
  }

  render () {
    return (
      <form>
        <input type="text" value={this.state.value} onChange={this.handleChange} />
      </form>
    )  
  }

}

You can get full control of cursor position by event.target.selectionStart and event.target.selectionEnd values without any access to real DOM tree.

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top