Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Unexpected behavior in node view where this.dom a bare div #1505

Closed
anandthakker opened this issue Jan 29, 2025 · 4 comments
Closed

Unexpected behavior in node view where this.dom a bare div #1505

anandthakker opened this issue Jan 29, 2025 · 4 comments

Comments

@anandthakker
Copy link

I have a node view whose dom looks like this:

<div>
  <div class="label">blah</div>
  <div class="content">...</div>
</div>

with contentDOM equal to the class=content child node. The node this is used for contains rich text (essentially block+). I'm seeing very strange behavior: if there are two paragraphs in the node, with the second having a single character, then deleting that character causes a very incorrect change to the document.

Some observations:

  1. Happens in both Safari and Chrome. Does NOT happen in Firefox.
  2. In practice, that "label" piece is marked contenteditable=false, but the bug happens either way.
  3. The details of the incorrect change do seem to be sensitive to some unknown details -- in my actual application, the entire contents of the node get deleted; in the minimal reproduction linked below, the node's content is fine, but an additional node is prepended to the document.
  4. ❗ Adding a class name (or id, or style, or apparently any attribute) to the outer div seems to prevent the unexpected behavior.
  5. I spent a little time stepping through the debugger and noticed some suspect-looking mutations from the MutationObserver, but didn't have time to actually investigate the mutations in detail.

For my purposes, I think 4 is probably going to be sufficient to fix the issue (and really, having a class name on that node is better for me anyway). But I wanted to report it in case this is revealing an issue where an unusual-but-correct set of mutations--or a bug in Safari and Chrome's MutationObserver implementation?--aren't getting handled correctly in ProseMirror.

Minimal repro: https://jsfiddle.net/t1wh7543/17/

(here's the repro code in case the link doesn't work) const mySchema = new prosemirrorModel.Schema({ nodes: { doc: { content: "section+" }, section: { content: "paragraph+" }, paragraph: { content: "text*", toDOM: () => ["p", 0] }, text: { inline: true }, }, marks: { strong: { toDOM: () => ["b", { contenteditable: false }, 0], parseDOM: [{ tag: "b" }], }, }, })
window.view = new prosemirrorView.EditorView(
  document.getElementById("editor"),
  {
    state: prosemirrorState.EditorState.create({
      doc: prosemirrorModel.DOMParser.fromSchema(mySchema).parse(
        document.getElementById("content"),
      ),
      plugins: prosemirrorExampleSetup.exampleSetup({ schema: mySchema }),
    }),
    nodeViews: {
      section: (node, view, getPos) => {
        const dom = document.createElement("div")
        // Uncommenting this line appears to fix the problem
        // dom.className = "section-wrapper"
        
        const label = document.createElement("div")
        label.innerText = "[SECTION]"
        label.style.fontSize = '75%';
        dom.appendChild(label)

					const content = document.createElement("div")
        content.className = "section-content"

					dom.appendChild(content)
        return {
          dom,
          contentDOM: content,
        }
      },
    },
  },
)
@marijnh
Copy link
Member

marijnh commented Jan 29, 2025

Your node types don't seem to have any parse rules associated with them at all. You're going to want your document to be representable as HTML, and parseable from HTML.

@anandthakker
Copy link
Author

Oh, they do in our application, I just omitted from the repro example. Here's an updated one with parseDOM & toDOM included in the node specs: https://jsfiddle.net/hct596jb/6/

@marijnh
Copy link
Member

marijnh commented Jan 29, 2025

What appears to be happening is that that Chrome 'normalizes' out the attribute-less <div> element as part of its backspace-last-character-in-block behavior. This prevents ProseMirror from realizing that the content in the editor was drawn as a section element, making it try to parse the decorating elements of your node view as actual content. Firefox doesn't do this weird thing, which is why the issue is specific to Webkit-ish browsers.

I recommend just putting on that class name. We do rely on the native backspace implementation, and when that does weird things, working around them is more or less the only option.

@anandthakker
Copy link
Author

👍 thanks for the quick response and for taking a look.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants