javaScript
使用像 Vue 或 React 这样的框架可能会感觉太复杂了,或者在纯 js 项目中使用,请好好阅读本节。
渲染节点视图
import { Node } from '@tiptap/core'
import Component from './Component.vue'
export default Node.create({
// configuration …
addNodeView() {
return ({ editor, node, getPos, HTMLAttributes, decorations, extension }) => {
const dom = document.createElement('div')
dom.innerHTML = 'Hello, 我是node view!'
return {dom}
}
},
})
步骤如下:
- 创建节点扩展,节点添加addNodeView(),并编写渲染逻辑
- 配置 Tiptap 以使用您的新节点扩展
下边有个更具体的例子(例子中的节点视图甚至与编辑器存在交互)。
🌰 举个例子

- index.js
- extension.js
import React from "react";
import DemoNode from './extension';
import StarterKit from "@tiptap/starter-kit";
import { EditorContent, useEditor } from "@tiptap/react";
export default () => {
const editor = useEditor({
extensions: [StarterKit, DemoNode],
content: "下边是个例子:<node-view/>",
});
return <EditorContent editor={editor} />;
};
import { mergeAttributes, Node } from '@tiptap/core'
export default Node.create({
name: 'nodeView',
group: 'block',
atom: true,
addAttributes() {
return { count: { default: 0 } }
},
parseHTML() {
return [{ tag: 'node-view' }]
},
renderHTML({ HTMLAttributes }) {
return ['node-view', mergeAttributes(HTMLAttributes)]
},
addNodeView() {
return ({ editor, node, getPos }) => {
const { view } = editor
const label = document.createElement('span')
label.classList.add('label')
label.innerHTML = '节点视图'
const button = document.createElement('button')
button.innerHTML = `按钮被点击了${node.attrs.count} 次`
button.addEventListener('click', () => {
if (typeof getPos === 'function') {
view.dispatch(view.state.tr.setNodeMarkup(getPos(), undefined, {
count: node.attrs.count + 1,
}))
editor.commands.focus()
}
})
const content = document.createElement('div')
content.classList.add('content')
content.append(button)
const dom = document.createElement('div')
dom.classList.add('node-view')
dom.append(label, content)
return { dom }
}
},
})
访问节点属性
渲染函数可能会用到编辑器定义的一些变量,该如何访问呢?
addNodeView() {
return ({ node }) => {
console.log(node.attrs.count)
// …
}
}
更新节点属性
有时候会有这种节点视图更新节点属性需求,如下便是做法
addNodeView() {
return ({ editor, node, getPos }) => {
const { view } = editor
// 创建一个按钮 …
const button = document.createElement('button')
button.innerHTML = `This button has been clicked ${node.attrs.count} times.`
// … 当按钮被点击的时候 …
button.addEventListener('click', () => {
if (typeof getPos === 'function') {
// … 触发一个更新事件 …
view.dispatch(view.state.tr.setNodeMarkup(getPos(), undefined, {
count: node.attrs.count + 1,
}))
// … 焦点收回编辑器.
editor.commands.focus()
}
})
// …
}
}
是不是有点太复杂了?考虑使用React或Vue,事情就容易多了。
节点视图的内容可编辑
上边的例子,我们的节点视图都是默认不可编辑的。
来个可以编辑的视图节点的例子:只要您为节点视图返回一个容器,为内容(contentDOM)返回另一个容器。
🌰 举个例子

- index.js
- extension.js
import React from "react";
import DemoNode from './extension';
import StarterKit from "@tiptap/starter-kit";
import { EditorContent, useEditor } from "@tiptap/react";
export default () => {
const editor = useEditor({
extensions: [StarterKit, DemoNode],
content: "下边是个例子:<node-view/>",
});
return <EditorContent editor={editor} />;
};
import { mergeAttributes, Node } from '@tiptap/core'
export default Node.create({
name: 'nodeView',
group: 'block',
content: 'inline*',
parseHTML() {
return [
{
tag: 'node-view',
},
]
},
renderHTML({ HTMLAttributes }) {
return ['node-view', mergeAttributes(HTMLAttributes), 0]
},
addNodeView() {
return () => {
const label = document.createElement('span')
label.classList.add('label')
label.innerHTML = '节点视图'
label.contentEditable = false
const content = document.createElement('div')
content.classList.add('content')
const dom = document.createElement('div')
dom.classList.add('node-view')
dom.append(label, content)
return {
dom,
contentDOM: content,
}
}
},
})
需要注意的是,此内容由 Tiptap 呈现。这意味着你需要告诉允许什么样的内容,例如content: 'inline*'