Цикл - это простой способ перебрать ряд элементов, которые содержатся в массиве или коллекции html-элементов для того, чтобы выполнить с ними ряд действий. Также циклы используются для выполнения однотипных действий типа подсчета суммы диапазона чисел или перемещения группы объектов на какое-то количество пикселей, например, при создании снега или дождя в canvas. В JavaScript существует несколько видов циклов:
Цикл for()
Цикл for() называется еще циклом со счетчиком и имеет следующий синтаксис:
1 2 3 | for ([инициализация счетчика]; [условие]; [изменение счетчика]) { //некий код } |
Этот цикл выполняется до тех пор, пока условие возвращает true
. Как только условие вернуло false
, выполнение цикла прекращается и выполняется тот код, который идет за ним, например, вывод результата работы цикла. Если вы запишите условие таким образом, что оно сразу вернет false
, то цикл не выполнится ни разу.
Каждое выполнение кода внутри фигурных скобок называется итерацией цикла, а сам блок внутри фигурных скобок - телом цикла.
Например, нам необходимо подсчитать сумму чисел от 1 до 10. Цикл будет выглядеть так:
1 2 3 4 5 | var sum=0; for(let i=1; i<=10; i++){ sum+=i; document.write("<p>i="+i+", sum="+sum+"</p>"); } |
Если вам недостаточно одного счетчика, вы можете использовать 2, тогда они инициализируются через запятую. Условий также может быть 2 или можно оставить одно. Это зависит от поставленной задачи. Код, представленный ниже, например, 50 раз выведет в консоль число 101:
1 2 3 | for(let i=1, j=100; i<=50; i++, j--){ console.log(i+j); } |
Цикл for()
также позволяет перебрать элементы массива или html-коллекции, хотя с этим также отлично справляется метод массивов forEach()
. В примере ниже по клику мы будем показывать информацию о неких выдуманных людях, которая спрятана в data-атрибутах элементов списка. Разметка и css-стили:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | <style> .for-example li { cursor: pointer; } .for-example.complete li { cursor: not-allowed; list-style-type: square; color: #939393; } </style> <ul class="for-example"> <li data-name="Игорь Низов" data-age="26">Показать информацию</li> <li data-name="Наталья Петрова" data-age="37">Показать информацию</li> <li data-name="Сергей Пименов" data-age="38">Показать информацию</li> <li data-name="Николай Колганов" data-age="45">Показать информацию</li> <li data-name="Ирина Невская" data-age="56">Показать информацию</li> </ul> |
JavaScript-код:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | let li = document.querySelectorAll('.for-example li'), count = 0; for (let i = 0, len = li.length; i < len; i++) { li[i].addEventListener('click', showInfo); } function showInfo() { this.innerHTML = this.dataset.name + ', ' + this.dataset.age + " лет"; this.removeEventListener('click', showInfo); count++; if (count == 5) { this.parentElement.classList.add('complete'); } } |
В этом коде мы сначала назначаем каждому элементу <li>
обработчик события click
, а затем, после того, как пользователь кликнул по элементу списка, удаляем этот обработчик события. После того, как показана информация о всех людях в этом списке, мы меняем его внешний вид за счет добавления класса "complete"
к элементу <ul>
.
Попробуйте самостоятельно, как работает код.
- Показать информацию
- Показать информацию
- Показать информацию
- Показать информацию
- Показать информацию
Как назначать обработчики событий можно прочитать в статье "Обработка событий в JavaScript", про варианты отображения курсоров - в статье "Виды курсоров в CSS".
Вложенные циклы
Некоторые задачи требуют использования нескольких циклов. Такие циклы называются вложенными и выполняются таким образом: сначала выражения, записанные во внешнем цикле до того момента, пока код не дойдет до внутреннего цикла. После этого выполняются все действия во внутреннем цикле, пока его условие не вернет false
, затем снова действия внешнего цикла. Т.е. внутренний цикл выполняет все свои действия столько раз, сколько это заложено в условии внешнего цикла.
В качестве примера давайте выведем методом document.write()
таблицу из 4-х строк и 6 ячеек в каждой строке. Операции по выводу строк и ячеек как раз являются примером повторяющихся действий, т.к. в каждой <tr>
должно быть одинаковое количество <td>
(более сложные таблицы с объединением ячеек рассматривать не будем).
1 2 3 4 5 6 7 8 9 | document.write("<table style='width: 96%'>"); for(var i = 1; i<=4; i++){ document.write("<tr>"); for(var j= 1; j<=6; j++){ document.write("<td>Ячейка № "+j+"</td>"); } document.write("</tr>"); } document.write("</table>"); |
Цикл while()
Цикл while() называют еще циклом с предусловием. Это значит, что вы записываете в скобках после ключевого слова while некое условие, которое возвращает либо true
, либо false
. Если возвращается true
, цикл выполняется, если же false
, то нет. Синтаксис этого цикла таков:
1 2 3 4 5 6 | while( условие ){ //код цикла операция 1; ... операция n; } |
Если переводить JavaScript-код на русский язык, то смысл цикла while можно выразить такой фразой: пока (while) выполняется условие, мы будем делать некоторые операции.
Вполне возможна ситуация, когда ваш код не выполнится ни разу. Например, вам нужно подсчитать сумму чисел в заданном пользователем диапазоне. Рассмотрим вариант кода для этого с использованием полей формы типа number
:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 | <form name="numbers"> <p> <label for="from">От числа</label> <input type="number" min="1" max="50" value="1" id="from"> </p> <p> <label for="to">До числа</label> <input type="number" min="1" max="50" value="5" id="to"> </p> <p><input type="button" value="Подсчитать" id="countBtn"></p> </form> <script> document.numbers.countBtn.onclick = countNumbers; function countNumbers () { let from = +document.numbers.from.value, to = +document.numbers.to.value; if(from > to) { let temp = from; from = to; to = temp; } let sum = from; while(to>from){ from++; sum += from; } alert(sum); } </script> |
В этом примере цикл не выполнится ни разу в том случае, если значения начального и конечного чисел совпадут. Если же первое число будет больше, чем второе, мы поменяем их местами и цикл выполнится, как нужно.
Попробуйте сами:
Поскольку условие цикла while() должно вернуть либо true
, либо false
, мы можем использовать в нем диалоговое окно confirm(), которое как раз возвращает одно из этих логических значений в зависимости от выбора пользователя.
1 2 3 4 5 6 7 8 | const prediction = ["У вас сегодня удачный день", "Вам нужно задуматься о вашем выборе", "Сегодня вас ждет сюрприз", "Вы - любимчик удачи", "Попробуйте еще раз", "Азартные игры не для вас", "Учите JavaScript - и вас ждет успех", "Пора сделать зарядку", "Перерыв на кофе-чай-печеньки сейчас будет кстати", "Пора выбросить мусор из квартиры", "У вас отличный вкус", "Нечего заглядываться на чужих жен", "Пора поработать"]; while (confirm("Хотите получить предсказание")) { let rand = Math.floor(Math.random() * prediction.length); alert(prediction[rand]); } |
Давайте немножко погадаем:
true
, соответственно, цикл будет бесконечным и будет замедлять работу браузера.Цикл do ... while()
Как видно из его названия, цикл do ... while() похож на while(), но в нем мы сначала выполняем некие операции, а уже потом проверяем условие, поэтому цикл do ... while() еще называют циклом с постусловием. Как и в предыдущем цикле действия внутри фигурных скобок (тела цикла) выполняются до тех пор, пока условие возвращает true
. Как только условие вернет false
, выполнение цикла прекращается, и программа выполняет действия после него.
Цикл do ... while() также может стать бесконечным, если вы сформировали условие таким образом, что оно все время возвращает true
. Так, как на картинке выше :).
Синтаксис цикла do ... while()
1 2 3 4 5 | do { операция 1; ...; операция n; } while (условие); |
Т.е. в отличие от цикла while()
цикл do ... while()
выполнится хотя бы раз даже, если условие вернет false
.
В примере ниже мы используем цикл do ... while() для того, чтобы показать построчно строчки стихотворения. Здесь мы сочетаем возможности JavaScript с возможностями анимации CSS3, а именно свойств animation
и animation-delay
, а также директивы @keyframes
. С помощью css-свойств мы можем создать анимацию, не задействуя такие методы JavaScript, как setTimeout()
или setInterval()
, а с помощью цикла - задать задержку animation-delay
, которую мы увеличиваем для каждой следующей строки.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 | <style> @keyframes movingLeft { from { transform: translateX(-100%) } } @-webkit-keyframes movingLeft { from { -webkit-transform: translateX(-100%) } } #loopDoWhile { box-sizing: border-box; background: url(images/ded-moroz.jpg) no-repeat; background-size: contain; width: 650px; height: 365px; color: #fff; padding: 20px; position: relative; overflow: hidden; } #loopDoWhile::after { box-sizing: border-box; content: ''; display: block; position: absolute; top: 0; left: 0; width: 100%; height: 100%; background-color: rgba(0, 0, 0, 0.5); } #doWhileInner { position: relative; z-index: 2; } .poem-row { font-size: 22px; animation: movingLeft .7s ease-out backwards; } </style> <div class="test" id="loopDoWhile"> <div id="doWhileInner"></div> </div> <button id="moroz">Показать стих</button> <script> const poem = ["Где живёт Дед Мороз?", "Удивительный вопрос!", "Не в лампе, не в будильнике", "Посмотрим в холодильнике!"], doWhileInner = document.getElementById('doWhileInner'); moroz.onclick = function() { let n = 0; doWhileInner.innerHTML = ''; do { doWhileInner.innerHTML += `<p class="poem-row" style="animation-delay: ${.5*n}s">${poem[n]}</p>`; n++; } while (n < poem.length) } </script> |
Давайте посмотрим пример в действии:
Кстати, при формировании css-стилей для примера, был использован подход с созданием перекрывающего черного полупрозрачного слоя overlay из этой статьи.
Оператор break
Давайте рассмотрим еще один пример do ... while()
, в котором в условии у нас указано true
, т.е. мы все-таки сделаем бесконечный цикл. Необходимо как-то из него найти выход. Для этого в JavaScript существует оператор break (англ. - ломать, нарушать, прерывать), который как раз и предназначен для прерывания цикла. Обычно это осуществляется при выполнении какого-либо условия внутри цикла.
В примере нам необходимо вывести методом document.write()
несколько div-ов с классом yellow-bg
и разной шириной - от 200 до 700px. Для этого мы объявим и будем использовать в цикле переменную w
. Сначала она будет равна 200, но с каждой итерацией цикла мы будем увеличивать ее на 100 и использовать в атрибуте style
для вывода div-а. Как только эта переменная станет больше 700, мы прервем выполнение цикла оператором break.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 | <style> .yellow-bg { background-color: #ff0; border: 2px dotted #f40; padding: 10px; margin: 10px 0; } </style> <script> let w=200; do{ document.write(`<div class="yellow-bg" style=" width: ${w}px">Ширина блока ${w}px</div>`); w+=100; if(w>700) break; } while(true) </script> |
Вот, что получилось в итоге:
Как видно из примера, после вывода div-а с шириной в 700px цикл действительно прервался.
Оператор continue
Иногда в цикле могут возникать ситуации, при которых прерывать его нет смысла, но и продолжать выполнять какие-то операции или вычисления также не имеет смысла. В этом случае стоит использовать оператор continue, который в переводе с английского звучит, как "продолжить". Он прерывает текущую итерацию цикла и запускает его снова, т.е. как бы переходит к следующей итерации, не завершив до конца предыдущую.
Для примера ниже, в котором мы подсчитываем сумму введенных пользователем положительных чисел, оператор continue используется дважды: когда пользователь вводит ноль (любое число с прибавленным 0 - то же самое число, так зачем это делать?) и когда пользователь вводит строку, которая не может быть прибавлена к числам.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | var sum = 0, count = 0, str=''; do { var num = parseInt(prompt("Введите положит. число, для прерывания введите отрицат. число", "10")); if (isNaN(num)) { alert("Вводите только числа"); continue; } if(num==0) continue; if(num<0) break; sum+=num; count++; str+=num +','; } while (true) alert("Сумма чисел "+str+" = "+sum+",\nсреднее арифметическое = "+sum/count); |
Оператор break используется для выхода из цикла при вводе отрицательного числа, иначе скрипт все время будет запрашивать числа, считать их сумму и среднее арифмтическое.
Попробуйте сами, нажав на кнопку:
Цикл for...in()
Цикл for...in() предназначен для перебора ключей массива или объекта. Он имеет следующий синтаксис:
1 2 3 4 5 | for(let key in obj){ //некий код } |
Рассмотрим такой пример: у нас есть массив строк и его нужно вывести на экран или в консоль. Если мы воспользуемся ключами массива, то код будет выглядеть так:
1 2 3 4 | let words = ['Уж', 'я', 'веники', 'не', 'вяжу']; for(let one in words){ console.log(one); } |
Результат: мы видим только цифры, которые являются индексами массива (console.log()
заменили на document.write()
):
В случае, если нам необходимо вывести значения, а не индексы элементов массива, код нужно переписать так:
1 2 3 4 | let words = ['Уж', 'я', 'веники', 'не', 'вяжу']; for(let one in words){ console.log(words[one]); } |
Результат:
Этот цикл хорошо использовать для ассоциативных массивов, в которых ключи являются строковыми и перебрать их другим способом не получится.
1 2 3 4 5 6 7 8 | let site=[]; site["name"] = 'Магазин игрушек "Bambino House"'; site["url"] = 'http://bambino-house.com/'; site["admin_name"] = 'bambino_admin'; site["admin_pass"] = 'my_pass'; for(let each in site){ console.log(each + ': ' + site[each]); } |
Посмотрим, что получилось (в примере была замена console.log()
на document.write()
):
Еще одна область применения цикла for ... in - это вывод значений свойств объекта, например, таким образом:
1 2 3 4 5 6 7 8 9 | const obj = { name: "John", surname: "Doe", age: "27", position: "Frontend Developer" } for (let key in obj){ console.log(obj[key]); } |
В консоли получим такой результат:
Цикл for...in для перебора html-элементов
Дело в том, что псевдомассивы отличаются от обычных массивов, т.к., во-первых, не имеют тех методов, которые есть у класса Array
для управления элементами массивов, а во-вторых, имеют свойство length
и, возможно, собственные методы, которые цикл for...in
также перебирает. Поэтому вы можете столкнуться с такой ошибкой:
Эта ошибка произошла при переборе в цикле for...in элементов с классом "test-block"
, когда к каждому из них при нажатии на кнопку нужно добавить класс "test-blue"
. Код срабатывает для 4-х html-элементов, которые перебраны в цикле, но не может добавить класс для свойства length
, которое возвращает не html-элемент, а число.
Код примера:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 | <div class="test"> <div class="test-block">Test 1</div> <div class="test-block">Test 2</div> <div class="test-block">Test 3</div> <div class="test-block">Test 4</div> <p> <button class="button" onclick="addBlueClass()">Добавить класс к блокам</button> </p> </div> <script> function addBlueClass() { const testBlocks = document.getElementsByClassName('test-block'); //console.log(testBlocks); for (let ind in testBlocks) { console.log(testBlocks[ind]); testBlocks[ind].classList.add('test-blue'); } } </script> |
Сам пример вполне рабочий, но в консоли вы увидите ошибку.
Происходит это из-за разницы в обращении ко всем свойствам псевдомассива html-элементов, перебираемым в цикле for ... in
. На скриншоте вы видите сравнение в выводе в консоль данных из обычного массива, который мы разбирали ранее, и псевдомассива из html-элементов:
Временами это может привести к неработоспособности всего кода, который вы будете писать после цикла for...in
.
Цикл for...of()
В отличие от for...in()
цикл for...of() предназначен для вывода значений элементов массива. Синтаксис for...of()
таков:
1 2 3 | for(let value of arr){ //некий код } |
Пример использования цикла на основе уже известного нам массива. Перепишем код таким образом:
1 2 3 4 | let words = ['Уж', 'я', 'веники', 'не', 'вяжу']; for(let one of words){ console.log(one); } |
В этом примере мы сразу выводим значения элементов массива, не затрагивая его индексы.
Стоит отметить, что цикл for...of()
появился в стандарте ES6 (EcmaScript2015) и может не работать в Internet Explorer и некоторых устаревших версиях браузеров. Смотрите поддержку этого цикла на caniuse.com.
for...of()
проводит перебор значений итерируемых конструкций (Array, Map, Set и DOM) и в переменную-счетчик отправляется значение, по которому происходит итерирование. Однако, из-за того, что в объектах или ассоциативных массивах нет итерируемых значений, а есть ключи или названия свойств, цикл for ... of
не может использоваться для перебора объектов и ассоциативных массивов, как и цикл for()
.Применение цикла for...of для перебора символов строки
Поскольку символы в строках являются также итерируемыми, т.е. к любому символу строки, объявленной в виде переменной let str="Hello world"
можно обратиться таким образом: str[0], str[1]
, ..., str[str.length-1]
. Поэтому мы можем перебрать содержимое какого-либо элемента и вывести его в виде разноцветных букв.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 | <p id="testString" style="font-weight: bold; font-size: 2em; letter-spacing: 1px;">Hello world</p> <button class="button" onclick="setColorString()">Обновить</button> <script> const randColorNum = () => { let r = Math.floor(Math.random() * 256); let g = Math.floor(Math.random() * 256); let b = Math.floor(Math.random() * 256); return `rgb(${r}, ${g}, ${b})`; } function setColorString() { let str = ''; for (let char of testString.textContent) { str += `<span style="color: ${randColorNum()}">${char}</span>`; } testString.innerHTML = str; } setColorString(); </script> |
Давайте посмотрим, как сработает for...of для строки на реальном примере:
Hello world
Решение на codepen.io - это то, что нужно!
спасибо Вам большое, что уделили моему вопросу столько времени!
Дякую! 😉
Уважаемый автор!
подскажите, пожалуйста, а как написать счетчик чтобы он отображал общую сумму при добавлении своей цифры пользователем. Пример:
Внешне на сайте это должно выглядеть, например, так:
Общее количество приседаний: 2343948
добавить [тут поле для ввода своей цифры]
кнопка "отправить"
вследствие чего данные на сайте в строке "Общее количество приседаний" поменяется на то что было - 2343948 +число от пользователя.
Зараннее благодарная за ответ.
Уважаемая Olena!
Код примера вы найдете по ссылке на codepen.io
Никакого отношения к циклам он не имеет.
Здесь работа с формой и со свойством textContent для текстового элемента, а также преобразования строки в число.
Кроме того, новое число сохраняется в localStorage вашего браузера, поэтому при открытии страницы с количеством приседаний в том же браузере в следующий раз вы увидите уже число с добавленными вами приседаниями.