Prosemirror: Finding absolute start/end position of the node at a given position.

Created on 8 Dec 2016  路  6Comments  路  Source: ProseMirror/prosemirror

Given the simple document p("hello ", link("world")), is there a way using the API to find the start/end absolute positions (position 7/12) in the document of the link, given a position within it (eg wo|rld i.e. position 9)?

The use case is to implement a link editor, so that when the user clicks on a link, I can determine the start and end position of the link so that it can be edited or removed. I've spent the last two hours playing with all the prosemirror-model APIs I could find, but no luck extracting those positions :cry:

I feel like I must be missing something obvious in the docs, or else there may be a totally different way of going about solving this...

Most helpful comment

Seeing that this is really a little awkward, I'm adding a textOffset getter (pretty much equivalent to your offsetInLink variable) to resolved positions (since they already store that information), which would reduce this to...

const $pos = doc.resolve(pos)
const linkStart = pos - $pos.textOffset
const linkEnd = linkStart + $pos.parent.child($pos.index()).nodeSize

See https://github.com/ProseMirror/prosemirror-model/commit/e7c4d80966fcf305d71068483e93da0431a042d7

All 6 comments

childAfter and childBefore are probably relevant. I.e. if you have a resolved position inside the link (but not at the very start), doing $pos.parent.childBefore($pos.parentOffset) will give you the link node and its local offset.

Thanks.

This works for the simple example I provided, because the local position is the same as the absolute one, but I should have provided a more representative example.

Given something like: doc(bullet_list(list_item(p("foo")), list_item(p("hello", link("world")))))

From what I see, the solution you suggested would provide the local position within the p, but what I was after was the absolute position from the root doc. It would make sense to have to compute the absolute offset by recursively walking up the path of the resolved position, adding up local offsets until I reach the root node, but from what I can see only the immediate parentOffset is recorded, not at each level?

Is finding such absolute positions an anti-pattern? Am I meant to record positions using local offsets and generate a transform for that later on for my use case?

Thanks again for your help!

So add $pos.start(), or compute the difference between the start of the node and the position, $pos.parent.childBefore($pos.parentOffset).offset - $pos.parentOffset and subtract that from $pos.pos

Awesome, that did it, thanks a lot for your help!

For reference if anyone needs to do this, here is roughly what my code looks like:

const resolvedPos = doc.resolve(pos);
const parentInfo = resolvedPos.parent.childBefore(resolvedPos.parentOffset);
const linkNode = parentInfo.node;
const linkStartPos = parentInfo.offset;
const posInParent = resolvedPos.parentOffset;
const offsetInLink = posInParent - linkStartPos;
const linkFrom = pos - offsetInLink;
const linkTo = linkFrom + linkNode.nodeSize;

Note that if the same link mark applies to a segment of text that contains another sub-mark (eg italic), more work needs to be done to find the boundaries of the link.

Seeing that this is really a little awkward, I'm adding a textOffset getter (pretty much equivalent to your offsetInLink variable) to resolved positions (since they already store that information), which would reduce this to...

const $pos = doc.resolve(pos)
const linkStart = pos - $pos.textOffset
const linkEnd = linkStart + $pos.parent.child($pos.index()).nodeSize

See https://github.com/ProseMirror/prosemirror-model/commit/e7c4d80966fcf305d71068483e93da0431a042d7

Brilliant, thanks @marijnh!

Was this page helpful?
0 / 5 - 0 ratings