createSelection() primitive odd behaviour in contentEditable div's
Now I am not entirely looking for a solution with this post, though one would be appreciated, I believe I am just going to work around this behaviour for my use case. However, when using the createSelection() primitive to get the user selection in a textarea versus a contentEditable div, the behaviour is fairly different and bordering on broken in the case of the contentEditable div ( and I am aware of why this is and will get into that soon ).
If you take a look at the attached video, and pay close attention to whenever I input a newline character, moving the cursor to the beginning of that newly created line, in the contentEditable div ( above ) the primitve returns the [start,end] tuple of [0,0] regardless of whether or not there is content on previous lines - contrast this to the textarea where it correctly gets the length of the text previous to the cursor and also accounts for this when the selection is not collapsed, i.e start !== end.
I am aware this is because of differences in the window.getSelection() API and the properties available on each DOM element's object representation in TS, we can see this in the source code of the primitive where you'll notice special handling of the textarea and input element cases since those objects have the available selectionStart and selectionEnd properties which simplify the process of fetching these values, and for other cases the function goes on to fallback to the window.getSelection() API:
Now I suppose my question, or more accurately discussion topic, is what could be done about this in terms of a PR to improve this behavior, or if that should be done at all - for all I know this is the preferred or best possible solution, so I am opening it up to discussion, I suppose.
I spent a good couple hours fiddling around with a fork of the primitive and tried to handle it by introducing a special case for contentEditable divs where I would walk the DOM and check for linebreak
<br/>
elements, which seemed like it would vary between browsers and not at all be performant, but it sort of worked. I tried a few other things, and ultimately decided it wasn't worth the hassle of bending the DOM api's to my will and just decided to use a textarea which would get masked by a div with the rendered content.
Thoughts?GitHub
solid-primitives/packages/selection/src/index.ts at main · solidjs-...
A library of high-quality primitives that extend SolidJS reactivity. - solidjs-community/solid-primitives
7 Replies
Thank you for the feedback! It is some time since I wrote this primitive. I have put this issue on my to-do list, right after the setup for cookieStorage on the server in the storage package.
oh wow I wasn't expecting the author of this primitive to stumble upon this - I am willing to help out with a fix, I assume it wasn't intended behaviour but rather just an edge case. Do you have any ideas better than walking the DOM to determine whether the selection spans multiple Nodes? I was getting frustrated with my attempts since it seems like there should just be a browser API for this......
The existing API is supposed to handle even more complex use cases, which explains the complexity.
you mean the browser API? the complexity makes sense but why it differs needlessly between elements and browsers is what is frustrating to me. Though I suppose to some degree all web APIs do
because the comlpexity can be skipped if all you need is selectionStart and selectionEnd to achieve the use case I am working with but that somehow doesn't exist on a div object with contentEditable={true}
Agreed.
Do you expect it to become as comprehensive as the lexicaljs abstraction on input events? will we be able to build rich text experiences on top of it that work seamlessly on all devices? 😀
At some point, hopefully. At the moment, my time constraints are a bit too heavy.