Saving content containing DOM generated by nodeView

Hi,
I have set up a nodeView for wrapping tags inside a wrapping container in which I append another for displaying a resize handler in the corner above the image, another as a tooltip displaying the width of the image. The DOM structure of the nodeView is:

<div class="ProseMirror">
<span class="wrap">
<span class="resize"></span>
<span class="tooltip">200px</span>
<img src="...">
</span>
</div>

At present, when I save the content to my database, the DOM generated by the nodeView is saved in my database. Then, when I reload the content from my database into my editor, Prosemirror strangely unwrap the innerText of the tooltip span and “relocates” it above the wrap span.
I get the structure below:

<div class="ProseMirror">
200px
<span class="wrap">
<span class="resize"></span>
<span class="tooltip">200px</span>
<img src="...">
</span>
</div>

The same happens if the wrap span contains text content (<span class="wrap">hello<span class="resize">...</span>). It gives after reloading from database: <div class="ProseMirror">hello200px<span...</span></div>.

Should I remove the generated DOM from the content before saving to database ? Or is this rather related to the Schema not parsing the spans correctly ?

thank you

It sounds like you’re using the editor’s innerHTML and saving that in the database. In general, that’s a bad idea. Define data-only toDOM methods for your node types, and use a DOMSerializer to create a clean representation of the document when saving.

HI @marijn, I use document.createElement and innerText inside the constructor of my NodeView class, then assign the built element to the dom property of the class. Is it a wrong way ? Here’s my code:

class ResizableImageNodeView {
  constructor (node, view, getPos, decorations) {
    const wrap = document.createElement('span');
    wrap.classList.add('wrap');
    wrap.style.width = node.attrs.width + 'px';
    wrap.innerText = 'hello';

    const handle = document.createElement('span');
    handle.classList.add('resize-handle');
    handle.innerText = 'hello';

    const img = document.createElement('img');
    img.setAttribute('src', node.attrs.src);
    img.setAttribute('width', node.attrs.width);
    img.setAttribute('data-src-width', node.attrs['data-src-width']);
    img.setAttribute('height', node.attrs.height);
    const maxWidth = node.attrs['data-src-width'];
    const ratio = node.attrs.height / node.attrs.width;

    const tooltip = document.createElement('span');
    tooltip.classList.add('tooltip');
    tooltip.innerText = node.attrs.width + 'px';

    wrap.appendChild(handle);
    wrap.appendChild(tooltip);
    wrap.appendChild(img);

    this.dom = wrap;
    this.handle = handle;
    this.tolltip = tooltip;
    this.img = img;
  }
  selectNode () {
    this.handle.classList.add('active');
    this.tooltip.classList.add('active');
    this.img.classList.add('ProseMirror-selectednode');
  }

  deselectNode () {
    this.handle.classList.remove('active');
    this.tooltip.classList.remove('active');
    this.img.classList.remove('ProseMirror-selectednode');
  }
}

I wasn’t talking about the node view implementation, I was talking about the way you store content in the database.

I don’t understand: I save to my database the HTML produced by Prosemirror in the editor using view.dom.innerHTML, which is valid HTML. And I load it from my database into the editor using DOMParser.
Do I really need to use a DOMSerializer to get the content from the editor ? I cannot find an example in the docs and not sure about how to use it.

thank you

Yes, and I’m saying don’t do that, use DOMSerializer. It’s documented, and not hard to use.

let scratch = document.createElement("div")
scratch.appendChild(DOMSerializer.fromSchema(mySchema).serializeFragment(myDoc.content))
return scratch.innerHTML
1 Like

Indeed ! thank you.
I love ProseMirror.