Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

React contentEditable and cursor position

Tags:

I have simple component

class ContentEditable extends React.Component {
  constructor(props) {
    super(props);
    this.handleInput = this.handleInput.bind(this);
  }

  handleInput(event) {
    let html = event.target.innerHTML;
    if (this.props.onChange && html !== this.lastHtml) {
      this.props.onChange({ target: { value: html, name: this.props.name } });
      this.lastHtml = html;
    }
  }

  render() {
    return (
      <span
        contentEditable="true"
        onInput={this.handleInput}
        className={"auto " + this.props.className}
        dangerouslySetInnerHTML={{ __html: this.props.value }}
      />
    );
  }
}
export default ContentEditable;

<ContentEditable
  value={this.state.name}
  onChange={e => {
    this.setState({ name: e.target.value });
  }}
/>;

The component works but the cursor position never changes, it is always on first position instead after the rendered text.

I tested examples form this forum but it doesn't work for me.

I use React 15.6.1 and test it on chrome (Os X).

Any hint how I can solve this problem?

like image 932
jaroApp Avatar asked Jul 25 '17 14:07

jaroApp


2 Answers

The solution with useRef will be something look like below.

keep the data within custom input with useRef

Here the useRef will keep the default value / initial value apart from the component rendering cycles, so it will retain the original value without being affected by other kinds of operations we do in the react component.

This component does two things

  1. This will emit the user input to the parent component with an onChange method
  2. Takes a default value from parent component as prop named value and renders the value in the custom input box (that was created using contentEditable)

I have added a code sandbox, link here, use this to see how this works!

The code sandbox example contains two components

  • one is ContentEditableWithRef which solves the problem with useRef , which is an uncontrolled component and
  • the other component is ContentEditable which uses useState to solve the same problem.
like image 152
Lakshmaji Avatar answered Sep 20 '22 20:09

Lakshmaji


I also had same problem. Just fixed it with ref. Just assign textContent of event.target to ref.

const textareaEl = useRef<HTMLDivElement>(null);

const handleChange = (e: React.ChangeEvent<HTMLDivElement>) => {
    textareaEl.current.textContent = e.target.textContent;
    onChange(e); // If you have change event for form/state
};

/** If you're passing value from state, 
    you can mutate it each change for not losing cursor position. 
 */
useEffect(() => {
  if (value) {
     textareaEl.current.textContent = value;
  }
}, [value]);


return (
    <div
      id="textarea-element"
      ref={textareaEl}
      contentEditable={true}
      suppressContentEditableWarning={true}
      onChange={handleChange}
     />
)

like image 27
rufatZZ Avatar answered Sep 20 '22 20:09

rufatZZ