工作室注册登录标签云赞助光荣榜

当前位置:首页 > 全栈分享 > JavaScript > 正文

给文章代码高亮模块增加一键复制功能

发布时间:2021-08-23 23:39:50作者:魏义齐阅读:()评论:

本文为博主原创文章,未经博主允许不得转载。

ps(2022年6月9日):心急吃不了热豆腐!最近通过统计数据发现,词跟文章匹配的非常精准,但大都十秒左右就离开了,你要是想直接找一段代码实现代码复制是不可能的。通读全文并理解,你不仅能解决自己的问题,还能受益匪浅。补上这段话,不是怕我的跳出率高,而是替你们这帮不识货的感到惋惜!

正如本站描述所说,本站文章皆源自工作实践中的总结,因此遇到问题经常需要去写过的文章里复制代码,代码短了还好说,代码篇幅长了鼠标拉猛了容易选中别的东西,体验很不好,就越来越想做个一键复制功能。

codesnippet应该是有复制功能的,但开发出来有难度,再者,本站因为换程序原因,使用了不止一种代码高亮插件,除了codesnippet还有codeblock,因此我的方法要对这两个代码高亮插件都有效。

我第一想到的是clipboard.min.js,早在文章“点击文字即可将文字复制到粘贴板的实现方法”里就写过对该js的应用,但它是在容器里点击直接复制容器里的文字,而我的需求是点击复制按钮再复制代码,原理不一样,且我也不想再多一个http请求,于是想到秋叶网络博客,该大佬文章里的代码就有一键复制功能,在其博客搜“复制”一词果然找到了一段兼容性极高的js复制函数代码。

window.copy = {
    text: function(textContainer, callback) {
        var copyTextArea = document.querySelectorAll(textContainer); //要拷贝的文本容器
        var text = '';
        if(!copyTextArea.length) {
            text = textContainer;
        } else {
            for(var i = 0; i < copyTextArea.length; i++) {//把选择器所有的文本通过换行符拼接,取值区分输入框
                text += ((copyTextArea[i].nodeName === 'INPUT' || copyTextArea[i].nodeName === 'TEXTAREA') ? copy.HTMLEncode(copyTextArea[i].value) : copyTextArea[i].innerHTML) + '\n';
            }
        }
        this.div = document.createElement('pre');//创建临时容器,pre为了保证复制的内容带有格式,尤其是多段跨区域复制
        this.div.innerHTML = text;
        this.div.style.positon = 'absolute';
        this.div.style.left = '-99999999';//把临时容器偏移到无穷远处,absolute不妨碍网页原本的内容定位和视觉
        copyTextArea = this.div;
        document.body.appendChild(this.div);
        (copyTextArea.nodeName === 'INPUT' || copyTextArea.nodeName === 'TEXTAREA') ? copyTextArea.select(): copy.selectText(copyTextArea);
 
        copyTextArea.focus()
 
        try {
            var successful = document.execCommand('copy');
            if(!callback) {
                callback = function() {
                    alert("代码复制成功,如要转载文章请按版权声明获得授权!");
                };
            }
            if(successful) {
                callback();
            } else {
                copy.alertError();
            }
            if(this.div) {
                this.div.parentNode.removeChild(this.div);
            }
        } catch(err) {
            copy.alertError();
        }
    },
    HTMLEncode: function(html) {
        var temp = document.createElement("div");
        (temp.innerHTML != null) ? (temp.innerHTML = html) : (temp.innerText = html);
        var output = temp.innerHTML;
        temp = null;
        return output;
    },
    alertError: function() {
        alert("代码复制失败,请手动选择代码复制!");
    },
    selectText: function(copyTextarea) {
        if(copyTextarea.hasAttribute('contenteditable')) {
            copyTextarea.focus();
        }
        if(document.selection) {
            var range = document.body.createTextRange();
            range.moveToElementText(copyTextarea);
            range.select();
        } else if(window.getSelection) {
            var selection = window.getSelection();
            var range = document.createRange();
 
            range.selectNodeContents(copyTextarea);
            selection.removeAllRanges();
            selection.addRange(range);
        }
    }
};

原文地址:https://www.mizuiren.com/blog/498.html。上述代码略有修改,因为该代码是针对文字的,就过滤了html标签,而代码高亮模块里面的代码大部分情况下都是有html标签的,所以需要删除textContent属性相关的,不会删没有关系,把所有textContent替换成innerHTML,只要代码不出问题就可以,不删就只能复制出代码里面的文字。

