That particular bug with slack would be possible to fix with JS. The particular difference about that style is there is a little bit of padding at the start of the style. Bowsers have no understanding of clicking on that padding to select inside the the beginning of the inline style.
How? No matter what I try, the insertion point is always before the bold element, and not inside it.
[sandbox]: https://codesandbox.io/s/gracious-dew-rx3xhz?file=/index.htm...
On my Mac, Firefox 104.0.2 inserts new characters inside the <strong>, so they're bold.
In Chrome, the browser API allows positioning the selection there, but during the input event, the selection ends up normalized to just before the strong tag, so the characters aren't inserted into the <strong> tag.
In Safari, the browser API accepts the arguments to set the selection to the first position inside the strong tag, but normalizes during the API call, and the selection always ends up right before the strong tag.
The variance of contentEditable is a huge impediment to using the API raw. If you want reliable, relatively low-level control of rich text input on the web, use ProseMirror.
(I learned a lot about this stuff while working on Notion's rich-text editor, which is all custom.)
- The default behavior that triple-click to selects a whole paragraph/line will stop at the first contenteditable=false element.
- Android may dismiss the keyboard & selection if the selection ever touches one of your contenteditable=false marker elements.
- Firefox won't let you place the caret in between two contenteditable=false elements, or at the start or end of a contenteditable=true element if the first/last child is contenteditable=false.
Why not use ProseMirror?
Why not use ProseMirror? It doesn't fix this issue, does it? At least in the demos on their website you cannot insert text at the beginning of a styled element. You cannot do it even in a native application (at least on macOS) without a custom handling of the selection.
Do you happen to have any more samples to demonstrate how difficult it is to work with contenteditable APIs?
I think natively if you don’t jump into a tag and you don’t jump out of a tag. So if your cursor is in the tag and you move to the boundary, you stay in it. Similarly if you are outside of it, and move to the boundary you stay outside of it.
So I basically would listen to the the relevant input events, see if I’m at the boundary and the orientation of the cursor (which I save in memory) and if I’m only moving across a boundary (i.e. not to the next character) I call event.preventDefault(), manipulate the range accordingly, and update the orientation state.
I’m not sure how to style the caret it self (other then the color) but I’m sure it can be achieved with some absolute position hacking.