NodeView constructor being called multiple times

Hi, I’m currently trying to integrate MathJax into an editor, and began with inline math first. I created an input rule (/ $.+$ /), which, when triggered, created an inline_math node, defined here

inline_math: {
  inline: true,
  group: "inline",
  attrs: {
    texts: {}
  draggable: true,
  parseDOM: [{tag: "span"}],
  toDOM(node) { return ["span", 0]}

which was linked to a NodeView, defined here.

export class InlineMathView {

  constructor(node: Node, view: EditorView, getPos: () => number) {
      this.node = node;
      this.view = view;
      this.getPos = getPos;
      this.dom =  document.createElement("span");


So upon creation of the node, its content, which is set to $$, is typesetted and its content rendered into the DOM. Currently, the editor can correctly parse the ‘$’ delimiters and render the MathJax content, but upon pressing any character after the NodeView, the constructor is called again, as the content is typesetted again, as seen here. Also, when the NodeView is created again for more inline math on the same line, the other views’ constructors are called and are all re-rendered. I’m not too familiar with NodeViews currently, but I’m wondering what could be the cause of this problem?

Also, apologies if my last few posts have been vague or without much context. If there’s any more information needed for this post, however, please let me know.

1 Like

ProseMirror reserves the right to recreate node views any time it pleases, but it tries to be efficient by reusing the ones that are already in the DOM. I’m not sure why it wouldn’t do that in this case. Can you distill it down to a minimal example (possibly replacing the math node with an even simpler inline node, say a view for images) and see if it still happens there? If so, I’ll try to debug that.

1 Like

Sorry another thing I should have clarified was that, for the case where there were multiple NodeViews in a line, they would only be re-rendered after typing any character after the last NodeView was created, if that changes things. I recently tried it with an image view like so:

export class ImageView {
  private node: Node;
  private view: EditorView;
  private getPos: () => number;
  public dom: HTMLElement;

  constructor(node: Node, view: EditorView, getPos: () => number) {
    this.node = node;
    this.view = view;
    this.getPos = getPos;
    this.dom = document.createElement("img");
    this.dom.setAttribute("src", node.attrs.src);
    this.dom.addEventListener("click", e => {
    console.log("You clicked me!");

and the same issue occurred. I also found out that, if you are on an empty line, create the NodeView, and type any character, it won’t call the constructor multiple times, but if at any point the line isn’t empty and the NodeView is created, it will re-render. I can send further Prosemirror-related code if it is needed. Thank you.

When I try it, adding a node view for image and typing around in a paragraph with images in it does not recreate the node view.

1 Like

Ah, I see. I have tried to strip every feature of my editor, including a custom dispatchTransaction and other node views, yet the image views are still re-rendered after typing a single character after the node view. Could you elaborate on the common situations in which a node view is re-rendered? Thank you.

Are you running a recent version of prosemirror-view? Older ones had some bugs where this would happen. Again, during normal typing it shouldn’t, so if you can put your test case on jsbin or glitch or something, that would make it easier for me to figure out what’s going on.

1 Like

I’m currently running the most recent version of prosemirror-view. I just tested an image NodeView in this glitch, and from this gif glitch-demo

when you create an ImageView, then press a character right after, it is constructed again. I’m wondering now if it’s just expected behavior? Thank you in advance.

I’m not seeing the image view being recreated in that glitch when I type after the image.

I just tried it on different browsers and it seems that Firefox doesn’t seem to have this issue. Are you using Firefox as well?

I tested Firefox and Chrome.

Any update on this issue?

I’m noticing the same behavior mentioned above: my custom inline nodeview is being re-created each time when there’s no characters before or after the nodeview, and a new character is inserted either before or after the nodeview.

The recreation of the nodeview is only an issue right after it is inserted, when the x^2 is deleted. However, I think this has to do more with how I’m using inputRules to initially insert the node.

new InputRule(/(?:\$)([^\$]+)(?:\$)$/, (state, match, start, end) => {
    const attrs = getAttrs instanceof Function ? getAttrs(match) : getAttrs
    const [matchedText, content] = match;
    const {tr} = state;
    if (matchedText) {
        // Create new Math node with content.
        const node = type.create(attrs, schema.text(content));
        tr.replaceWith(start, end, node);
        const cpos = tr.doc.resolve(tr.selection.anchor - tr.selection.$anchor.nodeBefore.nodeSize);
        tr.setSelection(new NodeSelection(cpos));
    return tr

I’m guessing it’s because when I replace the selection with a new node, the text content within the node is not being synchronized with the editor.

A temporary solution, which I got from Inline Nodes With Content, is to add a space before and after the nodeview. This stops the nodeview from being re-created.

Edit Solution was, as marijn said, to update prosemirror-view. I had only updated prosemirror-view for my dependencies and not the main libraries themselves. :slight_smile:

That kind of sounds like an issue that I recently looked at. Does prosemirror-view version 1.12.1 still have the problem? If so, can you set up a minimal demo?

Updating prosemirror-view to 1.12.1 in my main npm library and dependencies (I’m also using TipTap) solved the problem with the nodeview constructor being called multiple times :slight_smile:. I ran into the issue as I previously had only updated my dependencies.