Reputation: 925
I need to show a user some questions in a loop, and while displaying a question there needs to be displayed a countdown timer like this one:
function progress(timeleft, timetotal, $element) {
var progressBarWidth = timeleft * $element.width() / timetotal;
$element.find('div').animate({ width: progressBarWidth }, timeleft == timetotal ? 0 : 1000, 'linear');
if(timeleft > 0) {
setTimeout(function() {
progress(timeleft - 1, timetotal, $element);
}, 1000);
}
};
$('document').ready(function() {
$('#start').click(function() {
progress(5, 5, $('#progressBar'));
});
});
#progressBar {
width: 90%;
margin: 10px auto;
height: 22px;
background-color: #0A5F44;
}
#progressBar div {
height: 100%;
text-align: right;
padding: 0 10px;
line-height: 22px; /* same as #progressBar height if we want text middle aligned */
width: 100%;
background-color: #CBEA00;
box-sizing: border-box;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.0.3/jquery.min.js"></script>
<button id='start'>Start</button>
<div id="progressBar">
<div></div>
</div>
The problem is: If I activate the timer (press the start button in the fiddle) while it's already started and going down, it starts to do crazy things: going up and down.
I guess this has to do with the recursive nature of setTimeout()
.
How do I reinitialize the timer (progressBar function) each time anew?
Upvotes: 0
Views: 65
Reputation: 6646
Store the timeout in a variable and clear it when you want to start over.
var animating = null;
function progress(timeleft, timetotal, $element) {
var progressBarWidth = timeleft * $element.width() / timetotal;
$element.find('div').animate({ width: progressBarWidth }, timeleft == timetotal ? 0 : 1000, 'linear');
if(timeleft > 0) {
clearTimeout(animating);
animating = setTimeout(function() {
progress(timeleft - 1, timetotal, $element);
}, 1000);
}
};
$('document').ready(function() {
$('#start').click(function() {
progress(5, 5, $('#progressBar'));
});
});
#progressBar {
width: 90%;
margin: 10px auto;
height: 22px;
background-color: #0A5F44;
}
#progressBar div {
height: 100%;
text-align: right;
padding: 0 10px;
line-height: 22px; /* same as #progressBar height if we want text middle aligned */
width: 100%;
background-color: #CBEA00;
box-sizing: border-box;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.0.3/jquery.min.js"></script>
<button id='start'>Start</button>
<div id="progressBar">
<div></div>
</div>
Upvotes: 1
Reputation: 426
You have to clear the timeout. Like this:
function progress(timeleft, timetotal, $element) {
var progressBarWidth = timeleft * $element.width() / timetotal;
$element.find('div').animate({ width: progressBarWidth }, timeleft == timetotal ? 0 : 1000, 'linear');
if(timeleft > 0) {
if (window.timer) {
clearTimeout(window.timer)
}
window.timer = setTimeout(function() {
progress(timeleft - 1, timetotal, $element);
}, 1000);
}
};
$('document').ready(function() {
$('#start').click(function() {
progress(5, 5, $('#progressBar'));
});
});
#progressBar {
width: 90%;
margin: 10px auto;
height: 22px;
background-color: #0A5F44;
}
#progressBar div {
height: 100%;
text-align: right;
padding: 0 10px;
line-height: 22px; /* same as #progressBar height if we want text middle aligned */
width: 100%;
background-color: #CBEA00;
box-sizing: border-box;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.0.3/jquery.min.js"></script>
<button id='start'>Start</button>
<div id="progressBar">
<div></div>
</div>
Upvotes: 3