一个简单的JavaScript问题 - 剪切板访问

一. 前言

一个看似很简单的问题, 就是访问剪切板.

img

edge浏览器有一项功能就是复制连接粘贴到md文件时会自动复制标题并形成上面的链接形式.

鉴于edge已经臃肿得难以忍受, 大部分时间使用的是百分浏览器 - 追求速度, 简约和安全的网络浏览器, 所以就想写个脚本书签来实现这个功能.

按理说这个功能很简单, 就是复制网页的标题和链接, 然后拼起来复制到剪切板即可, 但最后发现访问剪切板是很麻烦的.

因为之前写过的脚本都是使用tampermonkey提供的GM_setClipboard, 从未注意到原生js api实现复制内容到剪切板所存在的问题.

1.1 脚本书签

javascript: (()=> console.log('bookmark'))();

基于上述形式的书签.

img

img

更多小书签见: 各种骚操作, 中文网最全 Bookmarklet 小书签 - 奔跑中的奶酪

二. clipboard API

Clipboard - Web API 接口参考 | MDN

img

这个api的支持也是乏善可陈, 各个浏览器的支持状况也不是很好.

需要注意的是在http非安全链接的页面, 该api是无法使用的.

三. 问题

查看上面的api, 以为直接用就可以, 但并不是如此.

img

两个函数的差异.

(async () => {
        // document.documentElement.click()
        await window.navigator.clipboard.writeText(location.href + document.title);
        console.log('a');
    })();

注意, 这个api是异步操作的.

img

直接操作会抛出错误.

img

但是这种操作就没有问题.

这种限制应该是浏览器出于安全考量, 让剪切板访问局限在相对小的范围, 只有当用户激活(主动操作)页面时, 相关操作才允许执行.

四. 问题延申

但是, 重新琢磨才发现问题不止如此.

img

From version 66, no permissons or gesture required to write to clipboard.

这个描述表示什么意思? gesture required , 不需要页面聚焦?

测试 chrome66/68, 都无法在无聚焦状态下实现复制内容到剪切板.

chrome浏览器在整个测试中表现都是一样的, 复制内容需要页面聚焦.

navigator.permissions.query({name: 'clipboard-write',}).then(result => console.log(result));
Promise {<pending>}
VM41:1
PermissionStatus {name: 'clipboard_write', state: 'granted', onchange: null}
name: "clipboard_write"
onchange: null
state: "granted"

4.1 edge浏览器

首先是edge"不支持"小书签(很符合微软的作风).

javascript - How can I achieve bookmarklet functionality in Microsoft Edge browser (without installing anything extra)? - Stack Overflow

Support for bookmarklets? - Microsoft Community

但. 但, 但是, 微软还留了尾巴.

img

img

没错, edge允许通过地址栏执行小脚本代码....呵呵, 微软的脑路.

但是edge浏览器在剪切板api上的表现和chrome一致.

4.2 centbrowser

Version 5.0.1002.354 (Official Build) (64-bit) (Portable) (Chromium 102.0.5005.167)

这个浏览器可能是因为魔改的原因, 在这个剪切板的api上的表现和上面的两个浏览器表现差异很大.

javascript:(()=>navigator.clipboard.writeText(location.href))();

这段代码的表现基本上和在chrome上类似.

javascript:navigator.clipboard.writeText(location.href);

但是这段代码就很诡异了, 多数情况下是可以复制内容到剪切板的, 但是这个过程并没有进行页面聚焦的操作.

两段代码差异在于是否包裹在匿名函数中执行.

img

但在一些个案中也出现无法正常复制内容的情况.

为了排除扩展或者其他脚本的影响, 无痕模式下, 第二个函数依然表现得和在常规模式下一样.

五. 解决

基于clipboardapi是无法稳定实现上述这个简单的复制链接功能.

javascript: (() => { const input = document.createElement('input'); document.body.appendChild(input), input.setAttribute('value', '[' + document.title + ']' + '(' + location.href + ')'), input.select(), console.log((document.execCommand('copy') ? 'successfully' : 'fail to') + ' copy title and href'), document.body.removeChild(input); })();

只能取巧使用这个过时的api document.execCommand, 通过创建html元素, 赋值, 选中, 复制内容, ....这种看似不是很优雅的方式实现一个很简单功能.

假如不主动聚焦页面, 高版本chrome的控制板支持一个新特性, 在渲染中模拟页面聚焦.

img

六. 小结

复制, 粘贴, 简单但不简单.