Изменение задержки выполнения в цикле

Проблема:

Есть цикл, при каждой итерации которого, происходит считка 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 шт):

Автор решения: CbIPoK2513

Как вариант, но если данных не много.

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>

→ Ссылка
Автор решения: OPTIMUS PRIME

Сейчас цикл выполняется мгновенно и запускает все таймеры, которые вместе выполняются через секунду. 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... Вызовы зацикливаются.

→ Ссылка