How can I select a node from the current doc?

I want to create a content table, which updates itself if the headings have been changed in the document:

If an item in the content table is clicked, I want to scroll to the relevant heading.

My current code:

function scrollHeadingIntoViewById(id) {
  for (let i = 0; i < window.editorView.state.doc.content.content.length; ++i) {
    let node = window.editorView.state.doc.content.content[i];
    if (node.type.name === 'heading' && node.attrs.id === id) {
      let ns =  NodeSelection.create(node,0);
      let tr = window.editorView.state.tr.setSelection(ns).scrollIntoView();
      window.editorView.dispatch(tr);
      break;
    }
  }
}

basically, I enumerate all top-level blocks, and if I find a heading block, plus its id equals to the TOC item’s id, I will create a transaction such that the heading block will be scrolled into view.

But this doesn’t work, I’m getting:

Uncaught RangeError: Selection passed to setSelection must point at the current document

Well I understand the concept that prosemirror doc is immutable. A change will result in a new doc, which might invalidate a selection made on an older doc.

But in my code, I’m using the current doc to create a selection and I don’t mutate the doc. How come my selection is invalid?

In my code, window.editorView is a global variable that holds the prosemirror editor.

Looks like that is the problem — you’re passing 0, but that’s not the actual position of the node. Using state.doc.forEach instead of the for loop should make it easy to have the position of the node available at this point.

1 Like

Thank you very much!

As a user, I feel that the positioning system is the most frustrating part of using prose-mirror. The concept is easy to understand, but the API and examples seem to be lacking. For example, I would expect the NodeSelection class to have an interface to create a selection by a node. But almost all functions related to selection or positioning ask for a “pos”. but I feel it is difficult to calculate the pos most of the time.

Maybe I’m missing something?

Can I bail out early from this forEach function? for example by returning “false”?

A node object does not identify a node in the document. These don’t have a meaningful identity, they are just values—you can create a document containing the same node object multiple times. Thus, as you found, the library uses positions to identify nodes.

1 Like