2

I'm trying to increase some progress bar using setInterval. When I test, it's working fine.

var progressBar = $('.progress-bar');

var count = 0;

var interval = setInterval(function () {
    var width = progressBar.width();

    console.log(width++);

    progressBar.width(width);

    count++;

    if (count === 101) {
      clearInterval(interval);
    }
}, 50);
.progress-bar {
  width: 0px;
  height: 4px;
  background-color: #00f;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/2.2.4/jquery.min.js"></script>

<div class="progress-bar"></div>

Since I nest it into dropdown, I'm getting trouble with the time to repeat every action:

var increase = function (progressBar, ms) {
    var count = 0;

    var interval = setInterval(function () {
        var width = progressBar.width();

        console.log(width++);

        progressBar.width(width);

        count++;

        if (count === 21) {
          clearInterval(interval);
        }
    }, ms);
}

$('.dropdown').on('shown.bs.dropdown', function () {
    var progressBar = $(this).find('.progress-bar');
    
    increase(progressBar, +progressBar.data('ms'));
});
.progress-bar {
  width: 0px;
  height: 4px;
}
<link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.4.1/css/bootstrap.min.css" integrity="sha384-Vkoo8x4CGsO3+Hhxv8T/Q5PaXtkKtu6ug5TOeNV6gBiFeWPGFN9MuhOf23Q9Ifjh" crossorigin="anonymous">
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/2.2.4/jquery.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/popper.js@1.16.0/dist/umd/popper.min.js" integrity="sha384-Q6E9RHvbIyZFJoft+2mJbHaEWldlvI9IOYy5n3zV9zzTtmI3UksdQRVvoxMfooAo" crossorigin="anonymous"></script>
<script src="https://stackpath.bootstrapcdn.com/bootstrap/4.4.1/js/bootstrap.min.js" integrity="sha384-wfSDF2E50Y2D1uUdj0O3uMBJnjuUD4Ih7YwaYd1iqfktj0Uod8GCExl3Og8ifwB6" crossorigin="anonymous"></script>

<div class="dropdown d-inline-block">
  <button class="btn btn-secondary dropdown-toggle" type="button" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false">
    Progress bar with 100ms
  </button>
  <div class="dropdown-menu" >
    <a class="dropdown-item" href="#">
        <div class="progress-bar" data-ms="100"></div>    
    </a>
  </div>
</div>

<div class="dropdown d-inline-block">
  <button class="btn btn-secondary dropdown-toggle" type="button" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false">
    Progress bar with 300ms
  </button>
  <div class="dropdown-menu">
    <a class="dropdown-item" href="#">
        <div class="progress-bar" data-ms="300"></div>    
    </a>
  </div>
</div>

When I open the dropdown, the progress bar width will be increased. But there is something wrong when I try different values to repeat the action.

In the example, if I set the value to 100 or lower, I'll get the progress bar width wrongly. The default width value is 0, after increasing (by adding 1), why it's 0.15625 and then 0.3125...?

Another try, if I set the value to 300 or higher, I get the width correctly (0, 1, 2...)

Anyone knows why?

Tân
  • 1
  • 15
  • 56
  • 102

1 Answers1

1

Use css width property instead od jQuery width() method since width() may return unreliable data.

var increase = function (progressBar, ms) {
    var count = 0;
    var interval = setInterval(function () {
        var width = parseInt(progressBar[0].style.width) || 0;
        console.log(width++);
        progressBar[0].style.width = `${width}px`;
        count++;
        if (count === 21) {
          clearInterval(interval);
        }
    }, ms);
}

$('.dropdown').on('shown.bs.dropdown', function () {
    var progressBar = $(this).find('.progress-bar');
    
    increase(progressBar, +progressBar.data('ms'));
});
.progress-bar {
  width: 0px;
  height: 4px;
}
<link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.4.1/css/bootstrap.min.css" integrity="sha384-Vkoo8x4CGsO3+Hhxv8T/Q5PaXtkKtu6ug5TOeNV6gBiFeWPGFN9MuhOf23Q9Ifjh" crossorigin="anonymous">
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/2.2.4/jquery.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/popper.js@1.16.0/dist/umd/popper.min.js" integrity="sha384-Q6E9RHvbIyZFJoft+2mJbHaEWldlvI9IOYy5n3zV9zzTtmI3UksdQRVvoxMfooAo" crossorigin="anonymous"></script>
<script src="https://stackpath.bootstrapcdn.com/bootstrap/4.4.1/js/bootstrap.min.js" integrity="sha384-wfSDF2E50Y2D1uUdj0O3uMBJnjuUD4Ih7YwaYd1iqfktj0Uod8GCExl3Og8ifwB6" crossorigin="anonymous"></script>

<div class="dropdown d-inline-block">
  <button class="btn btn-secondary dropdown-toggle" type="button" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false">
    Progress bar with 100ms
  </button>
  <div class="dropdown-menu">
    <a class="dropdown-item" href="#">
      <div class="progress-bar" data-ms="100"></div>
    </a>
  </div>
</div>

<div class="dropdown d-inline-block">
  <button class="btn btn-secondary dropdown-toggle" type="button" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false">
    Progress bar with 300ms
  </button>
  <div class="dropdown-menu">
    <a class="dropdown-item" href="#">
      <div class="progress-bar" data-ms="300"></div>
    </a>
  </div>
</div>

Taken from docs:

Note that .width() will always return the content width, regardless of the value of the CSS box-sizing property. As of jQuery 1.8, this may require retrieving the CSS width plus box-sizing property and then subtracting any potential border and padding on each element when the element has box-sizing: border-box. To avoid this penalty, use .css( "width" ) rather than .width().

Pranav C Balan
  • 113,687
  • 23
  • 165
  • 188