Maximum call stack size exceeded

I wonder if anybody could pinpoint what I might be doing wrong here.

I am trying to create a “custom html” node. All seems fine except on copy paste cut operations i sometimes get “Maximum call stack size exceeded” errors. This is in function compareDeep(a, b) {

This happens when I click on the node twice … so it might be that I am doing the selection of the node wrong.

I have defined it in the schema as

custom_html: {
        content: "block+",
        group: "block",
        defining: true, // node is considered an important parent node during replace operations
        selectable: true,
        atom: true, // though this isn't a leaf node, it doesn't have directly editable content and should be treated as a single unit in the view.
        draggable: false, // does not work !!!
        // isolating: true, // When enabled (default is false), the sides of nodes of this type count as boundaries that regular editing operations, like backspacing or lifting, won't cross.
        attrs: {
            dom: {
                default: {}
            }
        },
        parseDOM: [{
            tag: 'div',
            getAttrs: dom => {
                return { dom: dom };
            }
            // getContent: dom => {
            //     return 'This is the content of it.'
            // }
        }],
        toDOM(node) {
            // console.log('node');
            // console.log(node.attrs.dom);
            // let n = document.createElement("div");
            // n.innerHTML = '<p>Something</p>';
            let newClone = node.attrs.dom.cloneNode(true)
            // newClone.addEventListener("click", ()=>{ console.log('mouseup');}
            return newClone;

        }
    },

And my NodeView looks like this

import {
    // Selection,
    // TextSelection,
    NodeSelection
    // AllSelection
} from "prosemirror-state"

export default class DOMinatorCustomHtmlView {
    constructor(node, view, getPos) {
        // We'll need these later
        this.node = node
        this.view = view
        this.getPos = getPos
        this.dom = node.attrs.dom.cloneNode(true);
        this.dom.addEventListener("mousedown", event => {
            this.view.dispatch(this.view.state.tr.setSelection(
                NodeSelection.create(
                this.view.state.doc,
                this.getPos()
            )));
        });
       
    }

    selectNode() {
        this.dom.classList.add("ProseMirror-selectednode")
    }

    deselectNode() {
        this.dom.classList.remove("ProseMirror-selectednode")
    }

    update(node, decorations) {
        console.log('update --- DOMinatorCustomHtmlView');
    }

    stopEvent(event) {
        if(event.type === 'mousedown'){
            return true;
        }
        return false;
    }

    ignoreMutation() {
        return true;
    }

    // Called when the node view is removed from the editor or the whole editor is destroyed.
    destroy() {
        this.dom.remove();
        console.log('destroy --- DOMinatorCustomHtmlView');
    }

}

You will find all code here. The repo is very easy to get up and running (2 min) if like to see what is happening.

compareDeep is used to compare (node or mark) attributes. A stack overflow there suggests you’ve created a cyclic data structure and stored it in an attribute.

1 Like

I guess I won’t be able to do this.

getAttrs: dom => {
                return { dom: dom };
            }

What I am trying to achieve here is to retain the original “custom” html as much as possible. Including attributes as well as event handlers.

Is it even possible ? If so how would you go about it ?

Probably not.

In any case, attributes are expected to be simple JSON data, not something like an entire DOM node. The document is just data, and shouldn’t hold any stateful stuff like this.

I get it. It makes sense. Thank you for that. I have changed my code so that I store the inner html as a string and the class as a string and the call stack size error is now gone.

But : ) The copy paste cut issue remains. I though it was caused by the call stack size error but not so. What I was trying to achieve is to select the node myself on mousedown. But I must be doing something silly here which is not so obvious to me. The result is that every second click on the div causes cut past operation not to work on the div.

Can you spot anything obviously wrong with the code ? The way I select the node …


        this.node = node
        this.view = view
        this.getPos = getPos

        this.dom = document.createElement('div');
        this.dom.innerHTML = node.attrs.html;
        if(node.attrs.className){
            this.dom.className = node.attrs.className;
        }

        this.dom.addEventListener("mousedown", event => {

            // select node if not yet selected //event.metaKey &&
            if(!this.dom.classList.contains("ProseMirror-selectednode")){
                const selection = NodeSelection.create(
                    this.view.state.doc,
                    this.getPos()
                );

                this.view.dispatch(this.view.state.tr.setSelection(selection));
                // event.stopPropagation();
                // event.preventDefault();
            }

            console.log('mousedown');
        });
    }

    selectNode() {
        this.dom.classList.add("ProseMirror-selectednode")
    }

    deselectNode() {
        this.dom.classList.remove("ProseMirror-selectednode")
    }

    update(node, decorations) {
        console.log('update --- DOMinatorCustomHtmlView');
    }

    stopEvent(event) {
        const blacklisted = [
            'dragstart',
            'dragenter',
            'dragover',
            'dragend',
            'drop',
            'mousedown',
        ];

        if( blacklisted.indexOf(event.type) > -1 ){
            return true;
        }

        console.log(event.type);
        return false;
        // Can be used to prevent the editor view from trying to handle some or all DOM events that bubble up from the node view.
        // Events for which this returns true are not handled by the editor.
    }

The full code is here.

This error is almost always means you have a problem with recursion in JavaScript code, as there isn’t any other way in JavaScript to consume lots of stack. Sometimes calling a recursive function over and over again, causes the browser to send you Maximum call stack size exceeded error message as the memory that can be allocated for your use is not unlimited.

How to fix it?

Wrap your recursive function call into a -

  • setTimeout
  • setImmediate
  • process.nextTick

Also, you can localize the issue by setting a breakpoint on RangeError type of exception , and then adjust the code appropriately.