3

I am basically trying to replicate the functionality of Roam Research. The setup is:

  • Some text in markdown format. For example:
const markdownText = "Use **asterisks** for bold text. Use _underscores_ for italics text.";
  • A container div (C).
  • A "view" div (V) that shows the text rendered in markdown. This is a child of C (100% width/height).
  • An "edit" textarea (E) that shows the text in its original format. This is also a child of C (100% width/height).

When the page is loaded, V is visible and E is hidden. The user sees the rendered markdown text. When the user clicks anywhere on the text displayed in V, V is hidden and E becomes visible. The user now sees the original text and can edit it. When they're done, E is hidden and V becomes visible. The user sees edited text rendered in markdown.

The problem I'm facing is preserving the caret location, so that if the user clicks on a word (say between the "o" and the "l" of the word "bold") in the rendered markdown text in V, the caret is inserted at that location when the text switches to the original version in E.

I can get the cursor position in the rendered markdown version V with window.getSelection() (and potentially even use algorithms to count the number of characters of all nested elements in V as discussed in HTML contenteditable: Keep Caret Position When Inner HTML Changes), but my problem is figuring out how to correlate that with the original text in E, that has additional characters (e.g. the asterisks to bold text).

Using window.getSelection() in V would give me an offset of 20 for the position between the "o" and the "l" in "bold", but the cursor in reality should be placed at location 24 in E due to the four asterisks present in the original text.

Is there a more elegant solution than just parsing the original text character-by-character and counting how many markdown symbols I need to offset the result of window.getSelection() with?

0 Answers0