I'm trying to create a small text editor with React. For now I only need one feature: syntax highlighting.
The problem is absurd: onInput the cursor moves to the beginning of the div.
For obvious reasons I cannot use a textarea, so I need a div with contentEditable attribute.
I then tried onInput moving the cursor after the typed character, but without success.
So, if I understand correctly, either I give up contentEditable or I give up dangerouslySetInnerHTML. Or I give up the onInput handler.
But without these elements I don't see any alternative....
Below is the code produced so far
const EditableElement = () => {
const keywords = [
"def",
"if",
"else",
"elif",
"for",
"while",
"try",
"except",
];
const [text, setText] = React.useState("");
const elementRef = React.useRef(null);
const handleChange = () => {
const value = elementRef.current.innerText;
setText(value);
};
const highlightKeywords = () => {
let highlightedText = text;
keywords.forEach((keyword) => {
const regex = new RegExp(`\\b(${keyword})\\b`, "g");
highlightedText = highlightedText.replace(
regex,
`<span class="keyword">$1</span>`
);
});
return highlightedText;
};
React.useEffect(() => {
const html = highlightKeywords();
elementRef.current.innerHTML = html;
}, [text]);
return (
<div
className="code-editor"
placeholder="Write your Python code here..."
contentEditable
suppressContentEditableWarning
onInput={handleChange}
ref={elementRef}
dangerouslySetInnerHTML={{ __html: highlightKeywords() }}
/>
);
};```
By using
dangerouslySetInnerHTML
your content gets re-rendered on every state change. Because of that, your element loses the cursor position. In order to keep the cursor position you'll need to manipulate the DOM directly (update selection etc.).As others have already suggested, you probably don't want to go that path. There are good solutions out there.