如何在Tiptap中将Node转换为Mark并支持前后输入文本
将Tiptap自定义Block Node转换为行内Mark的实现方案
要实现将原有的块级自定义Node转为可在行内前后输入文本的Mark,不需要大幅修改现有代码,只需要调整核心定义部分并适配Mark的特性,以下是具体实现:
修改后的完整代码
import { NodeViewWrapper } from "@tiptap/react"; import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; import React from "react"; import { mergeAttributes, Mark } from "@tiptap/core"; import { ReactNodeViewRenderer } from "@tiptap/react"; import { faBook } from "@fortawesome/free-solid-svg-icons"; interface MyComponentProps { node: { attrs: { desc: string; url: string; }; }; updateAttributes: (attrs: Record<string, any>) => void; // Mark的NodeView需支持渲染包裹的内容 content: React.ReactNode; } const StudyLinkMark: React.FC<MyComponentProps> = (props) => { const { desc, url } = props.node.attrs; return ( // 改为行内布局容器,确保可与文本同行显示 <NodeViewWrapper className="studylink-mark" style={{ display: "inline-flex", alignItems: "center", gap: "4px", margin: "0 4px" }}> <FontAwesomeIcon icon={faBook} /> <a href={url} target="_blank" rel="noopener noreferrer"> {desc} </a> {/* 若需要Mark包裹文本内容,取消注释下方代码 */} {/* {props.content} */} </NodeViewWrapper> ); }; export const studyLinkMark = Mark.create({ name: "studyLinkMark", // 指定为行内元素组,支持在行内文本流中存在 group: "inline", // 允许Mark包裹任意行内内容(无需包裹文本可设为"") content: "inline*", // 开启选中、拖拽特性 selectable: true, draggable: true, addAttributes() { return { desc: { default: "Click here for more details", }, url: { default: "https://example.com", }, }; }, parseHTML() { return [ { tag: "span.studylink-mark", getAttrs: (node) => { const el = node as HTMLElement; const linkEl = el.querySelector("a"); return { desc: el.getAttribute("data-desc") || linkEl?.textContent, url: linkEl?.getAttribute("href") }; }, }, ]; }, renderHTML({ HTMLAttributes }) { return [ "span", mergeAttributes(HTMLAttributes, { class: "studylink-mark", "data-desc": this.attrs.desc }), ["i", { class: "fa-solid fa-book" }], ["a", { href: this.attrs.url, target: "_blank", rel: "noopener noreferrer" }, this.attrs.desc] ]; }, addNodeView() { return ReactNodeViewRenderer(StudyLinkMark); }, // 添加命令用于插入/切换Mark addCommands() { return { setStudyLinkMark: (attrs) => ({ commands }) => { return commands.setMark(this.name, attrs); }, toggleStudyLinkMark: (attrs) => ({ commands }) => { return commands.toggleMark(this.name, attrs); }, }; }, });
关键修改点说明
- 核心类型切换:将
Node.create改为Mark.create,从块级节点转为行内标记类型 - 布局与分组调整:
group: "inline":指定Mark属于行内元素组,支持嵌入文本流- 容器样式改为
inline-flex,确保与前后文本同行显示,实现可在两侧输入内容的效果
- HTML解析与渲染适配:
- 改用
span作为渲染根元素(行内元素),替代原块级自定义标签 - 调整
parseHTML逻辑,从行内元素中提取属性信息
- 改用
- 命令补充:新增
setStudyLinkMark和toggleStudyLinkMark命令,方便编辑器中调用插入或切换该Mark
使用建议
- 在编辑器配置中注册该Mark:
extensions: [studyLinkMark, ...] - 调用命令插入Mark:
editor.commands.setStudyLinkMark({ desc: "学习资料", url: "https://your-url.com" }) - 通过CSS自定义
.studylink-mark的样式,确保与行内文本对齐协调
内容的提问来源于stack exchange,提问作者Kyrian marchand
相关产品推荐
相关产品推荐

