10

I want to use <div onInput={onChange} contentEditable> to save user input like textarea does. I've heard that I need to use onInput listener to trigger fn when input change. The problem is when I update text with React state, It moves the caret to beginning of text constantly

this is my code

import React from "react";

const App = () => {
  const [value, setValue] = React.useState("I am edittable");
  const onChange = (e) => {
    const html = e.target.innerHTML;
    setValue(html);
  };

  return (
    <div onInput={onChange} contentEditable>
      {value}
    </div>
  );
};
export default App;

codesandbox https://codesandbox.io/s/react-editable-cell-pwil6?file=/src/App.js:0-310

how could I fix it ?

Shawn
  • 367
  • 4
  • 13

4 Answers4

3

this has a solution, but onChange doesn't register the event. It won't change the state. Do not use it!

My suggestion is to use onInput, but DO NOT use the state variable as innerHTML for the editable element.

Put some default value, the contentEditable will handle the change of the innerHTML, while the onInput will handle the change of the state from the value of the innerHTML.

If you want to be sure that innerHTML will not be empty you can add onBlur event, that checks if it is ==="" and will update it with the default value or something else, etc.

  • 4
    I dont understand your answer @Dimitar – Sarah Apr 06 '22 at 22:12
  • I meant, that contentEditable shows the default value and if you type something it will show the change even without onInput event. but if you need to handle the change of the value in the state then you need to use onInput, the problem here is that on every typed character it will move the cursor to the beginning of the string value (may be using some timeout delay will be reasonable). I do not know why you need it, but I would prefer to use instead with onChange event – Dimitar Daskalov Apr 15 '22 at 06:20
1

This is old, but I just solved this for myself, and I thought I'd share it.

onChange will never fire, and onInput isn't the best way. You need to simulate onChange with onFocus and onBlur.

Copy the value of the innerHTML to a variable when onFocus occurs.

When onBlur occurs, test innerHTML against the variable to see if it changed. If so, do your onChange stuff. You need something like this:

  const onFocus = (e) => {
    val_start = e.target.innerHTML;
  };
  const onBlur = (e) => {
    if(val_start !== e.target.innerHTML) {
      //Whatever you put here will act just like an onChange event
      const html = e.target.innerHTML;
      setValue(html);
    }
  };
0

This is what you need to have:

<div onChange={onChange}  contentEditable>

instead of onInput it should be onChange

demo:https://codesandbox.io/s/react-editable-cell-forked-ey7o1?file=/src/App.js

Sakshi
  • 1,464
  • 2
  • 8
  • 15
0

contentEditable will make the content inside div editable.

Instead of using onInput you can use onBlur so that the blinking cursor will not move to beginning of text constantly.

Whatever change you make to the text will be visible on the screen and the state will be updated once you move the focus out of the element.

<div onBlur={onChange} contentEditable>
  {value}
</div>
thewebtud
  • 455
  • 2
  • 4
  • 21