Изменение задержки выполнения в цикле
Проблема:
Есть цикл, при каждой итерации которого, происходит считка id'шника у элемента, после чего, нечетному элементу выдается один класс, и наоборот, четному иной. Вопрос такой:
Нужно сделать так, что бы на каждый элемент, на каждой итерации, добавлялся класс с задержкой.
Вот код:
let changeBtn = document.querySelector(".changeBtn");
let words = document.querySelectorAll(".span");
changeBtn.addEventListener("click", () => {
for (let i = 0; i <= words.length; i++) {
let delay = function timerId(){
console.log(words[i]);
console.log(words[i].id);
if (words[i].id % 2 === 0) {
words[i].classList.add("rightAnim");
}else {
words[i].classList.add("leftAnim");
}
}
timerId();
setTimeout(timerId, 1000);
}
});
Цикл счетчика я сделал, а вот задержку не понимаю как сделать при добавлении класса. Пытался в счетчик добавить функцию, и после нее сделал объявление (вне счетчика), и потом callback, но тогда вообще классы не добавлялись. Буду очень признателен за разъяснение ошибки.
Ответы (2 шт):
Как вариант, но если данных не много.
document.querySelectorAll('#list > div').forEach(function(e, i){
setTimeout(function(){
e.classList.add(i%2===0?'even':'odd');
}, i*500);
});
#list {
display: flex;
}
#list > div {
display: block;
width: calc((100vw - 45px) / 10);
height: calc((100vw - 45px) / 10);
background: gray;
border: 1px solid #333;
box-sizing: border-box;
}
#list > div:not(:last-child) {margin-right: 5px;}
#list .odd {
background: green;
}
#list .even {
background: blue;
}
<div id="list">
<div id="el-1"></div>
<div id="el-2"></div>
<div id="el-3"></div>
<div id="el-4"></div>
<div id="el-5"></div>
<div id="el-6"></div>
<div id="el-7"></div>
<div id="el-8"></div>
<div id="el-9"></div>
<div id="el-10"></div>
</div>
Вариант, где в качестве определения чётности использовается "числовой идентификатор" элемента:
Как вариант, но если данных не много.
document.querySelectorAll('#list > div').forEach(function(e,i){
let a = Number(e.dataset.id);
setTimeout(function(){
e.classList.add(a%2===0?'even':'odd');
}, i*500);
});
#list {
display: flex;
}
#list > div {
display: block;
width: calc((100vw - 45px) / 10);
height: calc((100vw - 45px) / 10);
background: gray;
border: 1px solid #333;
box-sizing: border-box;
}
#list > div:not(:last-child) {margin-right: 5px;}
#list > div::before {
content: attr(data-id);
display: flex;
justify-content: center;
align-items: center;
width: 100%;
height: 100%;
}
#list .odd {
background: green;
}
#list .even {
background: blue;
color: #fff;
}
<div id="list">
<div data-id="221"></div>
<div data-id="179"></div>
<div data-id="248"></div>
<div data-id="197"></div>
<div data-id="118"></div>
<div data-id="51"></div>
<div data-id="260"></div>
<div data-id="284"></div>
<div data-id="137"></div>
<div data-id="65"></div>
</div>
Сейчас цикл выполняется мгновенно и запускает все таймеры, которые вместе выполняются через секунду. setTimeout(timerId, 1000); → setTimeout(timerId, (i + 1) * 1000);, в таком виде таймеры в цикле также запустятся сразу, но первый выполнится через секунду, второй через 2 секунды и т.д.
Но создавать функции в цикле - плохо.
let words = document.querySelectorAll(".span");
document.querySelector(".change-btn").addEventListener("click", function() {
this.disabled = true;
// for (let i = 0; i < words.length; i++) ← как там:
let i = 0; // 1. Инициализация переменной,
(function _loop() {
if (!(i < words.length)) return;
// 2. «Тело цикла» выполняется только если условие верно.
let className = words[i].dataset.id % 2 == 0 ? "right" : "left";
words[i].classList.add(className);
i++; // 3. Перед каждой итерацией.
setTimeout(_loop, 500); // Следующая итерация запустится через 0.5 секунды.
})();
});
body {
white-space: pre-line;
text-align: center;
}
span {
transition: 0.5s;
}
.right {
margin-left: 100px;
}
.left {
margin-left: -100px;
}
<button class="change-btn">Test</button>
<span class="span" data-id="8">8</span>
<span class="span" data-id="1">1</span>
<span class="span" data-id="3">3</span>
<span class="span" data-id="2">2</span>
<span class="span" data-id="5">5</span>
<span class="span" data-id="7">7</span>
<span class="span" data-id="0">0</span>
<span class="span" data-id="9">9</span>
* Оформлено немного непривычно, чтобы показать параллель с for. Как минимум, if (!(i < words.length)) хорошо бы поменять if (i >= words.length)
Я просто новичок, и мне тяжело воспринимать все сокращения...
(function _loop() {
})();
↓ Эквивалентно (см. «IIFE, Immediately Invoked Function Expression»)
function _loop() {
}
_loop();
Где _loop — просто название функции. Создается функция, и сразу же вызывается.
if (!(i < words.length)) return;
! — логическое "НЕ". Если в какой-то момент i дошел до words.length (перестало выполняться условие i < words.length), return прерывает выполнение функии.
let className = words[i].dataset.id % 2 == 0 ? "right" : "left";
= (Число data-id делится на 2) ? Берем "right" : иначе "left"
↓ Эквивалентно (см. «Тернарный Оператор», «HTML data-атрибуты»)
let className;
if (words[i].dataset.id % 2 == 0) {
className = "right";
} else {
className = "left";
}
* dataset.id — Это всегда строка. Но операция % автоматически превращает её в число.
setTimeout(_loop, 500); — вызывает функцию _loop, которая вызывает setTimeout, который вызывает _loop, который вызывает setTimeout... Вызовы зацикливаются.