When typing text into a contenteditable span in Safari or Firefox the caret moves to the start on each keypress. This only seems to happen when React re/renders the component.
Chrome works just fine. Haven't tested IE.
An example where | is the caret and typing Hello:
| |H |eH |leH |lleH |olleH
Here's a simplified version of my component:
import React, { Component } from 'react';
class ContentEditable extends Component {
    constructor(props) {
        super(props);
        this.state = {
            value: props.value
        };
    }
    createMarkup() {
        return { __html: this.state.value };
    }
    handleInput(event) {
        this.setState({
            value: event.target.innerText
        });
    }
    isValid() {
        let bestLength = 3;
        return this.state.value.length > bestLength;
    }
    render() {
        let className = '';
        if (this.isValid()) {
            className = 'is-valid';
        }
        return (
            <span
                contentEditable="true"
                onInput={ this.handleInput.bind(this) }
                className={ className }
                dangerouslySetInnerHTML={ this.createMarkup() }
            >
            </span>
        );
    }
}
ContentEditable.propTypes = {
    value: React.PropTypes.string.isRequired
};
export default ContentEditable;
Anyone come across this before?
You can achieve a contenteditable by adding a handler to an HTML tag.
The problem is every time content is re-rendered the position of the cursor is reset. You are triggering a setState when the content of the span is edited, which is resetting the cursor position.
You could rather store a reference to the span tag and update its value rather than triggering a state change.
A ref could be stored which will inject the value in the span.
class ContentEditable extends Component {
  constructor(props) {
    super(props);
    this.spanRef = React.createRef();
    this.val = props.value;
  }
  handleInput(event) {
    const { innerText } = event.target;
    if (this.val !== innerText) {
      this.spanRef.innerText = innerText;
      this.val = innerText;
    }
  }
  render() {
    return (
      <span
        contentEditable="true"
        className={"is-valid"}
        onInput={this.handleInput.bind(this)}
        ref={this.spanRef}
        suppressContentEditableWarning={true}
      >
        {this.val}
      </span>
    );
  }
}
Running code
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