自定义右键菜单

发布于 2025/08/12 建站手札 总字数:267 阅读时长:1 分钟
文章导读 摘要由 Qwen 生成

这篇文章介绍了如何在自定义右键菜单中添加各种功能,包括返回、前进、刷新、滚动到页面不同位置、复制文本、搜索页面、打开链接、粘贴内容、复制本文地址、在新窗口打开链接、保存图片、随机文章浏览、切换深色模式、繁简转换、阅读模式以及隐私声明、Cookie协议和版权声明等。同时,文章还提供了创建Pug文件和JavaScript文件的步骤,详细说明了如何编写右键菜单的HTML结构和交互逻辑。

ARTICLE SERIES

博客定制手记

第 3 / 14 篇
  1. 01 配置说说页面
  2. 02 在侧边栏中添加欢迎信息
  3. 03 自定义右键菜单
  4. 04 利用插件自定义页脚菜单
  5. 05 博客添加AI总结
  6. 06 利用 SiteMap 随机访问站内页面
  7. 07 自定义导航栏
  8. 08 自定义分类条
  9. 09 自定义页脚(新)
  10. 10 使用GitHub推送Hexo到服务器
  11. 11 在侧边栏添加日历和倒计时
  12. 12 自定义字体
  13. 13 添加typesense搜索
  14. 14 添加网站左上角菜单

注意:本文为早期 Hexo / Butterfly 魔改记录,本站现已迁移到 Astro,相关代码、路径或插件可能已经失效,仅作历史参考。

演示

本站右键即可查看,和原有菜单相比比较美观

新建pug文件

\themes\butterfly\layout\includes新建 rightmenu.pug

#rightMenu.js-pjax
    .rightMenu-group.rightMenu-small
        a.rightMenu-item(href="javascript:window.history.back();")
            i.fa.fa-arrow-left
        a.rightMenu-item(href="javascript:window.history.forward();")
            i.fa.fa-arrow-right
        a.rightMenu-item(href="javascript:window.location.reload();")
            i.fa.fa-refresh
        a.rightMenu-item(href="javascript:window.scrollTo(0, 0);")
            i.fa.fa-arrow-up
    .rightMenu-group.rightMenu-line.hide#menu-text
        a.rightMenu-item(href="javascript:rmf.copySelect();")
            i.fa.fa-copy
            span='复制'
        a.rightMenu-item(href="javascript:rmf.searchinThisPage();")
            i.fas.fa-search
            span='站内搜索'
    .rightMenu-group.rightMenu-line.hide#menu-too
        a.rightMenu-item(href="javascript:window.open(window.getSelection().toString());window.location.reload();")
            i.fa.fa-link
            span='转到链接'
    .rightMenu-group.rightMenu-line.hide#menu-paste
        a.rightMenu-item(href='javascript:rmf.paste()')
            i.fa.fa-copy
            span='粘贴'
    .rightMenu-group.rightMenu-line.hide#menu-post
        a.rightMenu-item(href="javascript:rmf.copyWordsLink()")
            i.fa.fa-link
            span='复制本文地址'
    .rightMenu-group.rightMenu-line.hide#menu-to
        a.rightMenu-item(href="javascript:rmf.openWithNewTab()")
            i.fa.fa-window-restore
            span='新窗口打开'
        a.rightMenu-item(href="javascript:rmf.open()")
            i.fa.fa-link
            span='转到链接'
        a.rightMenu-item(href="javascript:rmf.copyLink()")
            i.fa.fa-copy
            span='复制链接'
    .rightMenu-group.rightMenu-line.hide#menu-img
        a.rightMenu-item(href="javascript:rmf.saveAs()")
            i.fa.fa-download
            span='保存图片'
        a.rightMenu-item(href="javascript:rmf.openWithNewTab()")
            i.fa.fa-window-restore
            span='在新窗口打开'
        a.rightMenu-item(href="javascript:rmf.click()")
            i.fa.fa-arrows-alt
            span='全屏显示'
        a.rightMenu-item(href="javascript:rmf.copyLink()")
            i.fa.fa-copy
            span='复制图片链接'
    .rightMenu-group.rightMenu-line
        a.rightMenu-item(href="javascript:randomPost()")
            i.fa.fa-paper-plane
            span='随便逛逛'
        a.rightMenu-item(href="javascript:rmf.switchDarkMode();")
            i.fa.fa-moon
            span='昼夜切换'
        a.rightMenu-item(href="javascript:rmf.translate();")
            i.iconfont.icon-fanti
            span='繁简转换'
        if is_post()||is_page()
            a.rightMenu-item(href="javascript:rmf.switchReadMode();")
                i.fa.fa-book
                span='阅读模式'
        a.rightMenu-item(href="javascript:pjax.loadUrl(\"/privacy/\");")
            i.fa.fa-info-circle
            span='隐私声明'
        a.rightMenu-item(href="javascript:pjax.loadUrl(\"/cookie/\");")
            i.fa.fa-info-circle
            span='Cookie协议'
        a.rightMenu-item(href="javascript:pjax.loadUrl(\"/cc/\");")
            i.fa.fa-info-circle
            span='版权声明'

