I have this JS code for a countdown progress bar that should take a time value and decrease until time is reached, then display EXPIRED.
function progress(timeleft, timetotal, $element) {
var bar = document.getElementById("#progressBar")
var progressBarWidth = (timeleft * bar.width()) / timetotal
console.log("width is" + bar.width() + "time left is" + timeleft)
$element.find("div").animate({
width: progressBarWidth
}, timeleft == timetotal ? 0 : 1000, "linear")
if (timeleft > 0) {
setTimeout(function() {
progress(timeleft - 1, timetotal, $element)
}, 1000)
}
}
progress(180, 180, $("#progressBar"))
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<div id="progressBar">
<div></div>
</div>
Problem is that here I set it to 3min for testing and bar doesn't decrease. I've debugged via console and 'bar.width()' seems to be undefined. Any ideas how to fix it? Thanks!
You are already passing in the $element
, which IS bar
.
function progress(timeleft, timetotal, $element) {
var progressBarWidth = (timeleft * $element.width()) / timetotal
console.log(`width: ${$element.width()} px | time left: ${timeleft} sec`)
$element.find("div").animate({
width: progressBarWidth
}, timeleft == timetotal ? 0 : 1000, "linear")
if (timeleft > 0) {
setTimeout(progress, 1000, timeleft - 1, timetotal, $element)
}
}
progress(60, 60, $("#progressBar"))
#progressBar div {
background: green;
height: 1em;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<div id="progressBar">
<div></div>
</div>
Note: You can invoke setTimeout
without creating a nested function call. The parameters following the timeout (2nd param) will be passed into the callback function.
Replace this:
if (timeleft > 0) {
setTimeout(function() {
progress(timeleft - 1, timetotal, $element)
}, 1000)
}
With this:
if (timeleft > 0) {
setTimeout(progress, 1000, timeleft - 1, timetotal, $element)
}
Here is a jQuery plugin version
(($) => {
const init = ($bar) => {
if ($bar.find('div').length === 0) $bar.append($('<div>'));
}
const run = ($bar, duration, timeRemaining, callback) => {
update($bar, duration, timeRemaining)
if (timeRemaining > 0) {
setTimeout(tick, 1000, $bar, duration, timeRemaining, callback)
} else {
callback()
}
}
const update = ($bar, duration, timeRemaining) => {
const width = (timeRemaining * $bar.width()) / duration
$bar.find('div').animate({
width: width
}, timeRemaining == duration ? 0 : 1000, 'linear')
}
const tick = ($bar, duration, timeRemaining, callback) => {
run($bar, duration, timeRemaining - 1, callback)
}
$.fn.progress = function(duration, timeRemaining, callback) {
init(this)
run(this, duration, timeRemaining, callback);
return this
}
})(jQuery);
$('#progress-bar-1').progress(10, 10, () => {
console.log('Task #1 completed!')
})
$('#progress-bar-2').progress(5, 5, () => {
console.log('Task #2 completed!')
})
div[id^="progress-bar"] div {
background: green;
height: 1em;
margin-bottom: 0.5em;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<div id="progress-bar-1"></div>
<div id="progress-bar-2"></div>