I have a plugin that enables custom nodes. These nodes do not have explicit NodeViews and rely on the toDOM
function in their spec. The plugin constructor sets up a click handler with
editorView.setProps({
handleClickOn: this.handleNodeClick,
});
and handleNodeClick
triggers a modal which allows the user to edit the underlying value of the node. I see the currentTarget
of the event passed to the handler is the document element, so ProseMirror is delegating events to <html>
. OK, that seems fine. But why when I run $(customNodeEl).click()
does it not trigger the handler? I dug through the prosemirror-view source in search of the answer, which seems to have something to do with click events not being bound directly to click but rather mousedown + some gateway logic + mouseup. The reason I’d like to trigger it programmatically is to automatically open the modal when the custom node is first inserted into the DOM. I saw Callback when NodeView is inserted into DOM and think I could achieve the same inside the toDOM
with a requestAnimationFrame
, but the synthetic click would have to actually fire the callback.
Update: I got it working, though this does feel like quite a hack.
This gets called when the user causes a new custom node to enter the document:
const createCustomNode = (state, attrs, nodeType, marks) => {
const mergedAttrs = { ...attrs, shouldOpenModal: true };
return state.schema.node(nodeType, mergedAttrs, null, marks);
};
And I updated the nodeSpec to expect it
const nodeSpec = {
[...]
attrs: {
[...]
// Ephemeral property, not stored in DOM
shouldOpenModal: { default: false },
},
toDOM: node => createNodeElement(node.attrs),
};
And used the passed value to simulate a “click”…
const createNodeElement = ({
[...]
shouldOpenModal,
}) => {
const wrapper = document.createElement('span');
[...]
if (shouldOpenModal) {
triggerModalAfterInsertion(wrapper);
}
return wrapper;
};
…which happens once the new node is in the DOM.
const triggerModalAfterInsertion = el => {
requestAnimationFrame(function() {
const rect = el.getBoundingClientRect();
const eventCoords = { clientX: rect.x, clientY: rect.y, bubbles: true };
const mousedown = new MouseEvent('mousedown', eventCoords);
const mouseup = new MouseEvent('mouseup', eventCoords);
el.dispatchEvent(mousedown);
el.dispatchEvent(mouseup);
});
};