新建js文件

创建 \themes\butterfly\source\js\rightmenu.js

function setMask() {//设置遮罩层
    if (document.getElementsByClassName("rmMask")[0] !== undefined) {
        return document.getElementsByClassName("rmMask")[0];
    }
    mask = document.createElement('div');
    mask.className = "rmMask";
    mask.style.width = window.innerWidth + 'px';
    mask.style.height = window.innerHeight + 'px';
    mask.style.background = '#fff';
    mask.style.opacity = '.0';
    mask.style.position = 'fixed';
    mask.style.top = '0';
    mask.style.left = '0';
    mask.style.zIndex = 998;
    document.body.appendChild(mask);
    document.getElementById("rightMenu").style.zIndex = 19198;
    return mask;
}

function insertAtCursor(myField, myValue) {

    //IE 浏览器
    if (document.selection) {
        myField.focus();
        sel = document.selection.createRange();
        sel.text = myValue;
        sel.select();
    }

    //FireFox、Chrome等
    else if (myField.selectionStart || myField.selectionStart === '0') {
        var startPos = myField.selectionStart;
        var endPos = myField.selectionEnd;

        // 保存滚动条
        var restoreTop = myField.scrollTop;
        myField.value = myField.value.substring(0, startPos) + myValue + myField.value.substring(endPos, myField.value.length);

        if (restoreTop > 0) {
            myField.scrollTop = restoreTop;
        }

        myField.focus();
        myField.selectionStart = startPos + myValue.length;
        myField.selectionEnd = startPos + myValue.length;
    } else {
        myField.value += myValue;
        myField.focus();
    }
}

let rmf = {};
rmf.showRightMenu = function (isTrue, x = 0, y = 0) {
    let $rightMenu = $('#rightMenu');
    $rightMenu.css('top', x + 'px').css('left', y + 'px');

    if (isTrue) {
        $rightMenu.show();
    } else {
        $rightMenu.hide();
    }
}
rmf.switchDarkMode = function () {
    const nowMode = document.documentElement.getAttribute('data-theme') === 'dark' ? 'dark' : 'light'
    if (nowMode === 'light') {
        activateDarkMode()
        saveToLocal.set('theme', 'dark', 2)
        GLOBAL_CONFIG.Snackbar !== undefined && btf.snackbarShow(GLOBAL_CONFIG.Snackbar.day_to_night)
    } else {
        activateLightMode()
        saveToLocal.set('theme', 'light', 2)
        GLOBAL_CONFIG.Snackbar !== undefined && btf.snackbarShow(GLOBAL_CONFIG.Snackbar.night_to_day)
    }
    // handle some cases
    typeof utterancesTheme === 'function' && utterancesTheme()
    typeof FB === 'object' && window.loadFBComment()
    window.DISQUS && document.getElementById('disqus_thread').children.length && setTimeout(() => window.disqusReset(), 200)
    switchPostChart();
};

rmf.copyWordsLink = function () {
    const decodedUrl = decodeURIComponent(window.location.href); // 解码 URL
    navigator.clipboard.writeText(decodedUrl)
        .then(() => {
            Snackbar.show({
                text: '链接复制成功!快去分享吧!',
                pos: 'top-right',
                showAction: false
            });
        })
};

rmf.switchReadMode = function () {
    const $body = document.body
    $body.classList.add('read-mode')
    const newEle = document.createElement('button')
    newEle.type = 'button'
    newEle.className = 'fas fa-sign-out-alt exit-readmode'
    $body.appendChild(newEle)

    function clickFn() {
        $body.classList.remove('read-mode')
        newEle.remove()
        newEle.removeEventListener('click', clickFn)
    }

    newEle.addEventListener('click', clickFn)
}

//复制选中文字
rmf.copySelect = function () {
    navigator.clipboard.writeText(document.getSelection().toString()).then(() => {
        Snackbar.show({
            text: '已复制选中文字!',
            pos: 'top-right',
            showAction: false,
        });
    });
}

