一. 前言
一个看似很简单的问题, 就是访问剪切板.
edge
浏览器有一项功能就是复制连接粘贴到md
文件时会自动复制标题并形成上面的链接形式.
鉴于edge
已经臃肿得难以忍受, 大部分时间使用的是百分浏览器 - 追求速度, 简约和安全的网络浏览器, 所以就想写个脚本书签来实现这个功能.
按理说这个功能很简单, 就是复制网页的标题和链接, 然后拼起来复制到剪切板即可, 但最后发现访问剪切板是很麻烦的.
因为之前写过的脚本都是使用tampermonkey
提供的GM_setClipboard
, 从未注意到原生js api
实现复制内容到剪切板所存在的问题.
1.1 脚本书签
javascript: (()=> console.log('bookmark'))();
基于上述形式的书签.
更多小书签见: 各种骚操作, 中文网最全 Bookmarklet 小书签 - 奔跑中的奶酪
二. clipboard API
Clipboard - Web API 接口参考 | MDN
这个api
的支持也是乏善可陈, 各个浏览器的支持状况也不是很好.
需要注意的是在http
非安全链接的页面, 该api是无法使用的.
三. 问题
查看上面的api
, 以为直接用就可以, 但并不是如此.
两个函数的差异.
(async () => {
// document.documentElement.click()
await window.navigator.clipboard.writeText(location.href + document.title);
console.log('a');
})();
注意, 这个api
是异步操作的.
直接操作会抛出错误.
但是这种操作就没有问题.
这种限制应该是浏览器出于安全考量, 让剪切板访问局限在相对小的范围, 只有当用户激活(主动操作)页面时, 相关操作才允许执行.
四. 问题延申
但是, 重新琢磨才发现问题不止如此.
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
"不支持"小书签(很符合微软的作风).
Support for bookmarklets? - Microsoft Community
但. 但, 但是, 微软还留了尾巴.
没错, 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);
但是这段代码就很诡异了, 多数情况下是可以复制内容到剪切板的, 但是这个过程并没有进行页面聚焦的操作.
两段代码差异在于是否包裹在匿名函数中执行.
但在一些个案中也出现无法正常复制内容的情况.
为了排除扩展或者其他脚本的影响, 无痕模式下, 第二个函数依然表现得和在常规模式下一样.
五. 解决
基于clipboard
api是无法稳定实现上述这个简单的复制链接功能.
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
的控制板支持一个新特性, 在渲染中模拟页面聚焦
.
六. 小结
复制, 粘贴, 简单但不简单.