Best way to do native selections

My problem appears in Chrome and not Firefox (haven’t checked other browsers) when a single word is selected. In Chrome, if the anchor is at the front and you press Shift+ArrowUp, it will collapse the selection to the front of the word. Similarly, if the anchor is at the end and you press Shift+ArrowDown, it will collapse to the end. It is being clever in a way that Firefox doesn’t attempt; in Firefox those key combinations will select up to the aligned position on the previous/next line, like you’d expect.

Chrome’s cleverness causes UX issues when you double click a word to select it, so they workaround their cleverness for that case by creating a selection that doesn’t actually have an anchor/head yet (though if you document.getSelection() it will report the anchor and focus as the start and end of the word respectively). If you press ArrowLeft, the front of the word becomes the head. Similarly if you press ArrowRight, the back becomes the head. If you instead press Shift+ArrowUp/Down, it behaves like Firefox.

I have a case that relies heavily on having full words selected, and I currently use a Mark for each word.

If I use native DOM Selection methods to…

const range = document.createRange()
range.selectNode(wordMarkEl);
const sel = document.getSelection();
sel.removeAllRanges();
sel.addRange(range)

…then I can get Chrome into the better state without an anchor/head. But I can’t do this if I use ProseMirror’s tr.setSelection(TextSelection.create(...)). And I can’t use NodeSelection on a Mark because this.nodeDOM is a TextNode.

I was wondering if you had any advice on the best way to do a native selection of a mark node and keep ProseMirror in sync. Or if there is a supported way to get into that headless/anchorless state with ProseMirror transactions alone?

https://github.com/ProseMirror/prosemirror-view/pull/58 seems to indicate that my best bet is to do the native DOM selection and immediately fire a ProseMirror transaction that sets the selection to the exact same positions, and ProseMirror will ignore the transaction. I’ve tried that and it seems to work. Would you say that’s the best way to do it? I’m just worried if the “ignore” behavior of ProseMirror ever changes, then my hack regresses pretty quickly. I also don’t have access to the view from appendTransaction so I have to add a kludge to get it (and currently I have bugs because the view is out of date within appendTransaction).

Thanks for any help!

Yes, this is likely to continue to work. Since, as you noticed, the JavaScript selection API only exposes a small subset of the information that the browser’s actual selection state holds, the editor tries it best not to mess with the selection when it doesn’t have to. This is a sad kludge, but seems to be the best we can do with the tools we have.

In that case do you have a recommendation for manipulating the view within appendTransaction? I need the root transaction to have already run to be able to set the selection natively. But to do that I think I’d have to do it async and fire the setSelection as a completely separate transaction, which won’t play well with undo/redo. There’s no way to execute native DOM manipulation in a Step is there? Like a custom Step?

No… that’s not really something that’s reasonable to do. State-manipulation logic can’t even assume that there is a view for this state.

Bummer. Thanks.