Handling mouse events for nodes

I have written a plugin that shows a button next to the line of text a cursor is on. I would like to have this behavior occur only when hovering over the node.

I have seen the suggestion to use handleDOMEvents here: How to add custom event handler to a node, more spcifically, mouse hover over a link?. But the mouseover and mouseenter events are attached to the editor, regardless of whether I use this prop in the plugin constructor or EditorView constructor.

Additionally, I am wondering how I can know which node is hovered, as I’m currently using view.state.selection to determine the node that the cursor is on. In a different approach, I created a custom ParagraphView and attached event listeners to its dom, but was unable to determine which node was hovered (the plugin requires access to a custom node attribute). Is there some way to create a transaction on mouse events?

I would appreciate any help thanks!

1 Like

Sure—handleDOMEvents can listen to mouseenter or similar events, and you can use posAtDOM with the event target, or posAtCoords with it coordinates, to figure out which paragraph the pointer is on.

1 Like

Thanks for the quick reply!

This seems a bit clunky, so I’d like to run through the approach (I am very new to prosemirror):

  • create a custom nodeviews for paragraphs, headers, etc (can this be simplified?) with mouseenter and mouseleave event listeners
  • on hover, dispatch a transaction with tr.setSelection based on the position returned from view.postAtDom
  • plugin will update due to change in state

Does this make for a viable solution?

No, you don’t need this—the handleDOMEvents prop can listen to events on the entire editor, no need to create handlers on specific nodes.

Absolutely not—you don’t want to select something just by hovering over it. Generally, selection and hover target are different things, and unifying them seems like a bad idea. Maybe create a state plugin with a state field that tracks the paragraph that’s being hovered over.

Thank you so much for your help, I’m able to get the desired behavior!!!

Initially I thought I would need to use event delegation to dynamically fire an event on mouseenter for nodes… but then I realized using mouseover works just fine.

And I didn’t know about plugin state fields! I refactored my plugin to be stateful and dispatch transactions with metadata of the position of the hovered node, and then update the position of the plugin’s view based on the state change. Quick question: I am accessing the metadata from view.state.plugin$… is this the correct approach? Since the logic is in the plugin’s view spec, the transaction obj is inaccessible so I can’t do tr.getMeta()…

In general, if a property isn’t in the docs, you’re not supposed to touch it. You can read state fields by giving your plugin a key. Accessing the transaction is done in the apply method of your state field.

1 Like

thanks for the mouseover tip. I was also using mouseenter which wasn’t resulting in the behavior I wanted.