Noonisy
Typecho代码块复制
2022-08-23
阅读:578

Typecho代码块复制


给代码块添加一个复制按钮,实现鼠标移到代码块上就显示 $copy$,移开就消失的效果

2024-10-07 修改

忽略以前的方法 在 MDUI2333.js 中增加以下代码(MDUI2333.js 是当前主题的 js 文件):
function initCodeCopy() {
    const codeblocks = document.querySelectorAll("pre");
    
    codeblocks.forEach(block => {
        if (!block.querySelector('.copy-button')) {
            block.style.position = "relative";
            const copy = document.createElement("div");
            copy.className = "copy-button";
            copy.innerHTML = "COPY";
            copy.style.cssText = "position: absolute; right: 4px; top: 4px; background-color: white; padding: 2px 8px; border-radius: 4px; cursor: pointer; box-shadow: 0 2px 4px rgba(255,0,0,0.05), 0 2px 4px rgba(255,0,0,0.05); visibility: hidden;";
            block.appendChild(copy);
        }
    });
}

function handleCodeCopy(e) {
    if (e.target.classList.contains('copy-button')) {
        const pre = e.target.closest('pre');
        const code = pre.querySelector('code');
        const range = document.createRange();
        range.selectNode(code);
        const selection = window.getSelection();
        selection.removeAllRanges();
        selection.addRange(range);
        document.execCommand('copy');
        selection.removeAllRanges();

        e.target.innerHTML = "COPY SUCCESSFUL";
        setTimeout(() => {
            e.target.innerHTML = "COPY";
        }, 1000);
    }
}

function handleCodeBlockHover(e) {
    const pre = e.target.closest('pre');
    if (pre) {
        const copyButton = pre.querySelector('.copy-button');
        if (copyButton) {
            copyButton.style.visibility = e.type === 'mouseover' ? 'visible' : 'hidden';
        }
    }
}

// 在文档加载完成后初始化
document.addEventListener('DOMContentLoaded', () => {
    initCodeCopy();
    document.body.addEventListener('click', handleCodeCopy);
    document.body.addEventListener('mouseover', handleCodeBlockHover);
    document.body.addEventListener('mouseout', handleCodeBlockHover);
});
在 footer.php 中添加代码:
// PJAX 相关代码
$(document).on('pjax:complete', function(){
    // ... 其他 PJAX 完成后的操作 ...
    initCodeCopy();
});
总结:现在不需要单独加载 codecopy.js,在每次 PJAX 完成后重新初始化代码复制功能,避免了多次绑定事件监听器的问题

2024-09-30 修改

在以前的操作上(下面的内容),继续在 footer.php 中,增加函数
function loadScript(url, callback) {
        var script = document.createElement("script");
        script.type = "text/javascript";
        script.src = url;
        script.onload = callback;
        document.head.appendChild(script);
    }
pjax:end 中 添加一行代码
loadScript("<?php $this->options->themeUrl('js/codecopy.js'); ?>")

添加复制按钮

在当前主题的js文件夹下新建codecopy.js,如下

注意:有几个地方childNodes[i]需要根据实际调整,可以console.log输出一下childNodes[i].length
var codeblocks = document.getElementsByTagName("pre")
//循环每个pre代码块,并添加 复制代码

for (var i = 0; i < codeblocks.length; i++) {
    //显示 复制代码 按钮
    currentCode = codeblocks[i]
    currentCode.style = "position: relative;"
    var copy = document.createElement("div")
    copy.style = "position: absolute;right: 4px;\
    top: 4px;background-color: white;padding: 2px 8px;\
    border-radius: 4px;cursor: pointer;\
    box-shadow: 0 2px 4px rgba(255,0,0,0.05), 0 2px 4px rgba(255,0,0,0.05);"
    copy.innerHTML = "COPY"
    currentCode.appendChild(copy)
    //让所有 "复制"按钮 全部隐藏
    copy.style.visibility = "hidden"
}


for (var i = 0; i < codeblocks.length; i++) {
    !function (i) {
        //鼠标移到代码块,就显示按钮
        codeblocks[i].onmouseover = function () {
            codeblocks[i].childNodes[2].style.visibility = "visible"
        }

        //执行 复制代码 功能
        function copyArticle(event) {
            const range = document.createRange();

            //范围是 code,不包括刚才创建的div
            range.selectNode(codeblocks[i].childNodes[1]);

            const selection = window.getSelection();
            if (selection.rangeCount > 0) selection.removeAllRanges();
            selection.addRange(range);
            document.execCommand('copy');

            codeblocks[i].childNodes[2].innerHTML = "COPY SUCCESS"
            setTimeout(function () {
                codeblocks[i].childNodes[2].innerHTML = "COPY"
            }, 1000);
            //清除选择区
            if (selection.rangeCount > 0) selection.removeAllRanges(); 0
        }
        if (codeblocks[i].childNodes.length == 2) {
            codeblocks[i].childNodes[1].addEventListener('click', copyArticle, false);
        } else {
            codeblocks[i].childNodes[2].addEventListener('click', copyArticle, false);
        }
    }(i);

    !function (i) {
        //鼠标从代码块移开 则不显示复制代码按钮
        codeblocks[i].onmouseout = function () {
            codeblocks[i].childNodes[2].style.visibility = "hidden"
        }
    }(i);
}

引用 $JS$ 文件

一般情况下,在footer.php内进行引用,在</body>之前就行,引用codecopy.js
<script type="text/javascript" src="/usr/themes/mdui2333/js/codecopy.js"></script>
当然,为了跟其他静态文件保持一致,使用 $CDN$,就用 $php$ 引入
<script src="<?php $this->options->themeUrl('js/codecopy.js'); ?>"></script>

设置 $PJAX$ 回调函数

本来以为这样就好了,但是刷新的时候发现出了问题,比如跳转之后,鼠标移动上去,并不会显示 $copy$,这是因为使用了PJAX,异步请求资源,实现站内页面刷新,而不是做页面跳转,好处是可以极大的减少网络请求、服务器消耗等,提升浏览体验

但是呢,会出现一个问题,就是PJAX只会获取pjax-container容器内的数据,并非当前页面的数据,这就会导致比如上一个页面加载过codecopy.js后,跳转之后并不会再次加载该js文件,这样的话,就无法在当前的代码块上面新建一个div用于展示 $copy$ 按钮,也就是说,代码块的复制功能失效了

知道了出现错误的原因,就好办了,既然是因为没有加载该js文件,那就再加载一次呗,于是用到了PJAX的回调函数。找到pjax: completepjax:end函数,在complete中间插入回调函数

注意:命名可能不太一样
$.getScript("<?php $this->options->themeUrl('js/codecopy.js'); ?>");  //在complete后, end前

参考

最后编辑于:2024 年 11 月 22 日 11:54
邮箱格式错误
网址请用http://或https://开头