制作chrome插件,解决简书复制的小尾巴——我最终失败了

我们在简书(或者其他的一些网站)看到了一些代码就会想复制下来,但是复制下来之后会出现一个问题,就是小尾巴。

《制作chrome插件,解决简书复制的小尾巴——我最终失败了》 image.png

很长的一个小尾巴。
如果一不小心就会运行这段代码。
但是不知道为什么,我的内容没有小尾巴,我以为是每个文章都会有的来着的。
先来说说最简单的办法(其实也是足够的了,但是为了学习到更多东西还是用麻烦的办法吧)把它的copy事件直接取消掉。

《制作chrome插件,解决简书复制的小尾巴——我最终失败了》 直接取消掉copy事件

或者还有一种办法

var content = document.getElementsByClassName('show-content')[0];
content.removeEventListener('copy',getEventListeners(content).copy[0].listener);

但是,问题是,他们都无法用在JS代码中,只有在控制台产生效果而已。
所以,通过制作一个chrome的插件来解决这个问题,当然啦,如果是用在其他浏览器上面也是可以的啊,因为大家都借鉴了chrome的做法。
说说通过这篇文章可以学到的内容吧(如果你不会的话)

1.制作chrome插件。

2.了解copy事件。

3.了解Selection和Range。

第一部分,说说简书是怎么实现小尾巴的好啦。
有一个事件叫做copy,在复制的时候会触发它,所以说,像这样子。

window.addEventListener('copy',function(e){
            console.log(e);
            console.log(e.clipboardData);
 })

结果

《制作chrome插件,解决简书复制的小尾巴——我最终失败了》 只要按下了ctrl+c就会产生这样的结果了

但是要直接过去到底复制了什么是做不到的(以前用IE是可以的),要不然就会产生安全问题了。

所以说,clipboardData 这个东西只有 setData() 才是有反应的,getData() 返回的一直是一个空行。
所以,要实现小尾巴,有三个步骤要走的。

1.获取用户选中的内容

2.在原有内容的基础上添加小尾巴

3.把修改好的内容用 clipboardData.setData() 添加进黏贴板。

第二和第三步都很容易可以实现。所以重点在第一步了。

第二部分,说说如何实现第一步。
先说两个东西

var selection  = getSelection();
var range = select.getRangeAt(0);
//因为在chrome上range数量永远都是一个,所以一直用0就好了
//注意啦,如果事先没有选中过任何对象的话,第二行会报错的。

这两个东西(selection和range)在MDN说的很明白了就不多说了。中文教程也是有的。
但是,为了让不了解的人快点懂,还是说说好了。
#####################################

《制作chrome插件,解决简书复制的小尾巴——我最终失败了》 一首来自首页的诗歌

对比一下下面的效果。

《制作chrome插件,解决简书复制的小尾巴——我最终失败了》 toString和copyCotents

如果,是下面用户复制到的内容是下面的那种肯定会不高兴了。
但是,上面的那种呢。有html节点诶,怎么办。
很好处理的。就是把这一段东西插入一个用户看不见的地方去并且在最后加入小尾巴,然后,利用 下面四个API进行全部选取就好了。
这样子加了小尾巴的内容就到了用户的剪贴板里面了。

        Range.setEnd()
        Range.setStart()
        document.createRange()
        Selection.addRange()

第三部分了,说说怎么放在一个看不见的地方,然后还让它被选取。

为了方便说明还是让大家可以看见它好了,相信会看见这篇文章的人实现这个东西应该是没有问题的。
跳过了若干你应该知道的步骤以后来到了这里。

《制作chrome插件,解决简书复制的小尾巴——我最终失败了》 image.png

《制作chrome插件,解决简书复制的小尾巴——我最终失败了》 image.png

然后是这样的。

var selection  = getSelection();
var range = document.createRange()
selection.removeAllRanges();
range.setStart(experimental.firstChild,0);
range.setEndAfter(experimental.lastChild);
select.addRange(range)

《制作chrome插件,解决简书复制的小尾巴——我最终失败了》 得到的效果

可以自由进行既然已经选中了,那么可以触发Copy了。
关于如何触发copy当然是这个咯

document.execCommand('Copy');

目前为止,完整的代码如下。

        let blankSpace=document.createElement('div');
        let article=document.getElementsByClassName('article')[0];
        article.insertBefore(blankSpace,article.firstChild);
        window.addEventListener('copy',function(e){
            let data=e['clipboardData'];
            let selection=getSelection();
            let oldRange=selection.getRangeAt(0);
            //把原来的range保存下来,得会还要放回去的。
            let content=oldRange.cloneContents();
            blankSpace.appendChild(content);
            let newRange=document.createRange();
            selection.removeAllRanges();
            newRange.setStart(blankSpace.firstChild,0);
            newRange.setEndAfter(blankSpace.lastChild);
            selection.addRange(newRange);
            document.execCommand('Copy');
            selection.removeAllRanges();
            selection.addRange(oldRange);
        })
//!!!但是,这段代码真的是正确的么,它可以实现我们的目的么。不可以的。

说说吧,为什么不可以。很简单的原因,会产生递归调用。document.execCommand(‘Copy’); 一样会产生一个copy事件,而且这个事件会让所有的Listener收到,相当于是广播的效果。
怎么处理呢。当执前的时候remove掉,之后再加上。
同时,document.execCommand是一个广播,也就是说,简书的copy会再来一次,我们本来是像复制没有小尾巴的结果,结果在复制的时候又被加上了小尾巴。

第三部分,如何解决上面的问题。
先说结果,我还是没想到怎么办才好。但是还是说说我都是怎么考虑的吧。我们都知道,先注册的事件先执行,后注册的后执行。
我们加进去的事件肯定是后执行的。
我用一下代码验证了我的想法

var hanshu=getEventListeners(document.getElementsByClassName('show-content')[0]).copy[0].listener
        
        var  aacfun = function(e){
            console.log('aaa');
            hanshu(e)
        }
        document.getElementsByClassName('show-content')[0].removeEventListener('copy',hanshu)
        document.getElementsByClassName('show-content')[0].addEventListener('copy',aacfun)

把原来的函数提取出来,然后再注册一次(注意,getEventListeners是Chrome的API代码里面是用不了的,只可以在console里面。)
结果,告诉我aaa被先输出,然后才执行了我的回调函数。

我还干了另外的事情。关于 document.execcommand的返回值,如果是true代表执行了,false代表不可以执行,document.execcommand(‘copy’)出于安全考虑只有在用户触发的事件才可以触发(模拟的就不可以了),并且回调函数中不可以用 setTimeout之类的操作。

我的返回值始终是 true 而且,我在它后面被处理。我搞不明白为什么我的结果里面仍然有小尾巴。
好了,这篇文章暂时就到这里了,继续下去只是浪费时间罢了。
很对不起看了文章的人们了,不过,我认为我写的文章更加是为了去“划重点”,告诉大家有什么东西要去学,要去用的。

    原文作者:姚舒杨
    原文地址: https://www.jianshu.com/p/c68e3dd492f4
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