How to gradually style elements that are being (bubble) sorted?

怎甘沉沦 提交于 2021-02-15 07:27:10

问题


I have several divs with random numbers on them that I latter arrange on an ascending order using some simple bubble sorting code, I would like to gradually arrange and add style to them instead of them being styled and arranged immediately.

This feels like something so simple yet I'm unable to find a way to sleep the for loop or use the setTimeout function properly... Also extra points if it highlights the two divs that are being currently compared.

Here's a working example of what I've got so far:

function bubbleSort(input) {
  var swapped;
  do {
    swapped = false;
    for (var i=0; i < input.length-1; i++) {
      var div1 = $('.divRandom').eq(i);
      var div2 = $('.divRandom').eq(i+1);

      if (input[i] > input[i+1]) {
        var temp = input[i];
        input[i] = input[i+1];
        input[i+1] = temp;
        arrangeDivs(div1, div2);
        swapped = true;
      }
    }
  } while (swapped);
}

function arrangeDivs(div1, div2){
  div1.before(div2);
  div1.removeClass('divUnsorted');
  div1.addClass('divSorted');
  div2.removeClass('divUnsorted');
  div2.addClass('divSorted');
}

$('.bubbleBtn').click(function() {
  var divArray = new Array();
  divArray = createArray(divArray);
  //console.log(divArray);
  bubbleSort(divArray);
  //console.log(divArray);
});

function createArray(divArray) {
  var divLength = $('.divUnsorted').length;
  for (var i = 0; i < divLength; i++){
    var divNumber = parseInt($('.divUnsorted').eq(i).text());
    divArray.push(divNumber)
  }
  return divArray;
}

$('.addDivBtn').click(function(){
  $('.divRandom').removeClass('divSorted');
  $('.divRandom').addClass('divUnsorted');
  var randomNumber = Math.floor((Math.random() * 1000) + 1);
  $('<div/>', {
    'class':'divRandom divUnsorted',
    'text':randomNumber,
  }).appendTo('.addDivRandom');

  $('.divRandom').addClass('divUnsorted');
});
.divRandom {
  display: inline-block;
  text-align: center;
  margin: 5px;
  padding: 10px;
  width: 100px;
  font-size: 20px;
}

.addDivRandom {
  text-align: center;
  margin: auto;
}

.divUnsorted {
  border: 2px solid green;
  background-color: #9db;
}

.divSorting {
  border: 2px solid darkred;
  background-color: #db9;
}

.divSorted {
  border: 2px solid darkblue;
  background-color: #9bd;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>

<div class="addDivRandom">
</div>
<button class="addDivBtn" style="display: block;">Add</button>
<button class="bubbleBtn" style="display: block;">Bubble</button>

回答1:


When you do a loop, the entire page will have to wait until it's finished before it regains control. This includes any changes you've done in the DOM, so the browser would wait for the entire execution to show you the results.

You can avoid that if you instead run each step of the sort, schedule the next step, then give control back to the browser and repeat. In this fashion you can have updates to the UI as you sort. To that effect, you can use setTimeout - you would schedule your function recursively until the sort operation finishes.

Your sorting function needs minor changes - there would be no loops, which in turn makes the swapped variable obsolete. You can keep track of the current index by passing it to each execution of bubbleSort. You complete the sort when there is nothing left to sort. I've commented out the code that is not needed to make clear the changes:

function bubbleSort(input, i = 0) {
  //nothing to sort
  if (input.length == 0) {
    console.log("sorting complete");
    return; 
  }
  //var swapped; -> not needed
  //do { -> not needed
    //swapped = false; -> not needed
    //for (var i=0; i < input.length-1; i++) { -> not needed
  var div1 = $('.divRandom').eq(i);
  var div2 = $('.divRandom').eq(i+1);

  if (input[i] > input[i+1]) {
    var temp = input[i];
    input[i] = input[i+1];
    input[i+1] = temp;
    arrangeDivs(div1, div2);
    //swapped = true; -> not needed
  }
    //} -> not needed - closes the for-loop 
    
    var next;
    if (i == input.length) {
      //loop back to the start
      next = 0;
      //exclude the last element - it's already sorted
      input = input.slice(0, -1)
    } else {
      //move the index
      next = i + 1
    }
    //schedule next step - you can change the delay to control the speed of the updates
    setTimeout(bubbleSort, 450, input, next)
  //} while (swapped); -> not needed
}

function arrangeDivs(div1, div2){
  div1.before(div2);
  div1.removeClass('divUnsorted');
  div1.addClass('divSorted');
  div2.removeClass('divUnsorted');
  div2.addClass('divSorted');
}

$('.bubbleBtn').click(function() {
  var divArray = new Array();
  divArray = createArray(divArray);
  //console.log(divArray);
  bubbleSort(divArray);
  //console.log(divArray);
});

function createArray(divArray) {
  var divLength = $('.divUnsorted').length;
  for (var i = 0; i < divLength; i++){
    var divNumber = parseInt($('.divUnsorted').eq(i).text());
    divArray.push(divNumber)
  }
  return divArray;
}

$('.addDivBtn').click(function(){
  $('.divRandom').removeClass('divSorted');
  $('.divRandom').addClass('divUnsorted');
  var randomNumber = Math.floor((Math.random() * 1000) + 1);
  $('<div/>', {
    'class':'divRandom divUnsorted',
    'text':randomNumber,
  }).appendTo('.addDivRandom');

  $('.divRandom').addClass('divUnsorted');
});
.divRandom {
  display: inline-block;
  text-align: center;
  margin: 5px;
  padding: 10px;
  width: 100px;
  font-size: 20px;
}

.addDivRandom {
  text-align: center;
  margin: auto;
}

.divUnsorted {
  border: 2px solid green;
  background-color: #9db;
}

.divSorting {
  border: 2px solid darkred;
  background-color: #db9;
}

.divSorted {
  border: 2px solid darkblue;
  background-color: #9bd;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>

<div class="addDivRandom">
</div>
<button class="addDivBtn" style="display: block;">Add</button>
<button class="bubbleBtn" style="display: block;">Bubble</button>

So, other than that - if you want to highlight the divs being sorted, here is what you can do - when you determine which two divs are being examined now, add the style divSorting to them. The next time you the function is executed, you remove all divSorting classes and highlight the next two. And when you loop back to re-start the counter, you can make the final element divSorted - it is already in its place.

function bubbleSort(input, i = 0) {
  //nothing to sort
  if (input.length == 0) {  
    return; 
  }
  var div1 = $('.divRandom').eq(i);
  var div2 = $('.divRandom').eq(i+1);
  //reset all styles - we are soring different divs now
  $("div").removeClass("divSorting");
  
  //highlight the ones that are being examined now
  div1.addClass('divSorting');
  div2.addClass('divSorting');

  if (input[i] > input[i+1]) {
    var temp = input[i];
    input[i] = input[i+1];
    input[i+1] = temp;
    arrangeDivs(div1, div2);
  }
    
    var next;
    if (i == input.length-1) {
      //loop back to the start
      next = 0;
      //exclude the last element - it's already sorted
      input = input.slice(0, -1);
      //mark the last div as sorted
      $('.divRandom').eq(i).addClass("divSorted");
    } else {
      //move the index
      next = i + 1
    }
    //schedule next step - you can change the delay to control the speed of the updates
    setTimeout(bubbleSort, 450, input, next)
}

function arrangeDivs(div1, div2){
  div1.before(div2);
}

$('.bubbleBtn').click(function() {
  var divArray = new Array();
  divArray = createArray(divArray);
  //console.log(divArray);
  bubbleSort(divArray);
  //console.log(divArray);
});

function createArray(divArray) {
  var divLength = $('.divUnsorted').length;
  for (var i = 0; i < divLength; i++){
    var divNumber = parseInt($('.divUnsorted').eq(i).text());
    divArray.push(divNumber)
  }
  return divArray;
}

$('.addDivBtn').click(function(){
  $('.divRandom').removeClass('divSorted');
  $('.divRandom').addClass('divUnsorted');
  var randomNumber = Math.floor((Math.random() * 1000) + 1);
  $('<div/>', {
    'class':'divRandom divUnsorted',
    'text':randomNumber,
  }).appendTo('.addDivRandom');

  $('.divRandom').addClass('divUnsorted');
});
.divRandom {
  display: inline-block;
  text-align: center;
  margin: 5px;
  padding: 10px;
  width: 100px;
  font-size: 20px;
}

.addDivRandom {
  text-align: center;
  margin: auto;
}

.divUnsorted {
  border: 2px solid green;
  background-color: #9db;
}

.divSorting {
  border: 2px solid darkred;
  background-color: #db9;
}

.divSorted {
  border: 2px solid darkblue;
  background-color: #9bd;
}nd-color: yellow;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>

<div class="addDivRandom">
</div>
<button class="addDivBtn" style="display: block;">Add</button>
<button class="bubbleBtn" style="display: block;">Bubble</button>



回答2:


One approach to this could be to change it to add a delay between each swap. The below logic has refactored the bubbleSort method to use an inner IIFE.

This IIFE performs a setTimeout of 0.5 seconds. Each execution it checks to see if an element should be swapped, and swaps them if it should. If the index is not at the end of the loop, it calls the IIFE for the next index to keep evaluating. Once it reaches the end of the loop, if a swap has happened, it calls bubbleSort again to start the process all over.

function bubbleSort(input) {
  var swapped = false;
  
  (function swapDivs ( index ){
    console.log( index );
    setTimeout(function(){
      if ( index < input.length - 1 ) {
        var $divs = $('.divRandom'),
            $div1 = $divs.eq( index ),
            $div2 = $divs.eq( index + 1 );
        
        if ( input[ index ] > input[ index + 1 ] ) {
          var temp = input[ index ];
          
          input[ index ] = input[ index + 1 ];
          input[ index + 1 ] = temp;
          
          arrangeDivs( $div1, $div2 );
          
          swapped = true;
        }
        
        swapDivs( index + 1 );
      }
      else if ( swapped ) {
        $('.divRandom.divSorted').toggleClass('divSorted divUnsorted');
        bubbleSort( input );
      }
    }, 500);
  })(0);
}

function arrangeDivs(div1, div2){
  div1.before(div2);
  div1.removeClass('divUnsorted');
  div1.addClass('divSorted');
  div2.removeClass('divUnsorted');
  div2.addClass('divSorted');
}

$('.bubbleBtn').click(function() {
  var divArray = new Array();
  divArray = createArray(divArray);
  //console.log(divArray);
  bubbleSort(divArray);
  //console.log(divArray);
});

function createArray(divArray) {
  var divLength = $('.divUnsorted').length;
  for (var i = 0; i < divLength; i++){
    var divNumber = parseInt($('.divUnsorted').eq(i).text());
    divArray.push(divNumber)
  }
  return divArray;
}

$('.addDivBtn').click(function(){
  $('.divRandom').removeClass('divSorted');
  $('.divRandom').addClass('divUnsorted');
  var randomNumber = Math.floor((Math.random() * 1000) + 1);
  $('<div/>', {
    'class':'divRandom divUnsorted',
    'text':randomNumber,
  }).appendTo('.addDivRandom');

  $('.divRandom').addClass('divUnsorted');
});
.divRandom {
  display: inline-block;
  text-align: center;
  margin: 5px;
  padding: 10px;
  width: 100px;
  font-size: 20px;
}

.addDivRandom {
  text-align: center;
  margin: auto;
}

.divUnsorted {
  border: 2px solid green;
  background-color: #9db;
}

.divSorting {
  border: 2px solid darkred;
  background-color: #db9;
}

.divSorted {
  border: 2px solid darkblue;
  background-color: #9bd;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>

<div class="addDivRandom">
</div>
<button class="addDivBtn" style="display: block;">Add</button>
<button class="bubbleBtn" style="display: block;">Bubble</button>


来源:https://stackoverflow.com/questions/56157185/how-to-gradually-style-elements-that-are-being-bubble-sorted

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!