Search code examples
javascripthtmlbootstrap-4

JavaScript progress bar unhides but won't update


I want a progress bar to unhide when the button is pressed and then to update. However, with the following code the progress bar shows but does not update.

If I remove style="display: none" the bar updates as expected. Any ideas?

document.getElementById("submit-btn").addEventListener("click", function() {
  document.getElementById("progress-top").style.display = 'block';

  var i = 0;
  if (i == 0) {
    i = 1;
    var width = 1;
    var id = setInterval(frame, 50);

    function frame() {
      if (width >= 100) {
        clearInterval(id);
        i = 0;
      } else {
        width++;
        document.getElementsByClassName('progress-bar').item(0).setAttribute('aria-valuenow', width);
        document.getElementsByClassName('progress-bar').item(0).setAttribute('style', 'width:' + Number(width) + '%');
      }
    }
  }


})
<div class="progress" style="display: none" id="progress-top">
  <div class="progress-bar" role="progressbar" aria-valuenow="" aria-valuemin="0" aria-valuemax="100">
  </div>
</div>

I'm using Bootstrap 4 for the CSS

.progress {
  display: flex;
  height: $progress-height;
  overflow: hidden; // force rounded corners by cropping it
  font-size: $progress-font-size;
  background-color: $progress-bg;
  @include border-radius($progress-border-radius);
  @include box-shadow($progress-box-shadow);
}

.progress-bar {
  display: flex;
  flex-direction: column;
  justify-content: center;
  color: $progress-bar-color;
  text-align: center;
  background-color: $progress-bar-bg;
  @include transition($progress-bar-transition);
}

Solution

  • Consider using the <progress> element were you just need to se the value.

    To prevent starting the loop twice, consider checking the PID.

    let id = null;
    
    document.getElementById("submit-btn").addEventListener("click", function() {
    
        document.getElementById("progress-top").style.display = 'block';
           
        if (id === null) {
            var val = 1;
            id = setInterval(frame, 50);
    
            function frame() {
                if (val >= 100) {
                    clearInterval(id);
                    id = null;
                } else {
                    val++;
                    document.getElementById('progress').setAttribute('value', val);
                }
            }
        }
    })
    .progress {
        height: 150px;
    }
    <div class="progress" style="display: none" id="progress-top">
        <progress id="progress" value="0" max="100">0%</progress>
    </div>
    
    <button id='submit-btn'>Click me</button>


    Regarding the Bootstap variant, you'll need to set both style.width and innerHTML.

    The aria-valuenow doesn't need to be updated.

    let id = null;
    
    document.getElementById("submit-btn").addEventListener("click", function() {
    
        document.getElementById("progress-top").style.display = 'block';
           
        if (id === null) {
            var val = 1;
            id = setInterval(frame, 50);
    
            function frame() {
                if (val >= 100) {
                    clearInterval(id);
                    id = null;
                } else {
                    val++;
                    document.getElementsByClassName('progress-bar')[0].style.width = val + '%';
                    document.getElementsByClassName('progress-bar')[0].innerHTML = val + '%';
                }
            }
        }
    })
    .progress {
        height: 150px;
        width: 200px;
    }
    <script src="https://cdn.jsdelivr.net/npm/bootstrap@5.2.3/dist/js/bootstrap.min.js"></script>
    <link href="https://cdn.jsdelivr.net/npm/bootstrap@5.2.3/dist/css/bootstrap.min.css" rel="stylesheet"/>
    
    <div class="progress" id="progress-top" style="display: none">
      <div class="progress-bar" role="progressbar" aria-valuenow="0" aria-valuemin="0" aria-valuemax="100"></div>
    </div>
    
    <button id='submit-btn'>Click me</button>