Files
blog/source/_posts/2025.08/custom-right-menu.md
2025-08-26 13:20:20 +08:00

17 KiB
Raw Blame History

title, categories, tags, series, abbrlink, cover, summary, date
title categories tags series abbrlink cover summary date
自定义右键菜单 建站手札 网站 webcustom 8bdb35fb https://pic.biss.click/i/2025/08/26/349074.webp 自定义右键菜单 2025-08-11 16:02:12

演示

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

新建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">