问题
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