posAtDOM() returns wrong position

I want to figure out a position of a node from drag event. I’m using

              const pos = view.posAtDOM(event.target as HTMLElement, 0);

I have the following node structure: figureGroupsubfigure+image caption. When I try to drag it by dragging the image, the above function returns the position which when use in view.state.doc.nodeAt(pos) returns correctly image node. BUT when I try to start dragging somewhere from subfigure area (and event.target correctly points to the subfigure node-view), the posAtDOM returns still the same position which point to image instead of subfigure. What am I doing wrong?

I tried to debug and here

  get posAtStart() {
        return this.parent ? this.parent.posBeforeChild(this) + this.border : 0;

for subfigure the result is 30 + 1(border) = 31, and for image its 31 + 0 (because its a leaf node). What is the best way to always get the correct node from event.target dom?

Plugin:

          dragstart: (view, event) => {
            if (!event.target) {
              return false;
            }

            const target = event.target as HTMLElement;
            const pos = view.posAtDOM(target, 0);
            const $pos = view.state.doc.resolve(pos);
            if ($pos.parent.type.name === 'subfigure') {
              event.preventDefault();
            }

            return false;
          },

Above pos is always the same even though target differs (subfigure-node when trying to drag entire node and img tag when trying to drag image)

Any suggestions? I guess the problem could be solved if view.posAtDOM would return the position does excludes the node border.

That’s how it is expected to behave—it gives you a document position that corresponds to the node you give it. Not every DOM node will have a unique document position.

so what can I do? Currently If I click image dom, then the function return the position of 31, which correctly points to the image node pos. But when I click subfigure dom, then your function tries to map subfigure node view to the position and it correctly points to the position 30 but it additionall adds a border which is one for that node and hence the results of nodeAt is the same in both cases even though I clicked different domes, both pointining to image and subfigure node views respectively. I guess that I need to figure out when to subtract this border from the pos but not sure how can I do that. Or maybe I should always subtract this border? If yes, how to get that for any node?

In situations like this (maybe not exactly like this) I’ve generally found there’s some generalizable rule that can usually be applied, eg add 3 or subtract 3 or whatever you find X to be. While it seems weird to need magic numbers, it can work.

Perhaps there’s a better way but I don’t know it.

Absolutely don’t add/subtract magic numbers you don’t understand.

Just figure out which DOM node you’re interested in, and find that, instead of just always using event.target.

Let me elaborate a little bit more. I selected entire subfigure node. Then I tried to drag it and in my handleDOMEventsdragstart, the event.target is div.react-renderer.node-subfigure, i.e. the DOM that corresponds to the subfigure node view. I would expect that when I call view.posAtDOM() and use that pos in view.state.doc.nodeAt(pos), the resulting node will be my subfigure node, while currently it is image node - the first child of my subfigure node. I debugged posAtDOM method and here is what I see:

image

First, desc is correct - it’s the node view of my subfigure node and its dom is my event.target. Then inside localPosFromDOM, atEnd is false and this.posAtStart is returned. This is calculated as follows: image

Above, border is 0 for leaf nodes and 1 for non leaf nodes. And this is where problem arised. While this.parent.posBeforeChild(this) returns 30, which when used in nodeAt() method returns the subfigure node, if we add that 1, the nodeAt returns image node. From the other side, when we click on image, then its this.parent.posBeforeChild(this) is 31, and border is 0 → again we get image (leaf) node.

Also, not sure what you mean by Just figure out which DOM node you’re interested in, and find that, instead of just always using event.target. The DOM which comes from event.target is the one in which I’m interested, as shown above.

You’re asking for the document position at index 0 in the subfigure DOM node. That’s what you get—the position right at the start of that document node. You seem to be assuming some kind of relation between posAtDOM and nodeAt that simply does not exist. If you want the position before the subfigure, give posAtDOM the subfigure’s parent node and its index.

Hmmm posAtDOM does not accept index argument and I don’t know how could I even find it from the event.target itself. If posAtDOM returns the position at the start of the node, is there any other method that returns the position right before start of a node which ignores border size? I want to prevent dragging of image nodes which are inside subfigure nodes but this way I can’t figure out whether I’m dragging entire subfigure or only image because event.target returns the same position in both cases.

1 Like

offset, the second argument, is the index into the DOM node.

Do you mean this kind of solution?

 const dragPlugin = new Plugin({
      props: {
        handleDOMEvents: {
          dragstart: (view, event) => {
            if (!event.target) {
              return false;
            }

            const target = event.target as HTMLElement;
            const parent = target.parentNode;
            let children = Array.from(target.parentNode?.childNodes || []);
            let offset = children.indexOf(target);

            const pos = view.posAtDOM(target.parentElement!, offset);
            const $pos = view.state.doc.resolve(pos);
            const node = view.state.doc.nodeAt(pos);

            if ($pos.parent.type.name === 'subfigure') {
              event.preventDefault();
            }

            return false;
          },
        },
      },
    });

Now node is indeed image when I’m dragging an image and subfigure when dragging subfigure DOM. Then above code stops dragging when trying to drag an image. I didn’t test it in any other cases, do you think it should work fine now?

Yes, that looks right.

1 Like