鼠标右击网页会弹出默认的浏览器菜单,但是很多时候我们需要自定义右键菜单(比如:在线文档编辑器、定制视频播放器等)。今天我们就来快速实现一个自定义右键菜单。
预览:
contextmenu
事件监听
首先,我们需要禁用浏览器弹出默认菜单的行为,通过阻止 contextMenu 事件的默认行为,并同时触发自定义菜单的显示:
document.addEventListener("contextmenu", (e) => {e.preventDefault();
showMenu(e);
});
构造菜单
1. 实现单例
一个页面中菜单应该只有一个实例,所以我们运用单例模式去创建菜单,典型的单例构造器可以实现为:
const ContextMenu = function (options) {
// 唯一实例
let instance;
// 创建实例方法
function createMenu() {// todo}
return {
// 获取实例的唯一方式
getInstance: function () {if (!instance) {instance = createMenu();
}
return instance;
},
};
};
2. 创建菜单实例
即实现上面的 createMenu
方法。
菜单的具体配置通过 options
传入,options
的结构定义为:
options: {
menus: [{
name: string, // 菜单名称
onClick: Function // 菜单点击回调
}]
}
通过遍历 options.menus
生成菜单列表,并挂载到 body
中,并最终返回菜单的实例:
function createMenu() {const ul = document.createElement("ul");
ul.classList.add("custom-context-menu");
const {menus} = options;
if (menus && menus.length > 0) {for (let menu of menus) {const li = document.createElement("li");
li.textContent = menu.name;
li.onclick = menu.onClick;
ul.appendChild(li);
}
}
const body = document.querySelector("body");
body.appendChild(ul);
return ul;
}
创建菜单的主要逻辑就完成了。
3. 初始化菜单
接下来向 ContextMenu
中传入 options
以初始化单例构造器:
const menuSinglton = ContextMenu({
menus: [
{
name: "custom menu 1",
onClick: function (e) {console.log("menu1 clicked");
},
},
{
name: "custom menu 2",
onClick: function (e) {console.log("menu2 clicked");
},
},
{
name: "custom menu 3",
onClick: function (e) {console.log("menu3 clicked");
},
},
],
});
初始化完成后,便可以通过 menuSinglton.getInstance()
获取菜单实例了。
显示菜单
当我们右击页面时,获取到鼠标的坐标,设置菜单为固定定位(position: fixed
),并将其左上角位置设置为鼠标坐标,以实现菜单在鼠标点击位置的弹出:
function showMenu(e) {const menus = menuSinglton.getInstance();
menus.style.top = `${e.clientY}px`;
menus.style.left = `${e.clientX}px`;
menus.style.display = "block";
}
隐藏菜单
最后,当我们点击页面中的其他区域时需要将菜单隐藏:
function hideMenu(e) {const menus = menuSinglton.getInstance();
menus.style.display = "none";
}
document.addEventListener("click", hideMenu);
大功告成!!
本文 Demo 参考:Codepen Trick by Day (2020-07-05) Custom Context Menus
每天一个小技巧,量变引起质变,希望你和我一起每天多学一点,让技术有趣一点。
所有示例将会汇总到我的 tricks-by-day github 项目中,欢迎大家莅临指导 ????