基于原生JavaScript实现Tiptap提及与建议功能求助
原生JavaScript实现Tiptap提及与表情建议功能示例
1. 安装依赖
首先安装所需的Tiptap包:
npm install @tiptap/core @tiptap/extension-mention @tiptap/suggestion
2. 完整示例代码
创建一个HTML文件,直接运行即可看到效果:
<!DOCTYPE html> <html lang="zh-CN"> <head> <meta charset="UTF-8"> <title>Tiptap Vanilla JS Mentions & Emojis</title> <style> /* 编辑器基础样式 */ .tiptap { border: 1px solid #ddd; padding: 10px; min-height: 150px; border-radius: 4px; margin-bottom: 10px; } /* 建议列表样式 */ .suggestions { position: absolute; background: white; border: 1px solid #eee; border-radius: 4px; box-shadow: 0 2px 8px rgba(0,0,0,0.1); max-height: 200px; overflow-y: auto; z-index: 100; display: none; } .suggestions-item { padding: 8px 12px; cursor: pointer; margin: 0; font-size: 14px; } .suggestions-item:hover { background: #f5f5f5; } /* 提及内容样式 */ .mention { background: #e8f4fd; padding: 2px 4px; border-radius: 3px; text-decoration: none; color: #1a73e8; } </style> </head> <body> <div id="editor"></div> <div id="suggestions" class="suggestions"></div> <script type="module"> import { Editor } from '@tiptap/core' import Mention from '@tiptap/extension-mention' import { suggestion } from '@tiptap/suggestion' // 模拟数据源:用户列表和表情列表 const userList = [ { id: 1, name: '张三', username: 'zhangsan' }, { id: 2, name: '李四', username: 'lisi' }, { id: 3, name: '王五', username: 'wangwu' } ] const emojiList = [ { id: 'smile', label: '😊', name: '微笑' }, { id: 'laugh', label: '😂', name: '大笑' }, { id: 'love', label: '❤️', name: '爱心' }, { id: 'cool', label: '😎', name: '酷' } ] // 生成建议组件配置 const buildSuggestionConfig = (editor) => { return { // 触发字符:@ 用于提人,: 用于表情 char: ['@', ':'], // 过滤匹配项 filter: (items, query) => { return items.filter(item => { const searchTarget = item.name || item.label return searchTarget.toLowerCase().includes(query.toLowerCase()) }) }, // 根据触发字符返回对应数据源 items: ({ query, char }) => { return char === '@' ? userList : emojiList }, // 渲染建议列表 render: () => { const container = document.getElementById('suggestions') return { onUpdate(props) { container.innerHTML = '' if (props.items.length === 0) { container.style.display = 'none' return } container.style.display = 'block' // 定位到光标下方 const { top, left, height } = editor.view.coordsAtPos(props.range.from) container.style.top = `${top + height + 5}px` container.style.left = `${left}px` // 生成列表项 props.items.forEach(item => { const itemEl = document.createElement('div') itemEl.className = 'suggestions-item' itemEl.textContent = props.char === '@' ? `@${item.name}` : `${item.label} ${item.name}` itemEl.addEventListener('click', () => props.command({ id: item.id, label: props.char === '@' ? item.name : item.label })) container.appendChild(itemEl) }) }, onHide() { container.style.display = 'none' } } }, // 选中建议项后的插入逻辑 onSelect: ({ editor, range, props }) => { editor .chain() .focus() .deleteRange(range) .insertContentAt(range.from, { type: 'mention', attrs: { id: props.id, label: props.label, type: props.char === '@' ? 'user' : 'emoji' } }) .insertContent(' ') // 添加空格,方便后续输入 .run() } } } // 初始化编辑器 const editor = new Editor({ element: document.getElementById('editor'), extensions: [ Mention.configure({ HTMLAttributes: { class: 'mention' }, suggestion: suggestion(buildSuggestionConfig) }) ], content: '<p>试试输入 @ 选择用户,输入 : 选择表情</p>' }) </script> </body> </html>
核心逻辑说明
- 利用
Mention扩展结合@tiptap/suggestion实现多触发字符的建议功能 - 通过
char配置项指定触发符号,根据符号切换数据源(用户/表情) - 直接操作DOM渲染建议列表,绑定点击事件触发编辑器内容插入
- 选中建议后通过Tiptap的链式API替换光标区域内容为提及节点
内容的提问来源于stack exchange,提问作者jyms
相关产品推荐
相关产品推荐

