首页 > 解决方案 > 实时视觉插入排序有间隙,输出延迟

问题描述

好的,所以我对 JS 比较陌生,但对 python 和 java 有丰富的经验。

我的代码有 2 个问题需要帮助。首先是我的代码的解释和背景。

理想情况下,我想要最简单的结构化视觉排序程序,我可以将其用作我的编码进展的基础以供参考。我首先最大化一个容器 div,该容器 div 用于填充 x 数量的 div .bars,div 的宽度和位置在插入时由 flexbox 自动处理。每个添加的 div 的高度是随机生成的,并存储在每个单独的元素属性中。我已经完成了所有这些,很容易。然后我做了一个元素交换器函数,它在 w DOM 中交换元素位置,效果很好。现在我的问题。我希望看到元素在 for 循环迭代时实时排序,但在循环结束之前它们不会更新。而且我找不到任何错误的我的插入算法也无法正常工作,但我认为我的功能方法是错误的。任何帮助将不胜感激。应该很容易为别人弄清楚。

const sortDisplay = document.getElementById('sortDisplay');
let resetbtn = document.querySelector('.reset');
resetbtn.addEventListener('click', reset);

let count = 0;
let amount = 100;

// create div that has custom attribute value, unique style tag, default bar style and append.
function generateBar() {
  // generate div
  let bar = document.createElement('div');
  // keep track of the total amount of bars
  count++;
  // assign random number 0-100 and setAttribute to the div
  let temp = Math.floor(Math.random() * 101);
  // create custom attribute that holds its value
  bar.setAttribute('value', temp);
  // create unique style tag with height as a percentage based on Attribute
  let barHeight = document.createElement('style');
  barHeight.innerHTML = `.barHeight${count} {height: ${temp}%;}`;
  // add that unique style to the DOM
  sortDisplay.appendChild(barHeight);
  // now add that unique style to the div
  bar.classList.add(`barHeight${count}`);
  // use standard style from css as well
  bar.classList.add('sortBar');
  // now add that div to the DOM
  sortDisplay.appendChild(bar);
}

// clear container div and regenerate
function reset() {
  // clear all data within the container
  sortDisplay.innerHTML = '';
  // reset the count
  count = 0;
  // generate k bars
  for (let i = 0; i < amount; i++) {
    generateBar();
  }
}

// when page is loaded reset
reset(amount);

// swap elements within the DOM
function swapElements(obj1, obj2) {
  // create marker element and insert it above where obj1 is
  var temp = document.createElement("div");
  obj1.parentNode.insertBefore(temp, obj1);
  // move obj1 to right before obj2
  obj2.parentNode.insertBefore(obj1, obj2);
  // move obj2 to right before where obj1 used to be
  temp.parentNode.insertBefore(obj2, temp);
  // remove temporary marker node
  temp.parentNode.removeChild(temp);
}

// sort the divs within the DOM
function sort() {
  for (let i = 1; i < amount; i++) {
    let j = i;
    for (j; j > 0; j--) {
      if (document.querySelectorAll('.sortBar')[j].getAttribute('value') < document.querySelectorAll('.sortBar')[j-1].getAttribute('value')) {
        swapElements(document.querySelectorAll('.sortBar')[j], document.querySelectorAll('.sortBar')[j-1]);
      }
      else {
        break;
      }
    }
  }
}

// Button to run the sort function
button = document.querySelector('.button');
button.addEventListener('click', sort);
* {
  box-sizing: border-box;
  margin: 0;
  padding: 0;
}

body {
  height: 100vh;
  width: 100%;
}

