ProseMirror自定义Inline节点光标选中停止问题求助
自定义ProseMirror Inline节点光标选中异常问题解决
问题背景
基于ProseMirror定义了SignatureNode自定义Inline节点,代码如下:
export default Node.create({ name: "SignatureNode", inline: true, group: "inline", atom: false, addAttributes() { return { content: { default: "", parseHTML: (el) => el.getAttribute("content") ?? el.getAttribute("data-content"), }, signId: { default: "", parseHTML: (el) => el.getAttribute("signId") ?? el.getAttribute("data-signId"), }, }; }, parseHTML() { return [ { tag: "signature-node", }, { tag: "div", getAttrs: (node) => (node as HTMLElement).classList.contains("signature-node") && null, }, ]; }, addCommands() { return { addSign: (options) => ({ chain, state }) => { // ... }, }; }, renderHTML({ HTMLAttributes }) { const div = document.createElement("div"); // Create app for getting article component html const app = createApp(SignatureNode, { content: HTMLAttributes.content, signId: HTMLAttributes.signId, }); const mounted = app.mount(div); const html = mounted.$el; // Unmount app for performance app.unmount(); return html; }, addNodeView() { return VueNodeViewRenderer(SignatureNodeWrapper); }, });
拖动光标选中内容时,光标会在该自定义节点的起始位置停止(仅从外部移动光标到节点内操作正常)。尝试给NodeViewWrapper添加contenteditable="true"后,光标会直接跳转到节点末尾,仍无法选中节点内元素。
解决方案
1. 调整NodeView结构与属性
修改SignatureNodeWrapper的DOM结构,确保外层容器保持inline特性,内部内容容器禁用原生编辑:
<NodeViewWrapper class="signature-node-wrapper" style="display: inline;"> <div class="signature-content" contenteditable="false"> <SignatureNode :content="node.attrs.content" :sign-id="node.attrs.signId" :update-attributes="updateAttributes" /> </div> </NodeViewWrapper>
2. 实现NodeView的选中处理逻辑
在SignatureNodeWrapper组件中手动处理选中事件,让ProseMirror识别节点内的选中范围:
// SignatureNodeWrapper.vue export default { props: ['node', 'updateAttributes', 'view'], methods: { selectNode() { this.$el.classList.add('ProseMirror-selectednode'); }, deselectNode() { this.$el.classList.remove('ProseMirror-selectednode'); }, updateSelection() { const contentEl = this.$el.querySelector('.signature-content'); if (!contentEl) return; const range = document.createRange(); range.selectNodeContents(contentEl); const sel = window.getSelection(); sel.removeAllRanges(); sel.addRange(range); } }, mounted() { this.$el.addEventListener('mousedown', (e) => { e.preventDefault(); const contentEl = this.$el.querySelector('.signature-content'); const range = document.createRange(); range.selectNodeContents(contentEl); const sel = window.getSelection(); sel.removeAllRanges(); sel.addRange(range); }); } }
3. 优化节点的renderHTML方法
将渲染根元素改为span,保证节点处于inline文本流中,同时直接渲染内容让ProseMirror识别文本长度:
renderHTML({ HTMLAttributes }) { return [ 'span', { class: 'signature-node', ...HTMLAttributes }, HTMLAttributes.content ]; }
4. 补充CSS样式
添加基础样式确保节点表现与普通inline文本一致:
.signature-node-wrapper { display: inline; cursor: text; } .signature-content { display: inline; user-select: text; }
原理说明
- 通过自定义鼠标事件和
updateSelection方法,手动告诉浏览器选中节点内全部内容,让ProseMirror正确识别选中范围。 - 使用
span作为渲染根元素,避免block元素破坏编辑器的文本流,导致选中中断。 - 禁用内部元素的
contenteditable,防止原生编辑逻辑干扰ProseMirror的选中处理。
内容的提问来源于stack exchange,提问作者Mohib Arshi
相关产品推荐
相关产品推荐

