欢迎来到尧图网

客户服务 关于我们

您的位置:首页 > 文旅 > 文化 > 【vue】 如何在划词选择的文本上添加右键菜单

【vue】 如何在划词选择的文本上添加右键菜单

2025/6/13 16:21:16 来源:https://blog.csdn.net/m0_64455070/article/details/148555265  浏览:    关键词:【vue】 如何在划词选择的文本上添加右键菜单

基础实现步骤

1. 创建右键菜单组件

首先创建一个独立的右键菜单组件 ContextMenu.vue

<template><div v-show="visible"class="context-menu":style="{ left: `${position.x}px`, top: `${position.y}px` }"@click.stop><div class="menu-item" @click="handleCopy"><span class="icon">📋</span> 复制</div><div class="menu-item" @click="handleHighlight"><span class="icon">🖍️</span> 高亮</div><div class="menu-item" @click="handleSearch"><span class="icon">🔍</span> 搜索</div><div class="menu-divider"></div><div class="menu-item" @click="handleShare"><span class="icon">↗️</span> 分享</div></div>
</template><script>
export default {data() {return {visible: false,position: { x: 0, y: 0 },selectedText: '',selectedRange: null}},methods: {showMenu(e, selectedText, range) {this.selectedText = selectedTextthis.selectedRange = rangethis.position = { x: e.clientX, y: e.clientY }this.visible = true// 阻止默认右键菜单e.preventDefault()},hideMenu() {this.visible = false},handleCopy() {navigator.clipboard.writeText(this.selectedText).then(() => {this.$toast.success('已复制到剪贴板')}).catch(err => {console.error('复制失败:', err)})this.hideMenu()},handleHighlight() {if (this.selectedRange) {const span = document.createElement('span')span.className = 'text-highlight'span.style.backgroundColor = 'rgba(255, 255, 0, 0.5)'this.selectedRange.surroundContents(span)}this.hideMenu()},handleSearch() {const searchUrl = `https://www.google.com/search?q=${encodeURIComponent(this.selectedText)}`window.open(searchUrl, '_blank')this.hideMenu()},handleShare() {if (navigator.share) {navigator.share({title: '分享选中文本',text: this.selectedText}).catch(err => {console.log('分享取消:', err)})} else {this.handleCopy()}this.hideMenu()}},mounted() {document.addEventListener('click', this.hideMenu)document.addEventListener('contextmenu', this.hideMenu)},beforeDestroy() {document.removeEventListener('click', this.hideMenu)document.removeEventListener('contextmenu', this.hideMenu)}
}
</script><style scoped>
.context-menu {position: fixed;background: white;border: 1px solid #e1e1e1;border-radius: 4px;box-shadow: 0 2px 10px rgba(0, 0, 0, 0.15);z-index: 9999;min-width: 180px;padding: 5px 0;font-size: 14px;
}.menu-item {padding: 8px 15px;cursor: pointer;display: flex;align-items: center;
}.menu-item:hover {background-color: #f5f5f5;
}.menu-item .icon {margin-right: 8px;font-size: 16px;
}.menu-divider {height: 1px;background-color: #e1e1e1;margin: 5px 0;
}.text-highlight {padding: 0 2px;border-radius: 2px;
}
</style>

2. 在父组件中使用

<template><div class="text-container"><!-- 注册右键菜单组件 --><context-menu ref="contextMenu" /><!-- 可选中文本的内容区域 --><div class="selectable-content"@mouseup="handleMouseUp"@contextmenu="handleContextMenu"><h2>示例文本内容</h2><p>这里是可选择的文本段落。你可以用鼠标划选任意文本,然后右键点击会出现自定义菜单。</p><p>菜单提供了复制、高亮、搜索和分享等功能,增强了用户体验。</p><p>Vue.js 是一个渐进式 JavaScript 框架,用于构建用户界面。</p></div></div>
</template><script>
import ContextMenu from './ContextMenu.vue'export default {components: { ContextMenu },methods: {handleMouseUp() {// 延迟处理以确保选中文本已经更新setTimeout(() => {const selection = window.getSelection()const selectedText = selection.toString().trim()if (selectedText.length > 0) {// 存储当前选中范围this.currentSelection = selectionthis.currentRange = selection.getRangeAt(0)}}, 10)},handleContextMenu(e) {const selection = window.getSelection()const selectedText = selection.toString().trim()if (selectedText.length > 0) {// 显示自定义菜单this.$refs.contextMenu.showMenu(e, selectedText,selection.getRangeAt(0)}}}
}
</script><style>
.selectable-content {user-select: text;line-height: 1.6;padding: 20px;max-width: 800px;margin: 0 auto;
}.selectable-content p {margin-bottom: 15px;
}.text-highlight {background-color: rgba(255, 255, 0, 0.5);padding: 0 2px;border-radius: 2px;
}
</style>

高级功能扩展

1. 添加动画效果

在右键菜单组件中添加过渡动画:

<template><transitionname="menu"@before-enter="beforeEnter"@enter="enter"@leave="leave"><div v-show="visible"class="context-menu":style="{ left: `${position.x}px`, top: `${position.y}px` }"@click.stop><!-- 菜单内容 --></div></transition>
</template><script>
export default {methods: {beforeEnter(el) {el.style.opacity = 0el.style.transform = 'scale(0.95) translateY(-5px)'},enter(el, done) {const duration = 150const startTime = Date.now()const animate = () => {const elapsed = Date.now() - startTimeconst progress = Math.min(elapsed / duration, 1)el.style.opacity = progressel.style.transform = `scale(${0.95 + 0.05 * progress}) translateY(${-5 + 5 * progress}px)`if (progress < 1) {requestAnimationFrame(animate)} else {done()}}requestAnimationFrame(animate)},leave(el, done) {const duration = 100const startTime = Date.now()const animate = () => {const elapsed = Date.now() - startTimeconst progress = Math.min(elapsed / duration, 1)el.style.opacity = 1 - progressel.style.transform = `scale(${1 - 0.05 * progress}) translateY(${5 * progress}px)`if (progress < 1) {requestAnimationFrame(animate)} else {done()}}requestAnimationFrame(animate)}}
}
</script>

2. 支持动态菜单项

<script>
// 在ContextMenu组件中
export default {props: {menuItems: {type: Array,default: () => [{id: 'copy',label: '复制',icon: '📋',action: 'handleCopy',disabled: false},{id: 'highlight',label: '高亮',icon: '🖍️',action: 'handleHighlight'},{id: 'search',label: '搜索',icon: '🔍',action: 'handleSearch'},{type: 'divider'},{id: 'share',label: '分享',icon: '↗️',action: 'handleShare',disabled: !navigator.share}]}},computed: {processedMenuItems() {return this.menuItems.filter(item => {// 过滤掉禁用项return !item.disabled})}}
}
</script><template><!-- 动态渲染菜单项 --><div v-for="(item, index) in processedMenuItems" :key="index"><div v-if="item.type === 'divider'"class="menu-divider"></div><divv-elseclass="menu-item"@click="executeAction(item)"><span class="icon">{{ item.icon }}</span> {{ item.label }}</div></div>
</template>

3. 创建自定义指令

为了更方便地在多个地方使用,可以创建一个自定义指令:

// directives/selectionContextMenu.js
export default {bind(el, binding, vnode) {const contextMenu = vnode.context.$refs[binding.arg]let currentRange = nullel.addEventListener('mouseup', () => {setTimeout(() => {const selection = window.getSelection()const selectedText = selection.toString().trim()if (selectedText.length > 0) {currentRange = selection.getRangeAt(0)}}, 10)})el.addEventListener('contextmenu', (e) => {const selection = window.getSelection()const selectedText = selection.toString().trim()if (selectedText.length > 0 && contextMenu) {contextMenu.showMenu(e, selectedText, currentRange)e.preventDefault()}})}
}// 在main.js中注册
import SelectionContextMenu from './directives/selectionContextMenu'
Vue.directive('selection-menu', SelectionContextMenu)// 使用方式
<div v-selection-menu:contextMenu><!-- 可选中文本 -->
</div>

注意事项和最佳实践

  1. 浏览器兼容性

    • window.getSelection()Range API 在现代浏览器中广泛支持
    • clipboard.writeText() 需要HTTPS环境或localhost
    • navigator.share() 只在部分移动浏览器中可用
  2. 性能优化

    • 避免在大型文档上频繁操作DOM
    • 使用事件委托如果有多个可选中区域
    • 考虑节流鼠标事件处理
  3. 无障碍访问

    • 为菜单添加适当的ARIA属性
    • 确保可以通过键盘操作
    • 提供视觉反馈
  4. 与UI框架集成

    • 可以包装Element UI/Vuetify等的菜单组件
    • 保持一致的视觉风格

各位大佬,点点赞呗

版权声明:

本网仅为发布的内容提供存储空间,不对发表、转载的内容提供任何形式的保证。凡本网注明“来源:XXX网络”的作品,均转载自其它媒体,著作权归作者所有,商业转载请联系作者获得授权,非商业转载请注明出处。

我们尊重并感谢每一位作者,均已注明文章来源和作者。如因作品内容、版权或其它问题,请及时与我们联系,联系邮箱:809451989@qq.com,投稿邮箱:809451989@qq.com

热搜词