为什么DOM元素仅在事件侦听器完成后才更新? (纯js)

问题描述 投票:0回答:2

我正在使用javascript制作一个程序,该程序将在一段时间后更改元素的CSS样式(在这种情况下为背景色),类似于交通灯。

问题是这样的:单击按钮时,将进入按钮的onclick侦听器功能。在该函数中,有for循环遍历每个单元格并更改其颜色。每次通过循环应该更新单元格颜色之一,但不是。实际发生的是在按钮的onclick侦听器功能完成后,所有单元格都将更新。

这里是我制作的https://jsfiddle.net/lilweege/4291vjto/6/的简短版本的链接

let main = document.getElementById('main')

// create 'cells' which are just empty divs (refer to css styles above)
let cells = []
for (let x = 0, numCells = 15; x < numCells; x++) {
  let cell = document.createElement("DIV")
  cell.setAttribute('class', 'cell')
  cells.push(cell)
  main.appendChild(cell)
}


// create button to run main logic
const run = document.createElement("BUTTON")
run.innerHTML = 'change colors'
run.addEventListener('click', function() {
  console.log('starting program');
  // reset all cell colors
  for (let cell of cells) {
    cell.style.background = 'white'
  }
  for (let cell of cells) {
    // change color of cell
    cell.style.background = `hsl(${cells.indexOf(cell) * (360 / cells.length)}, 100%, 50%)`
    // halt program for 500ms
    sleep(100)
  }
  console.log('done');
})
main.appendChild(run)

// sleep function halts entire program during period of ms
function sleep(milliseconds) {
  console.log(`waiting ${milliseconds} milliseconds`);
  const start = Date.now();
  let current = null;
  do {
    current = Date.now();
  } while (current - start < milliseconds);
}
.main {
  display: flex;
}

.cell {
  width: 20px;
  height: 20px;
  border: 1px solid black;
  margin: 1px;
}
<div id="main" class="main"></div>

[添加元素,更改某些其他属性(例如innerHTML或DOM中的其他更改时,也是这种情况。

另外,我也不怀疑我的'sleep'函数是问题所在,因为它所做的只是暂停浏览器中的整个程序,直到经过了毫秒的时间(它调用Date.now()直到当前时间和开始时间大于传入的数量。

任何指针或解决方案将不胜感激,谢谢!

javascript html css dom
2个回答
2
投票

您的sleep功能问题。它半永久运行blocking循环。当前选项卡(或框架)的所有资源都将用于解决该循环,因此一旦完成,事件循环就可以继续并开始渲染。但是,如果嵌套循环(和事件侦听器)直到10秒钟后才解决,浏览器甚至在10秒钟后甚至无法start更新显示。

等待使用setTimeout的Promise-setTimeout不会阻塞处理或渲染,不像经过数万次迭代的循环:

const sleep = ms => new Promise(res => setTimeout(res, ms));
const main = document.getElementById('main')
const cells = []
for (let x = 0, numCells = 20; x < numCells; x++) {
  const cell = document.createElement("DIV")
  cell.setAttribute('class', 'cell')
  cells.push(cell)
  main.appendChild(cell)
}
const run = document.createElement("BUTTON")
run.innerHTML = 'change colors'
run.addEventListener('click', async function() {
  for (let cell of cells) {
    cell.style.background = `hsl(${cells.indexOf(cell) * (360 / cells.length)}, 100%, 50%)`
    await sleep(500)
  }
  console.log('done');
})
main.appendChild(run)
.main {
  display: flex;
}
.cell {
  width: 20px;
  height: 20px;
  border: 1px solid black;
  margin: 1px;
}
<div id="main" class="main"></div>

几乎[有充分的理由使用while循环来等待Date.now()达到阈值-这在计算上非常昂贵,对用户不友好(因为框架无法与之交互)期间),并且可能会产生意想不到的结果(例如您在这里遇到的情况)。


0
投票

CertainPerformance响应速度比我快,而我正在同一边努力。

我刚刚对代码做了一些改进,我也认为最好在上色之前睡个好觉。

const main = document.getElementById('main') , cells = [] , numCells = 16 , run = document.createElement('button') , sleep = ms => new Promise(res => setTimeout(res, ms)) ; let InOut = 0 // to reverse colors order ; run.textContent = 'set colors' ; for (let x=0; x < numCells; x++) // create 'cells' which are just empty divs (refer to css above) { cells[x] = main.appendChild(document.createElement('div')) } main.appendChild(run) // button to run main logic ; run.onclick = async _=> { for (let cell of cells) { cell.style.backgroundColor = null } for (let x in cells) { await sleep(200) cells[x].style.backgroundColor = `hsl(${(Math.abs(InOut-x)) *(360 /numCells)}, 100%, 50%)` } InOut = InOut ? 0 : (numCells-1) }
#main {
  display: flex;
}
#main div   {
  display: inline-block;
  width: 1.2em;
  height: 1.2em;
  border: 1px solid black;
  margin: .1em;
  background-color: white;
}
#main button   {
  margin-left:.3em
}
<div id="main"></div>
© www.soinside.com 2019 - 2024. All rights reserved.