为什么我复制的时候背景没有改变?我添加了一个console.log()
,正如你所看到的,console.log()
工作,但背景不会改变。这里的问题是什么?
要测试,单击代码片段,然后按CMD + C
(Windows:CTRL + C
)
window.addEventListener('copy', function(e) {
e.preventDefault();
//This should change!
document.getElementById("object").style.backgroundColor = 'white';
console.log("Started!");
tryCopyAsync(e).then(() =>
document.getElementById("object").style.backgroundColor = 'gray'
);
});
async function tryCopyAsync(e){
if(navigator.clipboard){
await e.clipboardData.setData('text/plain',getText());
}
}
function getText(){
var html = '';
var row = '<div></div>';
for (i=0; i<100000; i++) {
html += row;
}
return html;
}
#object{
width:100%;
height:100vh;
background:gray;
}
body{
padding:0;
margin:0;
overflow:hidden;
}
<div id='object'></div>
首先,您的sleepFor
方法完全同步地阻塞了事件循环,即使是从async
函数调用:
window.addEventListener('copy', function(e) {
e.preventDefault();
//This should change!
document.getElementById("object").style.backgroundColor = 'white';
console.log("Started!");
tryCopyAsync(e).then(() =>
document.getElementById("object").style.backgroundColor = 'gray'
);
console.log('sync');
});
async function tryCopyAsync(e){
if(navigator.clipboard){
await e.clipboardData.setData('text/plain',getText());
}
}
function sleepFor(sleepDuration){
var now = new Date().getTime();
while(new Date().getTime() < now + sleepDuration){}
}
function getText(){
console.log('blocking');
var html = '';
var row = '<div></div>';
for (i=0; i<10; i++) {
html += row;
sleepFor(300);
}
console.log('stopped blocking');
return html;
}
onclick = e => document.execCommand('copy');
#object{
width:100%;
height:100vh;
background:gray;
}
body{
padding:0;
margin:0;
overflow:hidden;
}
click to trigger the function
<div id='object'></div>
但是即使它在微任务中被调用,也不会改变什么,因为微任务也会阻塞事件循环。(阅读链接的答案,它解释了如何将呈现绑定到事件循环)。
如果你想让你的代码让浏览器做它的重绘,你需要让浏览器实际上循环事件循环,唯一的方法是:
- 拆分您的
getText
逻辑,并通过发布任务(例如通过setTimeout
)使其等待下一个事件循环迭代 - 使用一个专用的Worker来生成
getText
返回的数据。
但是要注意,您没有使用异步剪贴板API,而是简单地覆盖复制事件的默认值,这不能异步完成。这样的话,你需要用到剪贴板的API
下面是一个使用MessageChannel来发布任务的例子,因为当前稳定的Chrome仍然有1ms的setTimeout
最小延迟:
window.addEventListener('copy', function(e) {
e.preventDefault();
//This should change!
document.getElementById("object").style.backgroundColor = 'white';
console.log("Started!");
tryCopyAsync(e).then(() =>
document.getElementById("object").style.backgroundColor = 'gray'
);
});
async function tryCopyAsync(e) {
if (navigator.clipboard) { // you were not using the Clipboard API here
navigator.clipboard.writeText(await getText());
}
}
async function getText() {
var html = '';
var row = '<div></div>';
for (i = 0; i < 1000000; i++) {
if (i % 1000 === 0) { // proceed by batches of 1000
await waitNextFrame();
}
html += row;
}
return html;
}
function waitNextFrame() {
return new Promise(postTask);
}
function postTask(task) {
const channel = postTask.channel ||= new MessageChannel();
channel.port1.addEventListener("message", () => task(), {
once: true
});
channel.port2.postMessage("");
channel.port1.start();
}
onclick = (evt) => document.execCommand("copy");
#object {
width: 100%;
height: 100vh;
background: gray;
}
body {
padding: 0;
margin: 0;
overflow: hidden;
}
<div id='object'></div>
首先,"then"应该是一个函数
.then(()=>{})
从运行你的代码,它看起来像这样
document.getElementById("object").style.backgroundColor = 'gray';
在将背景色设置为白色后立即被调用,因此您无法注意到它变为白色,即使它(只是非常非常短暂地)。
尝试在tryCopyAsync函数中设置一些日志记录,看看为什么它完成得太快了。