f12可以分析出codesnippet的代码在code标签里的ul li里,codeblock的代码在blockquote标签里的ol li里。首先需要给所有ul和ol增加同一个类名,用于后面动态生成id;其次用prepend()方法给ul和ol增加复制按钮,这个方法的特殊性在于在被选元素的开头(仍位于内部)插入指定内容,也就是说插入的元素跟ul和ol里的li是兄弟关系,这为下一个方法siblings()的使用创造了便利;siblings()方法用于选中被选元素的所有同级元素,且这些同级元素和被选元素拥有同一个父元素,这是为了点击复制按钮后选中复制按钮在同父元素下的所有兄弟元素时,不至于选中其它ul或ol里的li,因为一个页面可能有多个code和blockquote;代码所在的li被选中后统一增加类名,这是为了方便复制它们;然后是给父元素动态生成id,也就是按代码模块的显示顺序给ul和ol生成id,有人担心原本的id被改变后会不会影响高亮主题样式对代码模块的控制,经测试是不会的;最后是点击复制按钮调用复制函数。

总体代码如下,做了详尽的注释:

//给codesnippet和codeblock添加一键复制功能
$(document).ready(function(){
	//给code里面的ul和blockquote里面的ol增加class类名codeblock,统一定义用于下面动态生成id
	$("code ul").addClass("codeblock");
	$("blockquote ol").addClass("codeblock");
	
	//给code里面的ul和blockquote里面的ol植入复制按钮,不用li是因为codesnippet的行号代码会自动给li添加序号
	$("code ul").prepend("<div class='copycode'>复制代码</div>");
	$("blockquote ol").prepend("<div class='copycode'>复制代码</div>");
	
	//给复制代码按钮增加悬停样式
	$(".copycode").css({"cursor":"pointer","padding-left":"10px"});
	
	//选中复制按钮在当前容器中的兄弟元素并添加class类名,为复制其做准备,因为f12会发现所有代码都在li里面。限定当前容器是因为页面可能有多处代码,而这些地方代码的容器id都是一样的。
	$(".copycode").siblings().addClass("copyobject");
	
	//动态为parent生成id
	$(".codeblock").each(function(index, element){
		const __id = 'wyq_copy_container_' + index
		$(this).attr('id', __id)
		
	})
	
	//点击复制代码
	$(".copycode").click(function(){
		//得到父元素的id
		const __id = $(this).parent().attr('id')
		//如果没有id则不执行复制
		if (!__id) return
		//否则调用复制函数
		copy.text('#' + __id + ' ' + ".copyobject")
	})
})

测试html代码是:

  1. <code> 
  2.   <ul> 
  3.     <li>被复制的内容1</li> 
  4.     <li>被复制的内容2</li> 
  5.   </ul> 
  6. </code> 
  7. <blockquote> 
  8.   <ol> 
  9.     <li>被复制的内容3</li> 
  10.     <li>被复制的内容4</li> 
  11.   </ol> 
  12. </blockquote> 
  13. <code> 
  14.   <ul> 
  15.     <li>被复制的内容5</li> 
  16.     <li>被复制的内容6</li> 
  17.   </ul> 
  18. </code> 
  19. <blockquote> 
  20.   <ol> 
  21.     <li>被复制的内容7</li> 
  22.     <li>被复制的内容8</li> 
  23.   </ol> 
  24. </blockquote> 

你可以点击代码运行测试工具测试。

如果你的文章有复制提示,那么代码的复制提示会和文章的复制提示冲突,复制代码时会先执行文章的复制提示再执行代码的复制提示,这个只要让文章的复制提示不对其子元素代码高亮模块执行就可以解决。

给oncopy事件执行一个函数,比如我的:

  1. <div class="article" oncopy="myFunction()"></div> 

文章的复制提示代码这么写:

<script>
  function myFunction(){
	  $(".article").not($(".codeblock"));
	  alert("复制成功,如需转载请按版权声明获得授权!");
  }
</script>

看懂本文,不管你使用的是什么代码高亮插件,都可以为其添加一键复制功能。

本文标签帝国cms代码高亮 ,您可以阅读与「帝国cms代码高亮 」相关的所有文章

魏义齐全栈技术交流:魏义齐全栈技术交流

上一篇:jQuery解决谷歌广告不显示但使打底广告无法点击问题下一篇:js给文章外链添加rel="nofollow",内链则自动排除

原创不易,您的赞助就是博主更新的动力!

赞助请备注,8.88元及以上可在赞助光荣榜留下外链信息。

HashOver畅言云评完全自托管的评论系统

工作室注册登录标签云赞助光荣榜

最新会员
  • 东方星雨
  • deanhan
  • 1264822519
  • aini3311
  • a1051020101
  • weiyiqi