# Unexpected selection behavior on mouse click within and outside an existing selection

**URL:** <https://discuss.prosemirror.net/t/unexpected-selection-behavior-on-mouse-click-within-and-outside-an-existing-selection/5750>\
**Category:** Show\
**Created:** [August 3, 2023, 10:58am UTC](https://discuss.prosemirror.net/t/unexpected-selection-behavior-on-mouse-click-within-and-outside-an-existing-selection/5750 "2023-08-03T10:58:50Z")\
**Posts on this page:** 14\
**Page:** 1

<div class="post-metadata">

**Author:** ![dwzkit](https://discuss.prosemirror.net/user_avatar/discuss.prosemirror.net/dwzkit/32/4077_2.png) [@dwzkit](https://discuss.prosemirror.net/u/dwzkit)\
**Post date:** [August 3, 2023, 10:58am UTC](https://discuss.prosemirror.net/t/unexpected-selection-behavior-on-mouse-click-within-and-outside-an-existing-selection/5750/1 "2023-08-03T10:58:50Z")

</div>

Hello,

I am experiencing a peculiar issue related to selection behavior when using mouse clicks inside and outside of an existing selection. I am using ProseMirror with the basic schema and the Vue.js framework.

**Issue Description:**

- When I click _within_ an existing selection range (from, to), I expect the editor state to be updated such that `from` equals `to`, since the mouse click should collapse the selection to a single point. However, the actual outcome is `from` not equal to `to`.
- When I click _outside_ of the existing selection range, the editor state is updated as expected, with `from` equals `to`.

**Code:**

The relevant Vue.js code snippet that I am using is provided below:

```auto
<template>
  <div ref="editorRef" @click="onClick"></div>
  <div id="content" style="display: none;" >Hello ProseMirror</div>
</template>

<script setup lang="ts">
import { ref, onMounted } from 'vue';
import { EditorState,TextSelection } from 'prosemirror-state';
import { EditorView } from 'prosemirror-view';
import { Schema, DOMParser } from 'prosemirror-model';
import { schema } from 'prosemirror-schema-basic';
import { addListNodes } from 'prosemirror-schema-list';
import { exampleSetup } from 'prosemirror-example-setup';

const editorRef = ref<HTMLElement | null>(null);
const editor = ref<EditorView | null>(null);

onMounted(() => {
  const mySchema = new Schema({
    nodes: addListNodes(schema.spec.nodes, "paragraph block*", "block"),
    marks: schema.spec.marks
  });

  if (editorRef.value) {
    const elementNode = document.querySelector("#content")
    if (elementNode) editor.value =new EditorView(editorRef.value, {
      state: EditorState.create({
        doc: DOMParser.fromSchema(mySchema).parse(elementNode),
        plugins: exampleSetup({schema: mySchema})
      })
    });
  }
});

const onClick = () => {
  const { doc, selection } = editor?.value?.state
  const { from, to, empty } = selection
  console.log("from.to",from,to)
}
</script>

```

I’d appreciate it if you could take a look and let me know if there is something I am missing or if this is an actual issue. Thanks in advance!

---

<div class="post-metadata">

**Author:** ![marijn](https://discuss.prosemirror.net/user_avatar/discuss.prosemirror.net/marijn/32/15_2.png) [@marijn](https://discuss.prosemirror.net/u/marijn)\
**Post date:** [August 3, 2023, 1:34pm UTC](https://discuss.prosemirror.net/t/unexpected-selection-behavior-on-mouse-click-within-and-outside-an-existing-selection/5750/2 "2023-08-03T13:34:23Z")

</div>

Can you reproduce the issue on [prosemirror.net](http://prosemirror.net)’s demo editor? If so, which browser and platform are you using? And if not, could you simplify your example code to not include Vue or other irrelevant libraries?

---

<div class="post-metadata">

**Author:** ![dwzkit](https://discuss.prosemirror.net/user_avatar/discuss.prosemirror.net/dwzkit/32/4077_2.png) [@dwzkit](https://discuss.prosemirror.net/u/dwzkit)\
**Post date:** [August 4, 2023, 6:01am UTC](https://discuss.prosemirror.net/t/unexpected-selection-behavior-on-mouse-click-within-and-outside-an-existing-selection/5750/3 "2023-08-04T06:01:28Z")

</div>

I’ve written an HTML version of the code (ESM module version) as follows:

```auto
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>ProseMirror Editor</title>
    <link rel="stylesheet" href="editor.css">
    <script type="module">
        // Import the ProseMirror modules
        import { EditorState, TextSelection } from 'https://cdn.skypack.dev/prosemirror-state';
        import { EditorView } from 'https://cdn.skypack.dev/prosemirror-view';
        import { Schema, DOMParser } from 'https://cdn.skypack.dev/prosemirror-model';
        import { schema } from 'https://cdn.skypack.dev/prosemirror-schema-basic';
        import { addListNodes } from 'https://cdn.skypack.dev/prosemirror-schema-list';
        import { exampleSetup } from 'https://cdn.skypack.dev/prosemirror-example-setup';

        const editorElement = document.querySelector('#editor');
        const contentElement = document.querySelector('#content');

        const mySchema = new Schema({
            nodes: addListNodes(schema.spec.nodes, "paragraph block*", "block"),
            marks: schema.spec.marks
        });

        let editor = null;

        if (editorElement && contentElement) {
            editor = new EditorView(editorElement, {
                state: EditorState.create({
                    doc: DOMParser.fromSchema(mySchema).parse(contentElement),
                    plugins: exampleSetup({ schema: mySchema })
                })
            });

            editorElement.addEventListener('click', function() {
                const { doc, selection } = editor.state;
                const { from, to, empty } = selection;
                console.log("from.to", from, to);
            });
        }
    </script>
</head>
<body>
<div id="editor"></div>
<div id="content" style="display: none;">Hello ProseMirror</div>
</body>
</html>

```

I have tested this on MacBookPro M1 Chrome(114.0.5735.133 (arm64)), Edge(115.0.1901.188 (arm64)), Firefox(115.0.1 (64 bit)) and Safari(16.5 (18615.2.9.11.4)) browsers. The problem still exists.

 ![image](https://discuss.prosemirror.net/uploads/secondsite/original/2X/1/1ea43ac0bd256f006236a15637e78ebb619e0451.png) ![image](https://discuss.prosemirror.net/uploads/secondsite/original/2X/c/ce538780b84d68cce60b0fb1923fccc7ac0a2036.png) ![image](https://discuss.prosemirror.net/uploads/secondsite/original/2X/6/6f67d08a3874f6ec95545fdbc790cda01a4c2547.png)

---

<div class="post-metadata">

**Author:** ![marijn](https://discuss.prosemirror.net/user_avatar/discuss.prosemirror.net/marijn/32/15_2.png) [@marijn](https://discuss.prosemirror.net/u/marijn)\
**Post date:** [August 4, 2023, 7:08am UTC](https://discuss.prosemirror.net/t/unexpected-selection-behavior-on-mouse-click-within-and-outside-an-existing-selection/5750/4 "2023-08-04T07:08:30Z")

</div>

You’re still using a CDN. That generally doesn’t work because most CDNs are dumb about diamond dependencies (where multiple direct dependencies end up pulling in the same transitive dependency), and duplicate them.

---

<div class="post-metadata">

**Author:** ![dwzkit](https://discuss.prosemirror.net/user_avatar/discuss.prosemirror.net/dwzkit/32/4077_2.png) [@dwzkit](https://discuss.prosemirror.net/u/dwzkit)\
**Post date:** [August 4, 2023, 9:09am UTC](https://discuss.prosemirror.net/t/unexpected-selection-behavior-on-mouse-click-within-and-outside-an-existing-selection/5750/5 "2023-08-04T09:09:28Z")

</div>

I’ve switched to using pnpm modules and bundling with rollup.js. The code for `index.html`, `main.js`, and `rollup.config.js` are as follows:

 ![image](https://discuss.prosemirror.net/uploads/secondsite/original/2X/a/a75579a7c882eaba176f93a2b7c02e0e3db74b57.png)

`index.html`:

```auto
<!DOCTYPE html>
<html>
<head>
  <title>ProseMirror Editor</title>
  <script src="dist/prosemirror-bundle.min.js" defer></script>
  <link rel="stylesheet" href="dist/editor.css">
</head>
<body>
<!--<div id="mad_interactive_editor"></div>-->
<div id="editor"></div>
<div id="content" style="display: none;">
  <p>Hello ProseMirror</p>
  <p>This is editable text. You can focus it and start typing.</p>
</div>
</body>
</html>

```

`main.js`:

```auto
import { EditorState } from 'prosemirror-state';
import { EditorView } from 'prosemirror-view';
import { Schema, DOMParser } from 'prosemirror-model';
import { schema as basicSchema } from 'prosemirror-schema-basic';
import { addListNodes } from 'prosemirror-schema-list';
import { exampleSetup } from 'prosemirror-example-setup';

// Mix the nodes from prosemirror-schema-list into the basic schema to
// create a schema with list support.
const mySchema = new Schema({
    nodes: addListNodes(basicSchema.spec.nodes, 'paragraph block*', 'block'),
    marks: basicSchema.spec.marks,
});

window.view = new EditorView(document.querySelector('#editor'), {
    state: EditorState.create({
        doc: DOMParser.fromSchema(mySchema).parse(document.querySelector('#content')),
        plugins: exampleSetup({ schema: mySchema }),
    }),
});

window.addEventListener('click', function() {
    const { doc, selection } = window.view.state;
    const { from, to, empty } = selection;
    console.log("from.to", from, to);
});

```

`rollup.config.js`:

```auto
import resolve from '@rollup/plugin-node-resolve';
import commonjs from '@rollup/plugin-commonjs';
import replace from '@rollup/plugin-replace';
import terser from '@rollup/plugin-terser';

export default {
    input: 'public/js/main.js', // Entry point for the bundle
    output: {
        file: 'dist/prosemirror-bundle.min.js', // Output file path and name
        format: 'umd', // Format of the output bundle (Universal Module Definition)
        name: 'ProseMirror' // Name of the exported global variable
    },
    plugins: [
        resolve({
            browser: true, // Resolve modules for browser environment
            preferBuiltins: false // Do not prefer built-in modules over local modules
        }),
        commonjs(), // Convert CommonJS modules to ES6
        replace({
            'process.env.NODE_ENV': JSON.stringify(process.env.NODE_ENV || 'development'), // Replace NODE_ENV variable with 'development' (or current environment)
            preventAssignment: true // Prevent replacement of variable assignment
        }),
        (process.env.NODE_ENV === 'production' && terser()) // If in production environment, minify the code
    ]
};

```

I bundle with `rollup --config` , but after deploying, the problem on the page is still the same. This time I believe I’ve ruled out the factor of CDN. I wonder if there’s something wrong with my approach?

---

<div class="post-metadata">

**Author:** ![marijn](https://discuss.prosemirror.net/user_avatar/discuss.prosemirror.net/marijn/32/15_2.png) [@marijn](https://discuss.prosemirror.net/u/marijn)\
**Post date:** [August 4, 2023, 10:16am UTC](https://discuss.prosemirror.net/t/unexpected-selection-behavior-on-mouse-click-within-and-outside-an-existing-selection/5750/6 "2023-08-04T10:16:02Z")

</div>

Did you ever test on [prosemirror.net](http://prosemirror.net) to see if the same problem exists there?

---

<div class="post-metadata">

**Author:** ![dwzkit](https://discuss.prosemirror.net/user_avatar/discuss.prosemirror.net/dwzkit/32/4077_2.png) [@dwzkit](https://discuss.prosemirror.net/u/dwzkit)\
**Post date:** [August 4, 2023, 11:35am UTC](https://discuss.prosemirror.net/t/unexpected-selection-behavior-on-mouse-click-within-and-outside-an-existing-selection/5750/7 "2023-08-04T11:35:02Z")

</div>

![image](https://discuss.prosemirror.net/uploads/secondsite/original/2X/2/2476cc0b454ddb57077b4fc79ba3a209f7cf35b0.png)

I’ve just tested it on [https://prosemirror.net/](https://prosemirror.net/), and I encountered the same issue.

---

<div class="post-metadata">

**Author:** ![marijn](https://discuss.prosemirror.net/user_avatar/discuss.prosemirror.net/marijn/32/15_2.png) [@marijn](https://discuss.prosemirror.net/u/marijn)\
**Post date:** [August 5, 2023, 9:42am UTC](https://discuss.prosemirror.net/t/unexpected-selection-behavior-on-mouse-click-within-and-outside-an-existing-selection/5750/8 "2023-08-05T09:42:41Z")

</div>

When I (on both macOS Chrome and Safari), go to [prosemirror.net](http://prosemirror.net), create a selection, and then click somewhere inside that selection, I do see the selection replaced by a cursor at the point of the click, which seems the appropriate behavior.

---

<div class="post-metadata">

**Author:** ![dwzkit](https://discuss.prosemirror.net/user_avatar/discuss.prosemirror.net/dwzkit/32/4077_2.png) [@dwzkit](https://discuss.prosemirror.net/u/dwzkit)\
**Post date:** [August 5, 2023, 10:33am UTC](https://discuss.prosemirror.net/t/unexpected-selection-behavior-on-mouse-click-within-and-outside-an-existing-selection/5750/9 "2023-08-05T10:33:42Z")

</div>

You are correct; my observation matches what you described. However, on the program side, I notice that the values of ‘from’ and ‘to’ within the click event (as logged by console.log) remain different, even though the text selection has been replaced by a cursor. I would expect the values of ‘from’ and ‘to’ to become the same once the text selection is replaced by a cursor, but the values printed by console.log for ‘from’ and ‘to’ remain the same as they were during the text selection operation.

---

<div class="post-metadata">

**Author:** ![marijn](https://discuss.prosemirror.net/user_avatar/discuss.prosemirror.net/marijn/32/15_2.png) [@marijn](https://discuss.prosemirror.net/u/marijn)\
**Post date:** [August 5, 2023, 11:42am UTC](https://discuss.prosemirror.net/t/unexpected-selection-behavior-on-mouse-click-within-and-outside-an-existing-selection/5750/10 "2023-08-05T11:42:48Z")

</div>

I see. At the time of the click event, the editor apparently hasn’t received the selectionchange event and hasn’t updated their selection state yet.

---

<div class="post-metadata">

**Author:** ![rnystrom](https://discuss.prosemirror.net/user_avatar/discuss.prosemirror.net/rnystrom/32/3684_2.png) [@rnystrom](https://discuss.prosemirror.net/u/rnystrom)\
**Post date:** [October 6, 2023, 7:36pm UTC](https://discuss.prosemirror.net/t/unexpected-selection-behavior-on-mouse-click-within-and-outside-an-existing-selection/5750/11 "2023-10-06T19:36:01Z")

</div>

[I ran into this same issue using TipTap](https://github.com/ueberdosis/tiptap/issues/4478), and believe the underlying issue is somewhere in Prosemirror.

I spun up a minimal reproducible example here:

> **[ecstatic-mclean-gkgpn5 - CodeSandbox](https://codesandbox.io/s/ecstatic-mclean-gkgpn5?file=%2Fsrc%2Feditor%2Findex.jsx)**
>
> ecstatic-mclean-gkgpn5 by rnystrom using less, pretty-format, prosemirror-example-setup, prosemirror-model, prosemirror-schema-basic, prosemirror-schema-list, prosemirror-state, prosemirror-view, react

The demo uses a very simple plugin to just log selection changes in `Plugin.view.update`:

```ts
function DemoPlugin() {
  return new Plugin({
    view() {
      return {
        update(view, prevState) {
          console.log(`prev: ${prevState.selection.from} ...`);
        }
      };
    }
  });
}

```

Here’s a video demonstrating the issue:

Some notes on the issue:

- This only occurs when you click _inside_ a span of selected text
- I expect the `update()` method to be called with a new selection
- After clicking inside the selection (which clears it), if you click again I then receive the new, correct selection
- The behavior only occurs with `editable: () => false`

---

<div class="post-metadata">

**Author:** ![prosed](https://discuss.prosemirror.net/letter_avatar_proxy/v4/letter/p/aeb1de/32.png) [@prosed](https://discuss.prosemirror.net/u/prosed)\
**Post date:** [November 23, 2023, 1:46am UTC](https://discuss.prosemirror.net/t/unexpected-selection-behavior-on-mouse-click-within-and-outside-an-existing-selection/5750/12 "2023-11-23T01:46:14Z")

</div>

I believe we may be hitting some form of this as well.

I’m trying to implement mixed selections of text and images and I’m experiencing this also. If we click the boundary of our selection in the text, the selection fails to collapse.

Unfortunately because [prosemirror.net](http://prosemirror.net) implements the `image` as an `inline` node we can’t attempt to reproduce the issue there.

---

<div class="post-metadata">

**Author:** ![jtormey](https://discuss.prosemirror.net/user_avatar/discuss.prosemirror.net/jtormey/32/4485_2.png) [@jtormey](https://discuss.prosemirror.net/u/jtormey)\
**Post date:** [March 3, 2025, 10:21pm UTC](https://discuss.prosemirror.net/t/unexpected-selection-behavior-on-mouse-click-within-and-outside-an-existing-selection/5750/13 "2025-03-03T22:21:55Z")

</div>

I just ran into this issue as well, while using the BubbleMenu extension by Tiptap.

In non-editing mode, if you select some range of text, then attempt to deselect it by clicking within the range of selected text, the selection will go away but the editor `selectionUpdate` event will not fire and the selection state doesn’t change. Everything works properly when deselecting the text by clicking anywhere else.

It’s easy to notice with BubbleMenu, since the extension relies on the selection state. I’ve had to implement a workaround for now since when this bug happens the menu will stay open, despite there being no selected text.

---

<div class="post-metadata">

**Author:** ![prosed](https://discuss.prosemirror.net/letter_avatar_proxy/v4/letter/p/aeb1de/32.png) [@prosed](https://discuss.prosemirror.net/u/prosed)\
**Post date:** [March 8, 2025, 6:19pm UTC](https://discuss.prosemirror.net/t/unexpected-selection-behavior-on-mouse-click-within-and-outside-an-existing-selection/5750/14 "2025-03-08T18:19:07Z")

</div>

Does selection update fire for the expanded selection you mentioned? I guess yes?

I wasn’t under the impression it ever fired when it wasn’t editable though.
