Статистика
Общее количество установок
1
Сегодня добавлено
+1
Рейтинг пользователей
-
Описание
Версии1
Оценки0
Разрешения
轻量级原生 JavaScript 右键上下文菜单组件库,支持子菜单、快捷键和动画效果
ContextMenu.js - 右键上下文菜单组件库
一个轻量级、可扩展的原生 JavaScript 右键上下文菜单组件库。
目录
特性
- 完全替换原生菜单 — 拦截浏览器原生右键事件,显示自定义菜单
- 零依赖 — 纯原生 JavaScript 实现,无需任何外部库
- 支持子菜单 — 可嵌套多级子菜单,自动处理边界溢出
- 快捷键支持 — 显示快捷键提示,支持键盘导航
- 丰富样式 — 现代暗色玻璃态设计,流畅动画
- 灵活配置 — 支持禁用、动态显示、回调函数等
- UMD 模块 — 支持 CommonJS、AMD 和全局变量
安装与引入
脚本猫
// ==UserScript==
// @name 我的脚本
// @require https://scriptcat.org/lib/xxxx/ContextMenu.js
// ==/UserScript==
(function () {
'use strict';
const menu = new ContextMenu();
menu.register({ id: 'copy', text: '复制' });
})();
在脚本猫环境中,
ContextMenu会自动挂载到全局window对象,直接new ContextMenu()即可使用。
直接引入
<script src="ContextMenu.js"></script>
<script>
const menu = new ContextMenu();
menu.register({ id: 'copy', text: '复制' });
</script>
Node.js
const ContextMenu = require('./ContextMenu.js');
// Node.js 环境无 DOM,仅可用于单元测试等非渲染场景
AMD
define(['ContextMenu'], function (ContextMenu) {
const menu = new ContextMenu();
return menu;
});
快速开始
// 创建菜单实例
const menu = new ContextMenu();
// 注册菜单项
menu.register({
id: 'copy',
text: '复制',
icon: '📋',
shortcut: 'Ctrl+C',
onClick: (info) => {
console.log('选中文本:', info.selectionText);
}
});
// 添加分隔线
menu.divider();
// 添加子菜单
menu.submenu('更多操作', (sub) => {
sub.register({ id: 'save', text: '保存' });
sub.register({ id: 'share', text: '分享' });
});
// 链式调用
menu
.register({ id: 'print', text: '打印' })
.divider()
.register({ id: 'exit', text: '退出' });
构造函数
const menu = new ContextMenu(options);
可选配置项:
| 选项 | 类型 | 默认值 | 描述 |
|---|---|---|---|
zIndex |
number | 2147483647 |
菜单的 z-index |
offsetX |
number | 0 |
X 轴偏移量 |
offsetY |
number | 2 |
Y 轴偏移量 |
menuClass |
string | 'cm-menu' |
菜单容器 CSS 类名 |
itemClass |
string | 'cm-item' |
菜单项 CSS 类名 |
hoverDelay |
number | 100 |
子菜单展开延迟(ms) |
fadeIn |
boolean | true |
是否启用淡入动画 |
animationDuration |
number | 150 |
动画时长(ms) |
API 文档
register(config)
注册一个新的菜单项,支持链式调用。
menu.register({
id: 'unique-id', // 唯一标识符
text: '菜单文本', // 显示文本
icon: '📌', // 图标(支持 emoji 或 HTML)
shortcut: 'Ctrl+S', // 快捷键提示
disabled: false, // 是否禁用
visible: true, // 是否可见(支持函数)
onClick: (info) => {} // 点击回调
});
config 参数:
| 属性 | 类型 | 必填 | 描述 |
|---|---|---|---|
id |
string | 是 | 菜单项唯一 ID |
text |
string | 是 | 显示文本 |
icon |
string | 否 | 图标(emoji 或 HTML) |
shortcut |
string | 否 | 快捷键提示文本 |
disabled |
boolean | 否 | 是否禁用该菜单项 |
visible |
boolean | Function | 否 | 是否显示,函数接收 MenuInfo 对象 |
onClick |
Function | 否 | 点击回调,接收 MenuInfo 对象 |
submenu |
Function | 否 | 子菜单配置回调 |
divider()
添加分隔线,支持链式调用。
menu.divider();
submenu(text, callback)
创建子菜单,支持嵌套。
menu.submenu('子菜单标题', (sub) => {
sub.register({ id: 'item1', text: '选项1' });
sub.register({ id: 'item2', text: '选项2' });
// 支持嵌套子菜单
sub.submenu('二级子菜单', (nested) => {
nested.register({ id: 'nested1', text: '嵌套选项1' });
});
});
| 参数 | 类型 | 描述 |
|---|---|---|
text |
string | 子菜单显示文本 |
callback |
Function | 子菜单配置回调,参数为子菜单实例 |
show(x, y)
手动在指定坐标显示菜单。
menu.show(100, 200);
hide()
隐藏菜单。
menu.hide();
clear()
清空所有菜单项。
menu.clear();
update(id, updates)
更新指定菜单项的属性。
menu.update('my-item', {
text: '新文本',
disabled: true
});
| 参数 | 类型 | 描述 |
|---|---|---|
id |
string | 菜单项 ID |
updates |
Object | 需更新的属性(支持 text、icon、disabled、visible、onClick) |
remove(id)
移除指定菜单项。
menu.remove('my-item');
getClipboardText()
获取剪贴板文本内容(异步)。
const text = await menu.getClipboardText();
destroy()
销毁菜单实例,移除所有事件监听和 DOM 元素。
menu.destroy();
MenuInfo 对象
回调函数中接收的 info 对象包含以下属性:
| 属性 | 类型 | 描述 |
|---|---|---|
x |
number | 鼠标点击 X 坐标 |
y |
number | 鼠标点击 Y 坐标 |
target |
Element | 触发右键点击的元素 |
selectionText |
string | 当前选中的文本 |
clipboardText |
string | 剪贴板内容 |
href |
string | 如果是链接元素,获取 href |
tagName |
string | 触发元素的标签名 |
className |
string | 触发元素的 className |
id |
string | 触发元素的 id |
{
x: number, // 鼠标点击X坐标
y: number, // 鼠标点击Y坐标
target: Element, // 触发右键点击的元素
selectionText: string, // 选中的文本
clipboardText: string, // 剪贴板内容
href: string, // 如果是链接元素,获取href
tagName: string, // 触发元素的标签名
className: string, // 触发元素的className
id: string // 触发元素的id
}
示例代码
基础用法
const menu = new ContextMenu();
menu.register({
id: 'copy',
text: '复制',
onClick: () => document.execCommand('copy')
});
menu.register({
id: 'paste',
text: '粘贴',
onClick: async () => {
const text = await menu.getClipboardText();
console.log(text);
}
});
menu.register({
id: 'cut',
text: '剪切',
onClick: () => document.execCommand('cut')
});
条件显示
// 只有选中文本时才显示
menu.register({
id: 'search',
text: '搜索',
visible: (info) => info.selectionText.length > 0,
onClick: (info) => {
window.open(`https://google.com/search?q=${info.selectionText}`);
}
});
// 只有右键点击链接时显示
menu.register({
id: 'open-link',
text: '打开链接',
visible: (info) => !!info.href,
onClick: (info) => {
window.open(info.href);
}
});
禁用菜单项
menu.register({
id: 'delete',
text: '删除',
disabled: true,
onClick: () => {}
});
// 动态启用
setTimeout(() => {
menu.update('delete', { disabled: false });
}, 3000);
键盘快捷键
| 按键 | 行为 |
|---|---|
Esc |
关闭菜单 |
↑ / ↓ |
上下导航 |
Enter |
执行选中项 |
→ |
打开子菜单 |
← |
返回上级菜单 |
浏览器兼容
| 环境 | 支持版本 |
|---|---|
| Chrome | 60+ |
| Firefox | 55+ |
| Safari | 11+ |
| Edge | 79+ |
| Node.js | 仅可用于非渲染场景(无 DOM) |
ContextMenu 依赖 DOM API,仅在有浏览器环境的场景中可正常渲染。脚本猫环境完全兼容。