Confused by inconsistent nodeview dragging behavior

I’m trying to understand how to make my nodeviews draggable. That is, I’d like to be able to click them with the mouse, drag them to some other location in the document, and drop them there. I’ve found that I can sometimes do this, but I’m lost trying to figure out why it works sometimes (rarely) and not others.

Here’s an example where the dragging doesn’t work:

move-marker-fail

You can see that the drag seems to be doing something, but when I drop the item it just bounces back to where it started.

Here’s an example of where it works (on the last drag attempt):

move-marker-success

There seems to be some set of ‘magic pixels’ for which the drag works. My nodeviews are both ‘draggable’ and ‘selectable’.

What I’m looking for is some guidance on what I should be looking at in my code. I’m at a bit of a dead end. How should I implement my nodeview to properly support dragging? What might I have done that could cause this sort of mixed behavior? Any help would be deeply appreciated.

Is the difference possibly the node that’s being hit by the mouse event? I have no idea what kind of node you’re trying to drag here, and what its DOM structure is like. The way you sometimes get a drag element that looks like it is a transparent rectangle covering some of the other content suggest there’s something interesting going on in the DOM.

The DOM is pretty simple, I guess:

<div class="marker" data-marker-name="marker" data-selected="false" data-highlighted="false" contenteditable="false" draggable="true" data-visible="true">
<span class="marker-label">marker</span>
</div>

The CSS adds before and after pseudo elements to attach the flag decorations. And the label on the marker is positioned absolute. Is there anything else you can think of that might be relevant?

Also, one thought: are those data attributes - esp. data-selected - problematic? Will they interfere with PM in some way?

Thanks for pointing me in the right direction. It turns out that the “bad” drags were happening when they were initiated on the pseudoelements. By making these pseudoelements draggable, everything seems to work just fine.

FWIW, the only way I could find to do this was to set the draggable attribute via CSS. Both my IDE and the browser devtools complain about me doing this, but it works.

A pointer-events: none style on the elements you don’t want to participate in pointer interaction often helps in cases like this

I thought I had this sorted out, but it seems not. After a lot of noodling around, I think maybe I just have the wrong model in my head of how things are supposed to work re drag with nodeview. Here’s what I (naively?) expected:

  • Any dragstart events originating from within the dom attribute of the NodeView result in drags for the node.
  • The dataTransfer for the event would contain the toDOM-ified node as “text/html” content.
  • Subsequent drops would result in the insertion of a new node which ultimately create a new NodeView.

And indeed this is what happens when these drags work, i.e. when I click on a ‘magic’ part of the elements. The rest of the time I just have no content in the event.

Is this model correct? And is it largely automatic for ‘draggable’ nodes, or do I need to do anything extra to make it work?

Also, related to this, I find that if I set ‘draggable’ to false in the NodeSpec for this node, dragging actually works really well! I can fairly consistently drag from the pseudo-elements (i.e. the little blue flag) as well as the text part. This is really surprising to me, but perhaps that’s a result of some deeper misunderstanding on my part.

Thanks for your help on this so far, and (clearly) any other guidance would be very helpful.

I’ve certainly had my share of head scratchers like this. In a few of my large editor projects you’ll end up finding sections of the code with details of “we set this to draggable false, because…” and “If you try to set it to true, check this case this case and this case. See this discussion on ProseMirror forums: https://…”

Suffice to say, it’s okay if you ended up using trial and error, I just recommend being paranoid about regressions and try to combat them with docs.

Good luck! :blush:

Yes, it is. Have you checked whether the dragstart is being fired at all when this goes wrong? And whether a stopEvent method may be eating it?

1 Like

I added a ‘dragstart’ listener to the DOM created by the nodeview, and it does seem to be called every time I try dragging a marker, whether the drag is correct or not. The events all look the same, as far as I can tell.

I did notice one interesting thing which may mean something. For a given marker, it may take an arbitrary number of attempts (i.e. bad drags) before a drag works. But once a drag for a particular marker does work, it then works consistently for that marker. I don’t know what to make of it, but it seems consequential.

And whether a stopEvent method may be eating it?

I haven’t had a chance to sort this out yet, but I’ll look into when I get a chance.

I think I finally got to the bottom of this, though I don’t fully understand it. Here’s what I see.

When I click on a newly created marker, I see a mousedown followed by a dragstart followed by a call to NodeView.selectNode(). What seems to be important here is that the selectNode is after the dragstart, so the drag isn’t applied to the correct element (or something like that…I’m not sure my terminology is correct). If I drag and release the mouse, I get a “bad drag” with no change in the marker’s location. Critically, when I subsequently move the mouse I get a call to NodeView.deselectNode().

What I’ve found, though, is that if I don’t move the mouse after a failed drag, but instead immediately click the marker again, the drag starts on the right element and everything works. That is, by avoiding the delectNode call, I’m able to start dragging the node correctly. This state remains until I click something else that does deselect the marker.

What I take from this is that I have to make sure the node is selected before I can drag it. Indeed, if I modify my marker’s mousedown handler to select the marker node, then everything works wonderfully.

Does this sound like the right thing to do? Or am I patching over a problem that should be fixed elsewhere?

Odd. In a demo like the dino one, I can drag the elements right away, even when not selected.