//回到顶部
rmf.scrollToTop = function () {
    document.getElementsByClassName("menus_items")[1].setAttribute("style", "");
    document.getElementById("name-container").setAttribute("style", "display:none");
    btf.scrollToDest(0, 500);
}
rmf.translate = function () {
    document.getElementById("translateLink").click();
}
rmf.searchinThisPage = () => {
    let mask = setMask(); // 确保 mask 元素存在于 document.body 中
    document.getElementsByClassName("local-search-box--input")[0].value = window.getSelection().toString();
    document.getElementsByClassName("search")[0].click();
    var evt = document.createEvent("HTMLEvents");
    evt.initEvent("input", false, false);
    document.getElementsByClassName("local-search-box--input")[0].dispatchEvent(evt);

    // 在尝试移除 mask 元素之前检查它是否存在于 document.body 中
    if (document.body.contains(mask)) {
        document.body.removeChild(mask);
    }
}

document.body.addEventListener('touchmove', function (e) {

}, {passive: false});

function popupMenu() {
    //window.oncontextmenu=function(){return false;}
    window.oncontextmenu = function (event) {
        Snackbar.show({
            text: '按住 Ctrl 再点击右键,即可恢复原界面哦',
            pos: 'bottom-left',
            showAction: false
        });
        if (event.ctrlKey || document.body.clientWidth < 900) return true;
        $('.rightMenu-group.hide').hide();
        if (document.getSelection().toString()) {
            $('#menu-text').show();
        }
        if (document.getElementById('post')) {
            $('#menu-post').show();
        } else {
            if (document.getElementById('page')) {
                $('#menu-post').show();
            }
        }
        var el = window.document.body;
        el = event.target;
        var a = /^(?:http(s)?:\/\/)?[\w.-]+(?:\.[\w.-]+)+[\w\-._~:\/?#[\]@!$&'*+,;=]+$/
        if (a.test(window.getSelection().toString()) && el.tagName !== "A") {
            $('#menu-too').show()
        }
        if (el.tagName === 'A') {
            $('#menu-to').show()
            rmf.open = function () {
                if (el.href.indexOf("http://") === -1 && el.href.indexOf("https://") === -1 || el.href.indexOf("blog.june-pj.cn") !== -1) {
                    pjax.loadUrl(el.href)
                } else {
                    location.href = el.href
                }
            }
            rmf.openWithNewTab = function () {
                window.open(el.href);
                // window.location.reload();
            }
            rmf.copyLink = function () {
                const url = el.href;
                navigator.clipboard.writeText(url);
                Snackbar.show({
                    text: '链接复制成功!快去分享吧!',
                    pos: 'top-right',
                    showAction: false
                });
            };
        }
        if (el.tagName === 'IMG') {
            $('#menu-img').show()
            rmf.openWithNewTab = function () {
                window.open(el.src);
                // window.location.reload();
            }
            rmf.click = function () {
                el.click()
            }
            rmf.copyLink = function () {
                const url = el.src
                navigator.clipboard.writeText(url);
                Snackbar.show({
                    text: '链接复制成功!快去分享吧!',
                    pos: 'top-right',
                    showAction: false
                });
            }
            rmf.saveAs = function () {
                var a = document.createElement('a');
                a.href = el.src;
                 // 获取图片的文件名部分
                a.download = el.src.split('/').pop(); // 使用图片的文件名作为下载文件名
                a.style.display = 'none'; // 隐藏下载链接
                document.body.appendChild(a);
                a.click();
                document.body.removeChild(a);
            }
        } else if (el.tagName === "TEXTAREA" || el.tagName === "INPUT") {
            $('#menu-paste').show();
            // rmf.paste=function(){
            //     input.addEventListener('paste', async event => {
            //         event.preventDefault();
            //         const text = await navigator.clipboard.readText();
            //         el.value+=text;
            //       });
            // }
            rmf.paste = function () {
                navigator.permissions
                    .query({
                        name: 'clipboard-read'
                    })
                    .then(result => {
                        if (result.state === 'granted' || result.state === 'prompt') {
                            //读取剪贴板
                            navigator.clipboard.readText().then(text => {
                                console.log(text)
                                insertAtCursor(el, text)
                            })
                        } else {
                            Snackbar.show({
                                text: '请允许读取剪贴板!',
                                pos: 'top-center',
                                showAction: false,
                            })
                        }
                    })
            }
        }
        let pageX = event.clientX + 10;
        let pageY = event.clientY;
        let rmWidth = $('#rightMenu').width();
        let rmHeight = $('#rightMenu').height();
        if (pageX + rmWidth > window.innerWidth) {
            pageX -= rmWidth + 10;
        }
        if (pageY + rmHeight > window.innerHeight) {
            pageY -= pageY + rmHeight - window.innerHeight;
        }
        mask = setMask();
        window.onscroll = () => {
            rmf.showRightMenu(false);
            window.onscroll = () => {
            }
            if (document.body.contains(mask)) {
                document.body.removeChild(mask);
            }
        }

        $(".rightMenu-item").click(() => {
            if (document.body.contains(mask)) {
                document.body.removeChild(mask);
            }
        });

        $(window).resize(() => {
            rmf.showRightMenu(false);
            if (document.body.contains(mask)) {
                document.body.removeChild(mask);
            }
        });

        mask.onclick = () => {
            if (document.body.contains(mask)) {
                document.body.removeChild(mask);
            }
        };

        rmf.showRightMenu(true, pageY, pageX);
        return false;
    };

    window.addEventListener('click', function () {
        rmf.showRightMenu(false);
    });
}

if (!(navigator.userAgent.match(/(phone|pad|pod|iPhone|iPod|ios|iPad|Android|Mobile|BlackBerry|IEMobile|MQQBrowser|JUC|Fennec|wOSBrowser|BrowserNG|WebOS|Symbian|Windows Phone)/i))) {
    popupMenu()
}
const box = document.documentElement

function addLongtabListener(target, callback) {
    let timer = 0 // 初始化timer

    target.ontouchstart = () => {
        timer = 0 // 重置timer
        timer = setTimeout(() => {
            callback();
            timer = 0
        }, 380) // 超时器能成功执行,说明是长按
    }

    target.ontouchmove = () => {
        clearTimeout(timer) // 如果来到这里,说明是滑动
        timer = 0
    }

    target.ontouchend = () => { // 到这里如果timer有值,说明此触摸时间不足380ms,是点击
        if (timer) {
            clearTimeout(timer)
        }
    }
}

addLongtabListener(box, popupMenu)

创建css

创建 \themes\butterfly\source\css\rightmenu.css

/* rightMenu */
[data-theme='light'] #rightMenu{
    display: none;
    position: fixed;
    width: 160px;
    height: fit-content;
    top: 10%;
    left: 10%;
    background-color: var(--card-bg);
    border: 1px solid rgb(210,210,210);;
    border-radius: 8px;
    z-index: 100;
    box-shadow: 3px 3px 5px #88888894;
    background-color: var(--june-white-acrylic1);
    backdrop-filter: blur(30px);
}
[data-theme='dark'] #rightMenu{
    display: none;
    position: fixed;
    width: 160px;
    height: fit-content;
    top: 10%;
    left: 10%;
    background-color: var(--card-bg);
    border: 1px solid rgb(210,210,210);;
    border-radius: 8px;
    z-index: 100;
    box-shadow: 3px 3px 5px #88888894;
    background-color: var(--june-black-acrylic1);
    backdrop-filter: blur(30px);
}
#rightMenu .rightMenu-group{
    padding: 7px 6px;
}
#rightMenu .rightMenu-group:not(:nth-last-child(1)){
    border-bottom: 1px solid rgb(180,180,180);
}
#rightMenu .rightMenu-group.rightMenu-small{
    display: flex;
    justify-content: space-between;
}
#rightMenu .rightMenu-group .rightMenu-item{
    height: 30px;
    line-height: 30px;
    border-radius: 8px;
    transition: 0.3s;
    color: var(--font-color);
}
#rightMenu .rightMenu-group.rightMenu-line .rightMenu-item{
    display: flex;
    height: 40px;
    line-height: 40px;
    padding: 0 4px;
}
#rightMenu .rightMenu-group .rightMenu-item:hover{
    background-color: var(--text-bg-hover);
    box-shadow: 0px 0px 5px var(--june-border);
}
#rightMenu .rightMenu-group .rightMenu-item i{
    display: inline-block;
    text-align: center;
    line-height: 30px;
    width: 30px;
    height: 30px;
    padding: 0 5px;
}
#rightMenu .rightMenu-group .rightMenu-item span{
    line-height: 30px;
}
#rightMenu:hover{
    border: 1px solid var(--june-theme);
}
#rightMenu .rightMenu-group.rightMenu-line .rightMenu-item *{
    height: 40px;
    line-height: 40px;
}
.rightMenu-group.hide{
    display: none;
}
.rightMenu-item:hover{
    color:white!important;
    background-color:var(--june-theme)!important;
}

引入

- <script type="text/javascript" src="/js/rightmenu.js"></script>
- <link rel="stylesheet" href="/css/rightmenu.css">

自定义右键菜单

/posts/8bdb35fb/
作者
biss
发布于
许可协议
CC BY-NC-SA 4.0
网站
订阅 打赏

Comments

自定义右键菜单

Ctrl + 右键:浏览器原生菜单