handleSelection - Listen for selection change


Is there a hook to listen for selection change on the proseMirror view or state?

At the moment, I am listening for a mouseUp event on the container dom element and then reading the selection from the editorState after a delay of 150ms.

I would like to remove the need for a delay and use a custom handler.

Is there the equivalent of handleSelection on the props.

For example: handleSelection: (view, dispatch, selection) => true Where you can set the selection manually using setSelection with a transaction?


Observe transactions and compare their selection to the previous one (or use selectionSet if you want to know whether it was explicitly set or just moved to follow changes)


Why not use the update: ?⁠fn(view: EditorView, prevState: EditorState) of a plugin and compare the current state to the previous state?

For example, in the code below, the plugin can detect if the selection has changed.

return new Plugin({
        view (view) {
            return {
                update: function (view, prevState) {
                    var state = view.state;
                    if (prevState && prevState.doc.eq(state.doc) &&

                    // selection changed

My guess is that you have a reason this won’t suit your purposes, but I only ask for my own edification to understand Prosemirror better.

1 Like

Hey @marijn and @supersonicecho

This is a good idea, however…

The plugin is supposed to update a decorationSet when the document is clicked. Currently, the console.log fires when you start dragging a new selection and that is where the issue lies. Is there a way within the transaction to see the events?

Or for there to be methods that can override the default:



export default () =>
  new Plugin({
    state: {
      init(config, editorState) {
        const { doc } = editorState;
        return initDecorationSet(doc);
      apply(transaction, decorationSet) {

        const { docChanged, curSelection } = transaction;

        if (!docChanged && selectionIsCollapsed(curSelection)) {
          // This fires when the mouse is still mid selection
          console.log('This should only be called when the selection is a click event not a drag event')
          return updateDecorationSet(decorationSet, curSelection);
        return decorationSet;
    props: {
      decorations(state) {
        return this.getState(state);

Thanks for the help! Ace community.


You may be able to use the "pointer" meta property, which is set to true on transactions that were caused (directly) by the pointer device. If that’s there, and the selection is empty, it was probably a click.

You can certainly override DOM events if you want.

1 Like

Hey thanks for the quick reply.

Ah I didn’t know about the ‘pointer’ meta property - It looks like this is still present when dragging a selection which makes it unsuitable in this scenario.

When I use handleDOMEvents it looks like it never gets called… which is also a little odd. Am I missing something?

new Plugin({
    props: {
      handleDOMEvents: (view, event) => {
        console.log('DOM_EVENT', view, event);
        return true;

Yes, that’s why I suggested ignoring transactions that don’t create an empty selection.

You’re using it wrong. Check the docs again.


Hi, its possible detect when selection change was caused only by local changes? (ignore collaborative updates). Thanks


I don’t have much experience with collab, but I suspect that transactions that originate from non-local sources are marked with some type of metadata. Try outputting transactions to the console and look for metadata that only exists for transactions generated non-locally.

1 Like

Not really, by default. You should be able to rather easily add a metadata property to the transactions created for remote changes, though, and check for that.

1 Like