Setting selection to newly inserted text node

Another one of those ‘I must be missing something here’ questions. I’ve got a few commands that if nothing is selected, insert a new block containing an ‘empty’ (one blank space for content) text node into the document at the cursor point. That bit works fine. However, I’d like the cursor to end up inside the new text node after creation, and it’s not - it’s not even staying where it was before, and will instead jump to the beginning of the doc, both if I do nothing and if I try to set the selection manually. Inspecting the state’s selection after the insert transaction seems fine, but the cursor isn’t going there.

Example command:

  function blockTest(state, dispatch) {
    let {selection} = state;
    if (selection.empty) {
        let text = mySchema.text(" ");
        let newNode = mySchema.nodes.heading.create({level : 1}, text);
        let tr = state.tr.replaceSelectionWith(newNode);
        return dispatch(tr);
    } 
}

Try using .setSelection(TextSelection.near(tr.doc.resolve(pos))) (in addition to replaceSelectionWith) where pos is the beginning of the text node.

1 Like

Tried it, same behavior :c There doesn’t seem to be a problem setting the selection, it’s just that the cursor ends up somewhere else - either some disconnect, or something else setting the selection again after…? But I tried disabling all my plugins but the menu button I’m using for the command, and it’s still doing the same thing, so I’m not sure what could be setting it again.

Maybe check if the pos is correct in the call to TextSelection.near? After you create the heading node, manually place your cursor at the beginning, and log that value.

Tried that, and the result was more or less right - the selection ends up right after the new text rather than inside it, but that’s easily solvable. HOWEVER. Doing the logging for that unearthed the actual problem, which is that something else is setting the selection again afterwards. And that, specifically, appears to be the code in my button menu that sets focus back to the editor after a button is clicked. So - button is clicked, the function I shared for inserting the node + setting selection runs, and then editorDom.focus() runs, and for reasons unclear to me, sets a new selection. It’s particularly unclear because the reason for the focus() in the first place was to skip the reset of selection that happened when a user had to manually click back into the editor area after hitting a button.

Going to keep poking at this now that I have a toehold in, but any ideas would be welcomed.

EDIT: And for clarity, I tried disabling the focus() bit, and moving the focus using Tab on the keyboard back to the editor, and that behaves as expected.

Are you using EditorView.focus or EditorView.dom.focus? The former should preserve the selection.

1 Like

Try marijn’s comment above first.

const button = document.createElement("button");
button.onclick = event => {
  view.dispatch(view.state.replaceSelectionWith(newNode));
  view.focus();
}

There’s also a hack-y workaround using onmousedown and onclick: javascript - onclick() and onblur() ordering issue - Stack Overflow. The default behavior of onmousedown, which runs before onclick, is to focus on the button (and blur the editor), so to avoid that you could use event.preventDefault() e.g.

const button = document.createElement("button");
button.onmousedown = event => event.preventDefault();
button.onclick = event => {
  view.dispatch(view.state.replaceSelectionWith(newNode));
}
1 Like

Aha! That seems to have done the trick. I knew it was going to end up being something small and stupid on my part. Thanks for the help!

The method with “mousedown” breaks a drag-n-drop functionality, unfortunately :frowning: