做有态度的前端团队

网易FEG前端团队

关于复制组件的那点事

1. 故事背景

自从Chrome 46屏蔽了Flash自动播放之后,很多小尺寸的Flash在Chrome 46以上都不会自动播放,替代的是一个播放按钮。而我们的复制组件就是基于Flash实现的,因此在Chrome 46以上的浏览器中,初始化后的复制按钮会呈现一个播放按钮,点击后Flash才会运行,之后再点击一下才能够实现复制,交互成本多了一个环节,用户体验就差了,遂需要对组件进行优化,实现Flash和浏览器接口的自主选择,在能使用浏览器接口的浏览器则默认使用浏览器接口,不支持的则使用Flash。

2. 发生了什么事情?

既然是要用浏览器接口实现,那第一件事就是需要知道各浏览器都为复制内容到剪贴板实现了怎样的接口,之间的兼容性怎样?

文档对象提供了一个execCommand方法,通过传入不同的参数可以实现对文档的当前选择或范围的不同操作,如复制、黏贴等。

参考文档:https://developer.mozilla.org/en-US/docs/Web/API/Document/execCommand

returnvalue.png

在文档中看到了上图,灵机一动,那就在初始化的时候事先运行一下这个接口函数,如果返回值是false,那就说明不支持该接口啦,哈哈,太聪明了。

3. 过程中遇到了什么问题?

实际上execCommand方法并未加入到标准中,还存在着很多的兼容性问题,例如在firefox 41以下不支持,但直接报异常(如下图),没有任何返回值。而在41及以下版本的Chrome等浏览器中,虽然也不支持该接口,但是却会返回false,也不会报异常。

securityerror: the operation is insecure

最最最关键的是,通过try catch居然不能捕获到这个异常,并且这个接口必须是要用户点击执行的才会生效并返回True,相反,如果用js去执行该接口,即使是支持该方法的浏览器,也是返回false。
遂,想要只通过它的返回值来判断是否支持该方法是行不通的。

4. 最后是怎么解决的?

于是查了execCommand的兼容性如下图:

复制接口兼容性.png

遂可以通过上图中支持copy/cut特性的浏览器默认设为使用execCommand接口,而其他不支持的浏览器则默认使用Flash。

文档说ie9以上才支持该接口,但亲测ie8和ie8下模拟ie7都能完美支持该接口。

遂在实践过程中默认只根据浏览器和其版本来判断是否用接口或flash,但是考虑到ie下每次复制前都会先弹窗提示用户,用户确认了才可以进行复制,用户取消了则无法复制成功,多了一层交互,用户体验就差了不少,遂ie则继续使用flash方式。

最终解决方案为:

火狐浏览器统一使用flash方式,其余浏览器则先黏贴剪切板的内容到页面中,然后再复制该内容到剪切板,期间如果发生报错或异常,则执行finally中使用flash方式的函数,考虑到finally是必须会执行的,所以在finally中也会对前面复制时的返回值进行判断,如果execCommand没有返回值,则说明出现firefox 41以下浏览器的这种报异常,没有返回值的情况,则直接使用flash方式。

5. 故事总结

对于execCommand接口的兼容,需要使用到异常捕获浏览器版本判断返回值相辅。

事先会定义一个标识符初值为undefined,然后在try中执行浏览器版本判断,如果是火狐浏览器,抛出异常,否则执行execCommand函数,并将返回值赋值给标识符。最终执行finally中的代码,首先判断标识符是否为undefined,是的话就说明execCommand没有执行成功。这个时候就使用flash方式吧。

平时比较少写这类总结文章,所以思路、表达上可能会比较那个,但我发现做完一个东西后,及时的进行总结真的是会有收获,例如这次总结,就发现了之前存在的一个BUG,虽然这个BUG实际上看上去好像对功能几乎没什么影响。

再者,因写总结的时间距离事件发生时间稍微有一点点久远,有些细节记得不是太清晰了,文章中难免会有错误或不严谨的地方,如有发现,欢迎在评论中指出,有大奖哦。

手机阅读请扫描下方二维码:

添加新评论

ali-40.gifali-41.gifali-42.gifali-43.gifali-44.gifali-45.gifali-46.gifali-47.gifali-48.gifali-49.gifali-50.gifali-51.gifali-52.gifali-53.gifali-54.gifali-55.gifali-56.gifali-57.gifali-58.gifali-59.gifali-60.gifali-61.gif