在ios Safari, 为了避免恶意网站攻击剪贴板,
使用navigator.clipboard API 必须是被用户交互直接触发(比如pointerdown 或pointerup), 且仅适用于在安全上下文中提供的内容(如https://)
复制异步获取的数据是不允许的. 同样, 也不能在用户交互触发的Promise中异步使用它
NotAllowedError: the request is not allowed by the user agent or the
platform in the current context, possibly because the user denied
permission.
解决办法是不要在异步的上下文调用Clipboard API, 而是反过来给Clipboard API一个异步Promise,这样, Clipboard API 就算是在通过用户交互直接触发的同步上下文中调用的, Clipboard API会在内部处理Promise请求
const text = new ClipboardItem({
"text/plain": fetch(this.sourceUrlValue)
.then(response => response.text())
.then(text => new Blob([text], { type: "text/plain" }))
})
navigator.clipboard.write([text])
Firefox 已经支持了 Clipboard API, 但是ClipboardItem和navigator.clipboard.write 函数的优先级在 Firefox上低于 dom.events.asyncClipboard.clipboardItem , 且这个是默认关闭的. 你需要手动打开它, 但是navigator.clipboard.writeText在Firefox上目前是支持的
这个方法只接受字符串作为参数,它不能像navigator.clipboard.write一样处理Promise, 也不受类似Safari的安全限制
因此最终兼容性的写法如下
if (typeof ClipboardItem && navigator.clipboard.write) {
const text = new ClipboardItem({
'text/plain': fetchOrigin()
.then((res: string) => new Blob([res], { type: 'text/plain' })),
});
navigator.clipboard.write([text]).then(() => {
console.log("复制成功")
}, () => {
console.log("复制成功")
});
} else {
fetchOrigin().then((res: string) => {
navigator.clipboard.writeText(res);
console.log("复制成功")
}, () => {
console.log("复制成功")
});
}
参考文章:
https://wolfgangrittner.dev/how-to-use-clipboard-api-in-safari/
https://stackoverflow.com/questions/62327358/javascript-clipboard-api-safari-ios-notallowederror-message
https://webkit.org/blog/10247/new-webkit-features-in-safari-13-1/