1

I'm trying to create a text editor and I'm using a contenteditable div and everytime someone changes the text inside it I want to wrrap all that new text with a strong element and change the div's innerHTML

This is what I tried (I'm using react/nextjs)

useEffect(() => {
    if (!divRef.current) return;

    let text = divRef.current.innerText;
    const htmlArray = text.split(" ").map((word) => {
      return `<strong style="color: red">${word} </strong>`;
    });
    text = htmlArray.join("");
    divRef.current.innerHTML = text;
  }, [text]);

everything here works as expected but everytime I type a character the cursor goes to the start and the text is rendered backwards. How can I fix the issue I want the cursor to stay an the end of the div when user type

Tarun
  • 37
  • 7
  • Does this answer your question? [How to set the caret (cursor) position in a contenteditable element (div)?](https://stackoverflow.com/questions/6249095/how-to-set-the-caret-cursor-position-in-a-contenteditable-element-div) – Jan Pfeifer Jan 09 '23 at 10:47
  • @JanPfeifer I have tried that method but it is not working for me (throwing error) `execute 'setStart' on 'Range': parameter 1 is not of type 'Node'.` – Tarun Jan 09 '23 at 14:22

1 Answers1

0

This is due to the fact that you're updating the innerHTML of the contenteditable div every time text changes.

You need to capture the actual position, and set it back after state change with Selection

useEffect(() => {
  if (!divRef.current) return;

  // Get the current cursor position
  let selection = window.getSelection();
  if (!selection.rangeCount) return;
  let range = selection.getRangeAt(0);
  let cursorPosition = range.startOffset;

  // Update the innerHTML
  let text = divRef.current.innerText;
  const htmlArray = text.split(" ").map((word) => {
    return `<strong data-id="hey" style="color: red">${word} </strong>`;
  });
  text = htmlArray.join("");
  divRef.current.innerHTML = text;

  // Get the new cursor position
  selection = window.getSelection();
  range = selection.getRangeAt(0);

  // Set the cursor position to the new position
  range.setStart(range.startContainer, cursorPosition);
  range.collapse(true);
  selection.removeAllRanges();
  selection.addRange(range);
}, [text]);
Johan
  • 2,088
  • 2
  • 9
  • 37
  • it's throwing this error `Failed to execute 'getRangeAt' on 'Selection': 0 is not a valid index.` – Tarun Jan 10 '23 at 09:29
  • Ha, it means you have to add a condition to check if there is range. I updated the code – Johan Jan 10 '23 at 09:32
  • the error is gone but the issue is still there, the cursor goes back to the start – Tarun Jan 10 '23 at 09:48