You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在React Tiptap编辑器中将单行转为标题而不改变整段

问题

在React项目中用最新版Tiptap编辑器时碰到个问题:把段落里的单行转成标题时,整段都会变成标题。操作流程是这样的:

  • 先有一段文本段落;
  • 选中段落里的某一行;
  • 尝试只把选中行改成<h1>这类标题,结果整个段落都变成了标题。

使用的扩展配置

Color.configure({ types: [TextStyle.name, ListItem.name] }),
TextStyle,
TextStyleExtended,
StarterKit.configure({
 bulletList: {
   keepMarks: true,
   keepAttributes: false, // TODO : 设为false是因为保留属性时标记无法被保存,还在等帮助
 },
 orderedList: {
   keepMarks: true,
   keepAttributes: false, // TODO : 设为false是因为保留属性时标记无法被保存,还在等帮助
 },
 // heading: {
 //   levels: [1, 2, 3, 4, 5, 6],
 // },
 // paragraph: {},
}),
Image.configure({
 allowBase64: true,
}),
Link.configure({
 openOnClick: false,
 autolink: true,
}),
Code,
];

Editor组件代码

export default function Editor(props: Props) {
  const { content, setFieldValue, isEdit } = props;
  if (isEdit && content.length === 0) return <>...loading</>;
  return (
    <Box
      sx={{
        border: "1px solid #E5E5E5",
        borderRadius: "8px",
      }}
    >
      <EditorProvider
        slotBefore={<MenuBar />}
        extensions={extensions}
        content={content}
        onUpdate={({ editor }) => {
          setFieldValue("content", editor.getHTML());
        }}
      />
    </Box>
  );
}
解决方案

Tiptap默认的标题切换命令是针对整个块级元素(比如段落)生效的,所以才会把整段都转成标题。要实现只转选中的单行,得自定义命令来处理选区,拆分段落再转换选中部分:

  1. 写个自定义标题切换函数
    把原来触发标题转换的逻辑换成下面这个自定义函数:
const toggleSelectedLineToHeading = (editor, level) => {
  // 只在当前是段落时处理
  if (!editor.isActive('paragraph')) return;

  const { from, to } = editor.state.selection;
  // 没选中内容就不处理
  if (from === to) return;

  editor.chain()
    // 把选中的内容从原段落拆出来,变成独立块
    .splitBlock()
    // 给这个独立块设置标题级别
    .toggleHeading({ level })
    // 合并可能产生的空块(可选,优化编辑体验)
    .joinBackward()
    .joinForward()
    .run();
};
  1. 在菜单栏按钮里用这个自定义函数
    如果你的MenuBar是自己写的,把原来调用editor.commands.toggleHeading({ level: 1 })的地方换成调用上面的函数:
// 举个H1按钮的例子
<button
  onClick={() => toggleSelectedLineToHeading(editor, 1)}
  className={editor.isActive('heading', { level: 1 }) ? 'active' : ''}
>
  H1
</button>
  1. 别忘了启用Heading扩展
    把你扩展配置里注释掉的Heading配置打开:
StarterKit.configure({
  // ...其他原有配置
  heading: {
    levels: [1, 2, 3, 4, 5, 6],
  },
}),

核心原理就是用splitBlock把选中的单行从原段落拆成独立块,再给这个块应用标题格式,这样就只会改选中的那一行,不会影响整个段落了。

内容的提问来源于stack exchange,提问作者Amine Ladrani

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.21 14:32:13