Small issues with pos and nodesBetween

Hello,

I’m having some issues with positions.

I have the following:

<section>
<!-- .... -->
<article>
  <p>Flex child 12</p>
  <!-- I'm moving my mouse here -->
</article>
<!-- ... -->
</section>

which, in practice, correspond to the following:

and I have the following code:

const outlineNodePlugin = new Plugin({
    props: {
        handleDOMEvents: {
            mouseover(view, event) {
                const { nodeHover } = storeToRefs(storeEditorEvent)
                // We clicked in the editor, retrieve the PM document position
                let pos = view.posAtDOM(event.target)

                console.log('===>>> HOVER1: ', pos)

                // Compute the new lineage from that pos
                const lineage = []

                view.state.doc.nodesBetween(pos, pos, (node, _pos) => {
                    console.log('===>>> HOVER LOOP: ', _pos, node.type.name, node.isText)
                    if (!node.isText) {
                        lineage.push(_pos)
                    }
                })

                // Get closest pos in the lineage tree
                if (lineage.length > 0) {
                    pos = lineage.reduce(
                        (prev, curr) => Math.abs(curr - pos) < Math.abs(prev - pos) ? curr : prev
                    )
                }

                // Check which node do we have at this pos
                const node = view.state.doc.nodeAt(pos)

                console.log('===>>> HOVER2: ', pos, node?.type.name)

                nodeHover.value = {
                    node: node,
                    pos: pos
                }
            },

When I’m moving my cursor as in the screenshot above (so outside of the <p>tag and within the <article>tag I don’t understand why I’m getting the posof the <p> (333) node and not of the flexItem (<article>) node (332)?

Then I have also an issue with the nodesBetween part of the code above when a custom node (Image) view is involved, the template of that view looks like:

<template>
  <node-view-wrapper :as="as" :class="wrapper_cls">
    <img draggable data-drag-handle 
      ref="img" 
      class="rounded-lg" 
      :src="node.attrs.src" 
      :data-objectid="node.attrs['data-objectid']"
      :width="width_attr"
      :height="height_attr"
      :class="[img_cls, extra_cls_img, bg_color_cls]" 
    />
    <resizeNode v-if="editable" :editor="editor" :selected="selected" :node="img" @resize="(size) => updateAttributes(size)" v-show="selected && img" /> 
  </node-view-wrapper>
</template>

as of:

the Image node isn’t returned by nodesBetween, it stops at the parent node (flexItem) … any idea?

Thanks!

Mapping a coordinate to a DOM position is done via caretPositionFromPoint, a browser API that sometimes does odd things. ProseMirror corrects some of those oddities in vertical block layouts, but a horizontal flex layout may A) cause the browser to do funny things and B) prevent the correction kludge from working.

You don’t seem to mention what positions you passed to nodesBetween.

Interesting, does it means that, theorically, posAtDom could return 2 differents pos on different browsers?

I’m passing the let pos = view.posAtDOM(event.target) pos with .nodesBetween(pos, pos, (node, _pos) => { ... }, it looks like when I’m having a custom Node view I should do .nodesBetween(pos, pos+1, (node, _pos) => { ... } … I’m missing something…

Thanks!

Oh, hold on, you’re using posAtDOM. I was talking about posAtCoords (which indeed might be inconsistent depending on browser behavior, though I try to fix such issues). posAtDOM should be predictable, but it probably helps if you pass all the arguments it expects (you appear to be passing only one).

nodesBetween with the same start and end position will never iterate through any (non-text) leaf nodes, since no positions can fall inside of those.

Thanks, there is still something weird (or expected …?) about posAtDOM … or maybe I don’t understand the offset and bias parameters…

Let’s take an exmaple with a mouseover(view, event) handler:

mouseover(view, event) {   
  let pos = view.posAtDOM(event.target)
  let node = view.state.doc.nodeAt(pos)  
  // ...
}

For example when I’m hovering my mouse over a I’m getting the correct event.target (a section tag) but the pos and the node is the 's first child. What am I missing?

nodesBetween with the same start and end position will never iterate through any (non-text) leaf nodes, since no positions can fall inside of those.

That make sense…! thanks

OK … it looks like I’m having the same questioning as posAtDOM() returns wrong position

After reading the thread I now understand correctly what posAtDOM does…