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 ответов:
Это связано с тем, что значение
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 масштаб.
Это, в частности, далеко не идеальный способ достижения вашей цели, но я написал его таким образом, чтобы сохранить как можно больше вашего кода в попытке уменьшить когнитивную нагрузку для вашего конкретного примера. По существу, то, что я делаю, - это обертывание вашего кода в IIFE (немедленно вызываемое выражение функции), которое создает новую область с вашими переменными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) }
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, как показано ниже...
Введите описание изображения здесь
Почему ?