.sortDisplay {
  background-color: #305c50;
  background-image: linear-gradient(28deg, #305c50 0%, #6ab19e 70%, #82d8a6 100%);
  display: flex;
  align-items: flex-end;
  justify-content: space-between;
  height: 100%;
  width: 100%;
  overflow: hidden;
}

.btn-container {
  position: absolute;
  right: 0;
  bottom: 0;
  margin: 25px;
}

.btn-container button {
  padding: 25px;
}

.sortBar {
  flex: 1;
  background-color: #0007;
}
<div class="btn-container">
  <button class="reset">reset</button>
  <button class="button">button</button>
</div>

<div id="sortDisplay"class="sortDisplay"></div>

标签: javascripthtmlcsssortingdom

解决方案


该错误非常小:您正在比较字符串,而不是排序中的数字。添加数字转换:

if (+document.querySelectorAll('.sortBar')[j].getAttribute('value') < +document.querySelectorAll('.sortBar')[j-1].getAttribute('value')) {

作为字符串,例如"3" > "29".

可视化不是“实时可见的”,因为代码完成得非常快,而且无需解除控制并等待 DOM 重新渲染。强制 DOM 重新渲染有一些小问题,但我认为这在这里并不重要。

要解决此问题,请在循环中添加延迟,这非常简单,您只需要一个规范的延迟函数 ( const delay = ms => new Promise(res => setTimeout(res, ms));),在您的( )async前面,以及交换之间的适当延迟 ( ,目前为 25 毫秒。确切的时间是不能保证,比如一次需要22ms,下一次可能需要27ms,由于各种因素,但这不是太重要)。sortasync function sortawait delay(DELAY_BETWEEN_SWAPS);

但是,这会导致取消问题:现在可以重置,而排序仍在继续(异步性的困境)。因此,有必要检查当前排序是否已取消。因此,每个排序过程都需要一个取消令牌,可用于在按下重置时停止旧排序。最后但最不重要的是,开始一个新的排序(只需按下“按钮”),也会自动取消最后一个排序。

请注意,我展示了一些概念,但不一定是能够赢得美容奖的代码。我也没有改变任何“有效但我不会那样做”的东西——例如,我更喜欢画布而不是做一个带有大量 DOM 更新的动画。

工作版本:

const sortDisplay = document.getElementById('sortDisplay');
let resetbtn = document.querySelector('.reset');
resetbtn.addEventListener('click', reset);

const DELAY_BETWEEN_SWAPS = 25;

const delay = ms => new Promise(res => setTimeout(res, ms));

let cancelLast = () => {};

let count = 0;
let amount = 100;

// create div that has custom attribute value, unique style tag, default bar style and append.
function generateBar() {
  // generate div
  let bar = document.createElement('div');
  // keep track of the total amount of bars
  count++;
  // assign random number 0-100 and setAttribute to the div
  let temp = Math.floor(Math.random() * 101);
  // create custom attribute that holds its value
  bar.setAttribute('value', temp);
  // create unique style tag with height as a percentage based on Attribute
  let barHeight = document.createElement('style');
  barHeight.innerHTML = `.barHeight${count} {height: ${temp}%;}`;
  // add that unique style to the DOM
  sortDisplay.appendChild(barHeight);
  // now add that unique style to the div
  bar.classList.add(`barHeight${count}`);
  // use standard style from css as well
  bar.classList.add('sortBar');
  // now add that div to the DOM
  sortDisplay.appendChild(bar);
}

// clear container div and regenerate
function reset() {
  cancelLast();
  // clear all data within the container
  sortDisplay.innerHTML = '';
  // reset the count
  count = 0;
  // generate k bars
  for (let i = 0; i < amount; i++) {
    generateBar();
  }
}

// when page is loaded reset
reset(amount);

// swap elements within the DOM
function swapElements(obj1, obj2) {
  // create marker element and insert it above where obj1 is
  var temp = document.createElement("div");
  obj1.parentNode.insertBefore(temp, obj1);
  // move obj1 to right before obj2
  obj2.parentNode.insertBefore(obj1, obj2);
  // move obj2 to right before where obj1 used to be
  temp.parentNode.insertBefore(obj2, temp);
  // remove temporary marker node
  temp.parentNode.removeChild(temp);
}

// sort the divs within the DOM
async function sort(cancellationChecker) {
  for (let i = 1; i < amount; i++) {
    let j = i;
    for (j; j > 0; j--) {
      if (cancellationChecker()) return;
      if (+document.querySelectorAll('.sortBar')[j].getAttribute('value') < +document.querySelectorAll('.sortBar')[j-1].getAttribute('value')) {
        swapElements(document.querySelectorAll('.sortBar')[j], document.querySelectorAll('.sortBar')[j-1]);
        await delay(DELAY_BETWEEN_SWAPS);
      }
      else {
        break;
      }
    }
  }
}

function btnSort() {
  let cancelled = false;
  cancelLast();
  cancelLast = () => cancelled = true;
  sort(() => cancelled);
}

// Button to run the sort function
button = document.querySelector('.button');
button.addEventListener('click', btnSort);
* {
  box-sizing: border-box;
  margin: 0;
  padding: 0;
}

body {
  height: 100vh;
  width: 100%;
}

.sortDisplay {
  background-color: #305c50;
  background-image: linear-gradient(28deg, #305c50 0%, #6ab19e 70%, #82d8a6 100%);
  display: flex;
  align-items: flex-end;
  justify-content: space-between;
  height: 100%;
  width: 100%;
  overflow: hidden;
}

.btn-container {
  position: absolute;
  right: 0;
  bottom: 0;
  margin: 25px;
}

.btn-container button {
  padding: 25px;
}

.sortBar {
  flex: 1;
  background-color: #0007;
}
<div class="btn-container">
  <button class="reset">reset</button>
  <button class="button">button</button>
</div>

<div id="sortDisplay"class="sortDisplay"></div>


推荐阅读