Vue.js - 富文本编辑器Tiptap使用详解9(气泡菜单)
作者:hangge | 2026-02-24 12:35
Tiptap 的 BubbleMenu(气泡菜单)是在选中文本时出现的上下文工具栏,体验接近 Word/Notion 的文本选中工具。下面我将通过样例演示如何实现气泡菜单。
(2)运行程序,当我们选中文本时,其会附近自动出现气泡菜单。点击气泡菜单上的按钮即可执行相应的命令。
九、气泡菜单实现
1,准备工作
(1)首先我们项目总需要安装 Tiptap 基础依赖,具体安装方法见之前的文章:(2)为了让气泡菜单更加美观,我们还需要安装 FontAwesome、ElementUI、Tailwind 依赖,并在 main.js 中引入。
import Vue from 'vue' import ElementUI from 'element-ui' import 'element-ui/lib/theme-chalk/index.css' import '@fortawesome/fontawesome-free/css/all.css' Vue.use(ElementUI)
2,实现步骤
(1)下面是气泡菜单的设置与使用代码,说明如下:
- Editor:由 @tiptap/core 创建,注册需要的扩展(示例使用 StarterKit + Link 等)。
- BubbleMenu:从 @tiptap/vue-2/menus 导入的 Vue 组件(它会在内部注册气泡菜单插件并使用 tippy.js 做定位)。BubbleMenu 接收两个重要 prop:
- editor:Tiptap 的 Editor 实例(必需)。
- tippy-options:传给 tippy.js 的配置(动画、placement、offset、delay 等)。
- BubbleMenu 的插槽内容就是气泡里的 HTML(一组按钮),我们在里面直接用 editor API(例如 editor.chain().focus().toggleBold().run())。
- BubbleMenu 默认显示条件:当选区非空(有文字被选中)时会自动显示;当选区为空或光标在某些节点时默认不显示(可自定义)。
- editor.isActive('bold') 等方法用于判断按钮是否处于激活态,从而给出视觉状态反馈。
<template>
<div class="editor-wrapper p-4">
<!-- 气泡菜单:只在 editor 已初始化时渲染 -->
<BubbleMenu v-if="editor" :editor="editor" :tippy-options="tippyOptions" class="bubble-menu">
<button
type="button"
class="bm-btn"
:class="{ active: editor.isActive('bold') }"
@click="toggleBold"
title="加粗 (Ctrl/Cmd+B)"
>
<i class="fas fa-bold"></i>
</button>
<button
type="button"
class="bm-btn"
:class="{ active: editor.isActive('italic') }"
@click="toggleItalic"
title="斜体 (Ctrl/Cmd+I)"
>
<i class="fas fa-italic"></i>
</button>
<button
type="button"
class="bm-btn"
@click="setLink"
title="插入 / 编辑 链接"
>
<i class="fas fa-link"></i>
</button>
<div class="bm-sep"></div>
<button type="button" class="bm-btn" @click="toggleHeading(1)"
:class="{ active: editor.isActive('heading', { level: 1 }) }" title="H1">H1</button>
<button type="button" class="bm-btn" @click="toggleHeading(2)"
:class="{ active: editor.isActive('heading', { level: 2 }) }" title="H2">H2</button>
<button type="button" class="bm-btn" @click="clearFormatting" title="清除格式">
<i class="fas fa-eraser"></i>
</button>
</BubbleMenu>
<!-- 编辑器主体 -->
<editor-content :editor="editor" class="editor-content" />
</div>
</template>
<script>
import { Editor } from '@tiptap/core'
import StarterKit from '@tiptap/starter-kit'
import { EditorContent } from '@tiptap/vue-2'
import { BubbleMenu } from '@tiptap/vue-2/menus'
import Link from '@tiptap/extension-link'
import Underline from '@tiptap/extension-underline'
import Strike from '@tiptap/extension-strike'
export default {
name: 'EditorWithBubbleMenu',
components: { EditorContent, BubbleMenu },
data() {
return {
editor: null,
// tippy options 可以传给 tippy.js 做定位/动画/延迟等配置
tippyOptions: {
duration: 120,
placement: 'top',
// offset: [0, 8],
// delay: [80, 0]
},
}
},
mounted() {
this.editor = new Editor({
extensions: [
StarterKit,
Link,
Underline,
Strike,
],
content: `<p>选中任意文字,气泡菜单会出现(示例:试试加粗 / 斜体 / 插入链接)。</p>`,
})
},
beforeDestroy() {
if (this.editor) this.editor.destroy()
},
methods: {
toggleBold() {
this.editor.chain().focus().toggleBold().run()
},
toggleItalic() {
this.editor.chain().focus().toggleItalic().run()
},
toggleHeading(level) {
this.editor.chain().focus().toggleHeading({ level }).run()
},
setLink() {
// 先检测是否已有链接,方便编辑
const previousUrl = this.editor.getAttributes('link').href || ''
const url = window.prompt('请输入链接 URL(以 http(s):// 开头)', previousUrl)
if (url === null) return // 取消
if (url === '') {
// 清除链接
this.editor.chain().focus().extendMarkRange('link').unsetLink().run()
return
}
this.editor.chain().focus().extendMarkRange('link').setLink({ href: url }).run()
},
clearFormatting() {
this.editor.chain().focus().clearNodes().unsetAllMarks().run()
},
},
}
</script>
<style scoped>
.editor-content {
min-height: 220px;
border: 1px solid #e6e6e6;
border-radius: 6px;
padding: 12px;
background: #fff;
}
/* 气泡菜单样式(可自定义)*/
.bubble-menu {
display: flex;
align-items: center;
gap: 6px;
padding: 6px;
background: #111827;
color: #fff;
border-radius: 6px;
box-shadow: 0 6px 18px rgba(0,0,0,0.18);
}
/* 按钮基础样式 */
.bm-btn {
background: transparent;
border: none;
color: inherit;
padding: 6px;
border-radius: 4px;
cursor: pointer;
}
.bm-btn:hover { background: rgba(255,255,255,0.06); }
/* 激活态 */
.bm-btn.active { background: rgba(255,255,255,0.14); }
/* 分隔符 */
.bm-sep {
width: 1px;
height: 20px;
background: rgba(255,255,255,0.08);
margin: 0 6px;
}
</style>
(2)运行程序,当我们选中文本时,其会附近自动出现气泡菜单。点击气泡菜单上的按钮即可执行相应的命令。

全部评论(0)