JS динамическое изменение ширины divs в цикле


Я хочу создать простую гистограмму с анимированными барами (ширина от 0 до конечного размера).

Я начал с CSS-переходов, но у opera и chrome иногда возникают проблемы.

Теперь я пытаюсь использовать механизм, который я видел здесь:

Https://www.w3schools.com/w3css/w3css_progressbar.asp

Использование JS.

У меня есть несколько "бар-областей":

  <div id="chart-bars">
    <div class="chart-bar-area">
        <div class="chart-bar" data-percent="80"  ></div>

    </div>

        <div class="chart-bar-area">
        <div class="chart-bar" data-percent="60"   ></div>
    </div>
  </div>

И JS-код, который прекрасно работает с одним баром, но если я попытаюсь реализовать этот механизм для всех в цикле этот скрипт корректно работает только для последнего бара. Другие бары не канавки.

Мой код JS ниже:

  var foo = document.getElementById('chart-bars');
  var width;
  var elem;
  var id;
  for (var i = 0; i < foo.children.length; i++) {

   elem = foo.children[i].children[0];

     width = 1;
     id = setInterval(frame, 10);
    function frame() {
      if (width >= elem.dataset.percent) {
        clearInterval(id);
      } else {
        width++; 
        elem.style.width = width + '%'; 
      }
    }

  }

Может кто-нибудь помочь мне ?

Спасибо

5 2

5 ответов:

Это связано с тем, что значение i находится вне области обратного вызова setInterval, setInterval принимает только последнее значение итерации, поскольку это метод window. Таким образом, вы должны пойти с закрытием или решением, как это.

    var foo = document.querySelectorAll(".chart-bar");
    var width;
    var elem;
    var id;
    for (var i = 0; i < foo.length; i++) {

        var myElem = {
            x: i
        }

        width = 1;
        id = setInterval(function() {
            if (width >= foo[this.x].dataset.percent) {
                clearInterval(id);
            } else {
                width++;
                foo[this.x].style.width = width + '%';
            }
        }.bind(myElem), 10);//Here you are binding that setInterval should run on the myElem object context not window context

   }

Почему бы вам не использовать специальные библиотеки jQuery для создания диаграмм, таких как morrischart или chartjs, они очень просты и работают очень хорошо есть документы http://morrisjs.github.io/morris.js/ https://www.chartjs.org

Похоже на проблему с областью видимости переменной, не могли бы вы попробовать что-то вроде этого:

var foo = document.getElementById('chart-bars');
for (var i = 0; i < foo.children.length; i++) {

   var elem = foo.children[i].children[0];
   elem.style.width = '1%';

   var id = setInterval(function() { frame(elem, id) }, 10);
}

function frame(elem, idInterval) {
   var width = parseInt(elem.style.width);
   if (width >= elem.dataset.percent) {
     clearInterval(idInterval);
   } else {
     width++; 
     elem.style.width = width + '%'; 
   }
}

В основном до того, как вы стирали в каждом цикле свои предыдущие переменные elem, width и id, потому что они были объявлены вне цикла for. Отсюда и странное поведение.

Edit: поместите функцию фрейма снаружи, чтобы она была более четкой.

Edit2: удаление ширины из функции фрейма, поскольку она не нужна.

Поздравляю! Сегодня мы узнаем, что такое" закрытие".

Вы столкнулись с проблемой определения области действия. Причина, по которой это работает только для последнего бара, заключается в том, что функция frame видит только elem как это в настоящее время существует на scope. То есть к тому времени, когда ваш setInterval работает, ваш цикл закончится и elem будет твердо установлен на то, что составляет foo.children[foo.children.length - 1].children[0]

Способ исправить это-создать новое закрытие. То есть, вы "закрываете" переменную в a масштаб.

var foo = document.getElementById('chart-bars');

for (var i = 0; i < foo.children.length; i++) {
  (function (elem, width) {
    var id = setInterval(frame, 10);
    function frame() {
      if (width >= elem.dataset.percent) {
        clearInterval(id);
      } else {
        width++; 
        elem.style.width = width + '%'; 
      }
    }
  })(foo.children[i].children[0], 1)
}
Это, в частности, далеко не идеальный способ достижения вашей цели, но я написал его таким образом, чтобы сохранить как можно больше вашего кода в попытке уменьшить когнитивную нагрузку для вашего конкретного примера. По существу, то, что я делаю, - это обертывание вашего кода в IIFE (немедленно вызываемое выражение функции), которое создает новую область с вашими переменными elem и width, закрепленными в области этой функции.

Дальнейший шаг в правильным направлением было бы вытащить вашу функцию frame за пределы цикла и создать ее только один раз, а затем принять в качестве аргументов elem, width, и id параметр, но я оставлю это в качестве упражнения для читателя : -)

Большое спасибо, Пример AB Udhay работает довольно хорошо! Но....

Когда у меня есть HTML:

<div id="chart-bars">
    <div class="chart-bar-area">
        <div class="chart-bar" data-percent="80"  data-toggle="tooltip" title="Hooray!" data-placement="right"></div><div class="chart-bar-percent"> 0%</div>

    </div>

        <div class="chart-bar-area">
        <div class="chart-bar" data-percent="60"   data-toggle="tooltip" title="Hooray!" data-placement="right"></div><div class="chart-bar-percent"> 0%</div>

    </div>
    <div class="chart-bar-area">
        <div class="chart-bar" data-percent="30"   data-toggle="tooltip" title="Hooray!" data-placement="right"></div><div class="chart-bar-percent"> 0%</div>

    </div>

        <div class="chart-bar-area">
        <div class="chart-bar" data-percent="40" data-toggle="tooltip" title="Hooray!" data-placement="right"></div><div class="chart-bar-percent"> 0%</div>

    </div>
        <div class="chart-bar-area">
        <div class="chart-bar" data-percent="10" data-toggle="tooltip" title="Hooray!" data-placement="right"></div><div class="chart-bar-percent"> 0%</div>

    </div>

        <div class="chart-bar-area">
        <div class="chart-bar" data-percent="1" data-toggle="tooltip" title="Hooray!" data-placement="right"></div><div class="chart-bar-percent"> 0%</div>

    </div>
  </div>

И JS вот так:

    var foo = document.querySelectorAll(".chart-bar");
    var width;
    var elem;
    var id;
    for (var i = 0; i < foo.length; i++) {

        var myElem = {
            x: i
        }

        width = 0;
        id = setInterval(function() {
            if (width >= foo[this.x].dataset.percent) {
                clearInterval(id);
            } else {
                width++;
                foo[this.x].style.width = width + '%';
                foo[this.x].nextSibling.innerHTML = width + '%';
            }
        }.bind(myElem), 1); 
   }

Затем я даю некоторый случайный resoult, как показано ниже...

Введите описание изображения здесь

Почему ?