现场复现
用户只是简单的说了几句,大家都表示不可思议,都表示这不可能。最后屏幕共享的时候,果然如此,简直让人怀疑人生。一用鼠标选中了文本,页面就弹出不能复制,大概是这样的表现:
选择文字的时候不手动复制都会触发copy
远程控制排查问题
首先打开控制台,把document.copy改写一下
const cp = document.oncopy.bind(document);
document.oncopy = () => {
console.log(1);
cp();
return false;
}
结果发现,选一下竟然真的还是弹出toast和打印了1
接下来加了个断点,还是会触发,一样的过程,看起来也没啥区别。于是,开始怀疑用户的插件,瞄了一眼,没有任何可疑的插件,然后把她的Chrome扩展全部关掉,依然会复现
此时想起一句话:90%可以通过重启解决,9%可以通过重装解决,1%只能通过买新电脑解决
电脑重启了,继续远程控制。还是一样的问题,我说要不你多打开其他网站试试,任何网站都行。小姐姐还是很耐心的一个个操作给我看:“你看什么页面都ok,就你这有问题”。于是我随便试了几个页面,打开控制台输入oncopy,然后就立刻复现问题:
document.oncopy = () => {
console.log(1);
return false;
}
"你看呐,所有的页面都有同样的问题~ 你再随便试多几个页面看看" ——其实我在查资料,看看是不是有我知识盲区之外的
资料也没查到类似的问题,大概无奈的看着她操作了几分钟,我也一句话都没有说,对着电脑发呆。突然萌生一个念头:系统上的个性化设定
check了一下输入法,搜狗,应该无影响。但是,在对方频繁操作中,有一个若隐若现的小logo引起我注意
??:“我发现你这有一个小logo,是干嘛的”
??:“一个翻译工具”
??:“多动动看看,我想看清楚一点”
??:“你看,放在这里,它就会翻译屏幕上的单词”
??:“那你试一下翻译其他软件如ppt呢”
??:“居然也可以喔”
??:“那关掉这个翻译软件,再回来看看页面呢”
??:“好像没问题了”
??:“嗯,那就是这个软件的问题。我看有一个自动翻译你鼠标所在的英文的功能,这个功能的实现方式可能是:你鼠标放到英文上,它会触发系统的copy事件,可能是直接帮你复制或者是背后帮你按下按键。你再打开这个应用,先把这个功能关了吧”
??:“哦,我知道了,有一个划词搜索的功能,应该跟他有关”
关掉后,问题是解决了,还是很好奇:你这软件叫什么,我也下载来玩玩
下载来玩玩
下载回来开启,自己写了一个简单的demo,果然都复现了
const C: React.FC = () => {
const ref = React.useRef<HTMLDivElement>(null);
React.useEffect(() => {
document.oncopy = (): boolean => {
Toast.error('禁止复制');// 仅仅一个toast,随便找个ui库吧
return false;
};
document.onclick = (): void => {
ref.current!.innerHTML += '你点击了页面<br />';
};
const handleKeydown = (e): void => {
ref.current!.innerHTML += `你按下了${e.key}<br />`;
};
document.addEventListener('keydown', handleKeydown);
return (): void => {
document.oncopy = null;
document.onclick = null;
document.removeEventListener('keydown', handleKeydown);
};
}, []);
return (
<>
<pre>
我说 你是人间的四月天; 笑响点亮了四面风; 轻灵在春的光艳中交舞着变。 你是四月早天里的云烟,
黄昏吹着风的软,星子在 无意中闪,细雨点洒在花前。
</pre>
<div>
操作记录
<div ref={ref} />
</div>
</>
);
};
按照预期,如果不开欧路词典,我们复制页面的内容,将会弹出toast禁止复制,如下:
开启了欧路词典,表现是这样:
问题因此转化为,如何区分出欧路词典的copy
解决方案
我们使用一种最简单的方式,按下command(key为Meta)不弹起的时候,生产key的队列,当最后一个按下的是c,则消费生产者队列,往前搜索有没有按过command
const Cpn: React.FC = () => {
const ref = React.useRef<HTMLDivElement>(null);
const providerQuene = React.useRef<string[]>([]);
const triggerCopy = React.useCallback(() => {
// 消费生产者队列的数据
const last = providerQuene.current.pop();
// 如果最后按下的是c,而且键盘不弹起,往前找是不是按下过command
if (last === 'c' && providerQuene.current.includes('Meta')) {
Toast.error('禁止复制');
}
}, [providerQuene]);
React.useEffect(() => {
document.oncopy = (): boolean => {
triggerCopy();
return false;
};
document.onclick = (): void => {
ref.current!.innerHTML += '你点击了页面<br />';
};
const handleKeydown = (e: KeyboardEvent): void => {
if (e.key === 'Meta') {
providerQuene.current.push('Meta');
}
if (e.key === 'c') {
providerQuene.current.push('c');
}
ref.current!.innerHTML += `你按下了${e.key}<br />`;
};
const handleKeyUp = ({ key }: KeyboardEvent): void => {
key === 'Meta' && (providerQuene.current = []); // meta键弹起,清理生产者队列
};
document.addEventListener('keydown', handleKeydown);
document.addEventListener('keyup', handleKeyUp);
return (): void => {
document.oncopy = null;
document.onclick = null;
document.removeEventListener('keydown', handleKeydown);
document.removeEventListener('keyup', handleKeyUp);
};
}, []);
return (
<>
<pre>
我说 你是人间的四月天; 笑响点亮了四面风; 轻灵在春的光艳中交舞着变。 你是四月早天里的云烟,
黄昏吹着风的软,星子在 无意中闪,细雨点洒在花前。
</pre>
<div>
操作记录
<div ref={ref} />
</div>
</>
);
};
最后
常见问题FAQ
- 免费下载或者VIP会员专享资源能否直接商用?
- 本站所有资源版权均属于原作者所有,这里所提供资源均只能用于参考学习用,请勿直接商用。若由于商用引起版权纠纷,一切责任均由使用者承担。更多说明请参考 VIP介绍。
- 提示下载完但解压或打开不了?
- 找不到素材资源介绍文章里的示例图片?
- 模板不会安装或需要功能定制以及二次开发?
发表评论
还没有评论,快来抢沙发吧!