JavaScript для начинающих | Разработка интернет-магазина квадрокоптеров DJI

JavaScript для начинающих | Разработка интернет-магазина квадрокоптеров DJI01:12:33

Информация о загрузке и деталях видео JavaScript для начинающих | Разработка интернет-магазина квадрокоптеров DJI

Автор:

Northern Lights

Дата публикации:

24.08.2024

Просмотров:

2.9K

Транскрибация видео

Спикер 2

Я подготовил новый проект на JavaScript.

Это интернет-магазин квадрокоптеров.

Пока что он будет состоять из пяти модулей.

В первом модуле мы научимся динамически добавлять карточки товаров на страницу.

Здесь вместо того, чтобы в HTML каждый раз добавлять карточку для нового товара, мы при помощи JavaScript создадим разметку такой карточки и для каждого продукта будем выводить соответствующие ему данные.

Второй модуль это погенация.

В нем вместо того, чтобы показывать все данные сразу, мы разобьем большой список данных на несколько страниц.

Причем здесь я покажу, как добавлять какое-то определенное количество товаров на страницу.

Например, 2, 3, 4.

5, 6, 7, 8, 10.

Оно может быть абсолютно любым.

Таким, каким мы захотим.

И в зависимости от количества товаров на одной странице, в погенации у нас будет отображаться соответствующее количество страниц.

Например, если у нас в массиве всего 80 продуктов, и на странице мы хотим показывать по 8 штук, то тогда у нас будет 10 страниц.

И здесь будет добавлена логика перехода на страницу

по нажатию на ее номер и логика перехода на следующую или предыдущую страницу в зависимости от того по какой кнопке был клик вперед или назад третий модуль это модальное окно в каждом интернет магазине есть корзина и чаще всего она идет или отдельной страницей или модальным окном и на примере с корзиной мы рассмотрим

Открытие и закрытие модального окна.

По нажатию на иконку корзины она будет открываться.

А по нажатию на крестик или область вне корзины она будет закрываться.

Четвертый модуль.

Счетчики.

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

Причем здесь я покажу вам два способа, как можно сделать кнопку минуса неактивной, когда количество товара в корзине равно единице.

И последний модуль – корзина.

Здесь я покажу, как добавлять товар в корзину, как сделать так, чтобы при добавлении того товара, который уже есть в корзине, он был одной позицией с изменением только количества штук, а не отдельными позициями.

Как удалять товар, как менять состояние корзины в зависимости от того,

есть в ней продукты или нет и как при добавлении товаров или изменении их количества пересчитывать стоимость отдельных позиций и итоговую стоимость и здесь же рассмотрим еще такую тему как делегирование событий когда это нужно делать а когда нет данное видео будет полезно для тех кто только знакомится с java скриптом и кому необходима практика исходники с

и css вы можете скачать в телеграм-канале ссылку на которой я оставлю в описании под этим видео по верстке я сейчас добавил только самое необходимое чтобы показать данный функционал пока что у нас будет вот такой проект и сразу расскажу что дальше есть несколько опций куда мы можем двигаться

Первая ветка — это верстка.

Я ее могу сделать с подробным объяснением.

Например, почему для этого элемента выбрали этот тег, а не другой, как правильно стилизовать и прочее, прочее, прочее.

Вторая ветка — это продолжать этот проект и добавлять новую логику на JavaScript.

Пока точно не решил, что это может быть, но как вариант...

Это асинхронное получение товаров с использованием фич, промисов.

Можно сделать сохранение товаров в корзине через Local Storage, чтобы при обновлении страницы все товары в ней сохранялись.

Можно добавить поиск.

Еще можно создать различные фильтры.

Также можно реализовать добавление в избранное, счетчик товаров в корзине, причем тоже с сохранением значения в Local Storage, чтобы при обновлении в нем всегда показывалось текущее актуальное значение.

Еще это может быть, например, слайдер картинок внутри карточки товара.

Или вот такое обновление картинки при наведении на соответствующую область изображения.

Вместо погенации вот здесь можно сделать кнопку «Показать еще».

Да и на самом деле здесь логики можно добавлять довольно много.

Или можно пойти по третьей ветке и сделать такой же интернет-магазин, но на React.

У меня уже есть небольшая заготовка для этого.

Здесь я полностью оставил такой же дизайн.

Но чтобы различать проекты, это уже будет интернет-магазин

Не квадрокоптеров, а наушников.

Варианты пока такие.

Можете написать, что вам было бы наиболее интересно.

Ещё каждый модуль в этом интенсиве я сейчас буду записывать отдельно и выкладывать на канале.

А когда выйдет последний модуль, то объединю их все в одно большое видео.

Сейчас у нас в файле index.html для каждой карточки товара добавлена лишка.

И в каждой лишке у нас одинаковая верстка.

Но при этом у каждого квадрокоптера своя фотография, своя модель и своя цена.

Так вот, наша задача будет при помощи JavaScript создать одну карточку товара и для каждого квадрокоптера добавлять в нее свои данные.

Динамическое добавление карточек необходимо, потому что у верски каждой отдельной карточки вручную есть свои недостатки.

Первое это трудность масштабирования.

Представьте, что у нас не 4 квадрокоптера, а например 100.

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

И это все отнимает много времени.

Помимо этого, вторым недостатком является сложность в изменениях.

Допустим, нам у каждой лишки необходимо будет заменить класс.

Да, конечно, мы это можем сделать при помощи поиска и замены, но если, например, вот здесь для каждого квадрокоптера

мы захотим добавить информацию о том, в наличии он или можно сделать только предзаказ, то тогда нам придется вносить изменения в каждую карточку отдельно.

И это совсем неудобно.

Как в этом случае можно поступить?

Можно создать массив, элементами которого будут объекты.

И в каждом объекте у нас будет содержаться информация по отдельному продукту.

Все свойства будут в паре ключ-значения.

Нам сейчас понадобится айдишник, модель, цена и фото.

Помимо этого я здесь добавил для каждого квадрокоптера категорию, серию, максимальное время полета и общую информацию.

Возможно в дальнейшем мы ее где-то будем использовать.

Значением для цены и для фото у нас будут массивы.

В цене первым элементом будет новая цена, а вторым элементом старая.

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

Если мы будем делать страничку отдельного продукта, то на ней можно выводить фотографии различных видов.

Это вид спереди, вид сверху, вид в собранном состоянии.

И на самом деле здесь можно добавить еще несколько видов.

Самое главное это обратите внимание на то, что у каждого объекта

должен быть уникальный ID-шник.

И давайте перейдем к написанию кода.

До этого в предыдущих видео я объяснял, что происходит в момент написания самого кода.

Сейчас я попробую это сделать в новом формате.

Напишу какой-то кусочек кода, оставлю это за кадром и на видео буду его объяснять.

Помимо этого мы здесь будем разносить всю функциональность в отдельные модули.

То есть будем использовать модульный подход.

Динамическое добавление это будет у нас один модуль.

Пагинация второй модуль.

Третий это модальные окна.

И четвертый корзина.

и в корзине мы еще сделаем счетчики такое разделение на модули упрощает чтение и понимание кода и также его будет проще поддерживать помимо этого модули позволяют изолировать контексты выполнения кода то есть у нас переменные и функции

внутри одного модуля, не будут конфликтовать с переменными и функциями в других модулях.

И еще одно важное преимущество, это то, что мы можем всегда какой-то модуль повторно переиспользовать на другом проекте.

Все модули у нас будут в папке Modules, и первый будет отвечать за динамическое добавление продуктов и называться ProductCards.

В этом модуле мы создаем функцию, которая будет рендерить карточки продуктов.

Это будет стрелочная функция, и здесь нам ее не

необходимо экспортировать.

А в файле main.js мы ее импортируем.

Для этого пишем слово import.

В фигурных скобках указываем ее имя.

Далее пишем from, то есть откуда.

И импортировать мы ее будем из модуля product-cards, который лежит в папке modules.

И здесь нам остается ее только вызвать.

Еще важный нюанс, это при подключении файла main.js нам необходимо для тега script добавить атрибут type со значением module.

И давайте файл index.html запустим через live сервера.

Чтобы убедиться, что мы сейчас правильно экспортировали и импортировали функцию,

Пусть она в консоль выводит единичку.

И да, у нас все работает.

Помимо этого, в файле Products.js мы точно так же экспортируем массив с продуктами.

А в файле Main.js его импортируем.

Но он у нас лежит не в папке Modules,

в папке JS.

Поэтому у него будет соответствующий путь.

И давайте нашу функцию мы будем вызывать по событию DOMContentLoaded.

Это событие сработает тогда, когда весь HTML документ будет полностью загружен и обработан браузером.

Это нам даст уверенность в том, что все необходимые элементы будут доступны для работы JavaScript кода.

И это помогает нам предотвращать ошибки.

То есть, если мы попробуем обратиться к элементам дом, до того как дом будет полностью загружен, эти элементы могут быть еще недоступны.

И первый элемент, который нам понадобится, это улка с классом JSProductsList.

Ее мы получаем через документ QuerySelector по этому классу и записываем в константу ProductContainer.

Наша функция RenderProductCards аргументами будет принимать массив с продуктами и контейнера, куда нам их необходимо будет выводить, то есть улку.

Соответственно в модуле при создании этой функции нам необходимо указать два параметра.

Products и Container.

И давайте здесь в консоль выведем этот массив с продуктами.

Мы его получили и дальше с ним можем работать.

Выше давайте мы создадим функцию, которая будет рендерить карточку только одного продукта.

И параметром она будет принимать этот самый продукт.

И в теле этой функции мы при помощи метода createElement создадим тег li и запишем его в константу li.

И пока что эта функция у нас будет просто возвращать эту лишку.

А внутри функции, которая будет рендерить все продуктовые карточки,

Мы можем массив Products перебрать при помощи метода ForEach.

Этот метод у нас принимает callback функцию.

И в нее мы передаем продукт.

То есть один отдельный объект.

И при переборе мы для каждого объекта будем создавать константу карт.

И в нее присваивать результат вызова функции RenderProductCard.

Поскольку у нас функция рендера одной карточки

возвращает лишку, то в консоли в константе карт у нас будет лишка.

И поскольку в массиве 4 объекта, и мы каждый раз выводим константу карт в консоль, то здесь мы видим 4 лишки.

Сейчас у нас есть лишки, и нам остается добавить их в улку.

Для этого выше мы создадим еще одну функцию appendProductCard.

И единственное, что она будет делать, это добавлять один элемент в конец другого.

при помощи метода append то есть мы будем добавлять продукт в конец контейнера и вызывать эту функцию нам необходимо после того как мы создали лишку добавлять мы будем карт это лишка а контейнер это улка если мы в devtools перейдем в элементы

и найдем здесь улку то увидим 8 лишек первые 4 это те которые у нас добавлены в файле index.html а последние 4 это те которые мы только что создали давайте в файле index.html все что у нас находится внутри улки

мы закомментируем и сейчас у нас здесь 4 лишки у каждой лишки есть вот эти классы поэтому функция рендера после того как мы создали лишку при помощи classList и метода add можно вот так через запятую в кавычках добавить все эти классы здесь мы видим что классы добавились и на странице появились 4 карточки

Поскольку у нас уже для класса Product в файле style.css заданы какие-то стили.

Дальше мы можем при помощи inner.html добавить разметку.

Поскольку мы будем вставлять значения переменных, то используем косые кавычки.

И в файле index.html копируем все содержимое какой-нибудь лишки.

Сейчас неважно какой.

И вставляем.

между этими косыми кавычками и вот таким образом мы в каждую лишку добавили разметку и вместо статичной информации мы можем подставлять значение свойств из объекта в функции рендера карточек мы перебираем массив с объектами и вот здесь при рендере одной карточки мы передаем вот эти отдельные объекты у нас их здесь 4 а при создании функции мы параметрам передали

этот объект, и чтобы получить значение того или иного свойства, мы через точку можем указать соответствующий ключ.

Например, если нам нужен id-шник, то указываем id.

Если нам нужна модель, соответственно указываем model.

Если нужна первая цена из массива цен, то тогда мы обращаемся к нулевому элементу этого массива.

Если нужно фото, где будет вид спереди, то соответственно обращаемся к нулевому элементу массива фото.

И по идее на этом можно было бы закончить.

Но давайте мы еще воспользуемся деструктуризацией объекта.

То есть извлечем значение объекта.

Для этого вот здесь в параметре мы используем вот такую запись.

Вместо product в ней у нас будут переменные id, фото, объект.

Model и Prices.

И поскольку это объект, то здесь мы используем фигурные скобки.

И после этого мы уже можем в разметке просто указать имена этих переменных, не обращаясь к Product.

Спикер 3

И у нас все будет точно так же работать.

Спикер 2

Пагинация это такой механизм, который позволяет разбить большой список данных на несколько страниц.

Вот представьте, что у нас на сайте есть 80 квадрокоптеров.

Если мы их просто будем динамически выводить на страницу, то тогда мы покажем все 80 штук сразу.

А вот благодаря пагинации мы можем отображать только определенное количество квадрокоптеров.

на каждой странице.

Вместо того, чтобы в файле index.html добавлять верстку для каждой карточки, мы научились динамически выводить их при помощи JavaScript.

И если карточек немного или погенация не нужна, то можно оставить все таким образом.

Но мы с вами сейчас напишем погенацию.

Поэтому функцию renderProductCards давайте закомментируем.

И сейчас у нас на странице нет ни одной карточки.

Потому что в файле index.html они также все закомментированы.

Отлично.

Начнем с нуля.

В папке modules создаем модуль pagination.

В этом модуле создаем стрелочную функцию paginate.

И сразу же ее экспортируем.

А в файле main.js ее импортируем.

И сразу же вызываем.

При вызове мы будем передавать в нее массив с продуктами.

Поэтому в модуле Pagination мы параметром передаем Products.

И давайте его выведем в консоль.

И да, мы его здесь видим.

Сейчас у нас в этом массиве 4 квадрокоптера.

Для погенации лучше, чтобы в нем было побольше продуктов.

Поэтому этот массив я закомментирую и вставлю новый.

В нем уже будет 80 товаров.

Я просто скопировал эти 4 товара.

вставил ниже у меня получилось их 8 штук и уже эти все скопировал еще 10 раз у первых восьми я заменил айдишники на уникальные а у всех остальных они там повторяются но для дальнейшей работы с корзиной нам вполне хватит и первых восьми в модуле pagination внутри функции paginate мы в самом начале

Создаем две переменные.

ProductCount и CurrentPage.

В ProductCount у нас будет количество продуктов, которые мы будем показывать на одной странице.

Пока что пусть будет 7.

А в переменной CurrentPage мы будем хранить текущую страницу.

И изначально у нас в текущей всегда будет первая страница.

Дальше нам необходимо получить все элементы со страницы, с которыми мы будем работать.

Это улка, в которую мы будем выводить карточки.

Еще нам понадобится контейнер для всей погинации, поскольку изначально ему добавлен класс hidden.

А в этом классе задано свойство display со значением none.

И если у нас на странице будет погинация, то этот класс нам необходимо будет удалять.

Помимо этого, мы еще здесь получаем две кнопки.

Вперед-назад.

Ниже мы пишем первую функцию, которая будет рендерить продукты.

Она будет называться RenderProducts и принимать 4 параметра.

Первым массив с продуктами.

Вторым контейнер, куда нам необходимо будет эти продукты выводить.

Третьим это число продуктов, то есть сколько необходимо выводить.

И четвертым страницу.

и ниже мы здесь эту функцию сразу вызываем в качестве массива у нас будет массив Products в качестве контейнера улка то есть константа ProductContainer число продуктов у нас содержится в переменной ProductCount и страница в переменной CurrentPage первое что мы будем делать в этой функции это при помощи innerHTML очищать все содержимое улки

То есть контейнера.

Если у нас до этого там что-то было, то это нам необходимо удалить.

Такая ситуация будет возникать, когда мы будем нажимать на страницы.

Например, мы захотим перейти на вторую страницу, отобразить продукты с этой страницы, а у нас уже в контейнере показаны первые 7 продуктов.

Поэтому эти первые 7 мы отсюда убираем.

Сейчас мы только указали, что хотим показывать по 7 продуктов.

Но при этом нам необходимо как-то для каждой страницы показывать именно соответствующие ей продукты.

То есть для первой страницы это должны быть первые 7 продуктов из массива.

Для второй страницы это должны быть продукты с 8 по 14 и так далее.

И вот чтобы это сделать,

Мы создаем константу ProductsOnPage, то есть продукты на странице.

И мы берем наш весь массив Products и при помощи метода Slice его обрезаем.

Этот метод у нас будет вырезать и возвращать указанную часть массива.

Первым параметром он принимает номер элемента массива, с которого начинается вырезание.

А вторым параметром номер элемента массива.

на котором закончится вырезание.

И при этом этот элемент с этим номером не будет включен в вырезанную часть.

Так, теперь нам необходимо разобраться с тем, как взять индекс первого элемента и индекс последнего элемента.

Для первого необходимо число продуктов на странице умножить на саму страницу и из этого вычесть это число продуктов.

А чтобы получить последний индекс, нам необходимо...

первому индексу просто прибавить число продуктов если мы возьмем первую страницу то для нее в первом индексе у нас будет 7 умножить на 1 минус 7 будет 0 и к нулю мы прибавляем 7 то есть в методе слайс у нас будет первым параметром 0 а вторым 7 и поскольку 7 индекс не включится в вырезанную часть то

то мы получим элементы с индексами 0, 1, 2, 3, 4, 5, 6.

Для второй страницы, для первого индекса, мы 7 умножаем на 2 и вычитаем 7.

Итого будет 7.

И для последнего индекса к 7 прибавляем 7.

То есть будет 14.

И в методе slice первым параметром будет 7, а вторым 14.

Думаю с этим понятно.

Если что, можете все вывести в консоль и менять значения ProductCount и CurrentPage.

Дальше нам необходимо вот этот вырезанный массив перебрать и для каждого его элемента создать карточку.

На этом я сильно останавливаться не буду, потому что это мы рассмотрели на динамическом добавлении товаров на страницу.

Здесь мы при переборе создаем при помощи метода CreateElement.TagLie.

Записываем его в константу li.

При помощи classList и метода add добавляем все классы, которые были у лишки в верстке.

И затем при помощи innerHTML добавляем разметку.

И в этой разметке для каждого квадрокоптера.

Подставляем свой ID, свое фото, свою модель и свою цену.

И в методе forEach в callback функции при передаче параметром элемента массива мы воспользовались деструктуризацией объекта.

И после того, как в лишку добавили разметку, мы этот элемент при помощи метода append добавляем в контейнер, то есть в волку.

И вот сейчас у нас на странице 7 продуктов.

Мы сейчас это количество можем задавать, меняя значение переменной прод.

Но пока что оставим 7.

Не смотрите на то, что сейчас 1 и 5 повторяются, 2 и 6, 3 и 7.

Это все потому, что я раскопировал эти 4 объекта и поменял у них только ID.

Дальше давайте мы будем показывать контейнер, в котором находятся кнопки «Вперед», «Назад» и «Страницы».

Для этого создадим функцию RenderPagination.

Параметрами нам здесь понадобится массив с продуктами и число продуктов на странице.

И ниже мы эту функцию сразу вызовем.

И аргументами передадим этот массив и ProductCount.

Давайте у контейнера Pagination при помощи класс List и метода Remove мы будем удалять класс Hidden.

Таким образом он у нас сейчас виден на странице.

Но между кнопками нам еще необходимо добавить страницы.

И вообще лучше удалять класс hidden только после этого.

Поэтому вот это действие в этой функции будет самым последним.

В зависимости от того, сколько продуктов на странице мы будем показывать, у нас будет меняться число страниц.

Поэтому нам его необходимо посчитать.

Для этого мы создаем константу.

pages count и в ней мы будем длину массива products то есть всего массива делить на число продуктов на одной странице сейчас у нас всего 80 продуктов а на странице мы показываем по 7 поэтому число страниц у нас будет равно 11,4 и при помощи mass

В sale мы округлим это значение в наибольшую сторону.

Почему в наибольшую?

Потому что на 11 страницах у нас будет по 7 продуктов, итого 77.

И значит на последней странице мы должны показывать еще 3 продукта.

Потому что 80 минус 77 будет 3.

3 последних продукта.

В этом контейнере с классом GSPagination у нас есть улка.

И ее мы будем заполнять лишками и в эти лишки добавлять значение страниц.

Поэтому нам ее необходимо получить со страницы.

У нее есть класс GSPaginationList.

Поэтому можем ее получить через DocumentQuerySelector по этому классу и записать в константу улку.

Дальше мы можем создавать лишки при помощи цикла for.

до того момента, пока их число не будет равно pagesCount.

Мы бы, конечно, могли здесь создавать эту лишку, добавлять ей классы и заполнять ее, но лучше создание лишки вынести в отдельную функцию, которую мы будем вызывать на каждой итерации цикла.

Давайте функцию для создания лишек мы назовем RenderBTN.

Это будет стрелочная функция.

И параметром она будет принимать номер страницы.

В ней мы при помощи метода createElement создаем тег li.

Записываем его в константу li.

При помощи classList и метода add добавляем классы paginationItem, row, justifyContentCenter и allyItemsCenter.

Последние три класса необходимы для того, чтобы выровнять текст влево.

внутри лишки по центру, по вертикали и по горизонтали.

А класс PaginationItem добавляет ей вот эти стили.

Далее при помощи свойства TextContent мы записываем в нее значение страницы.

Ниже мы добавляем условие, что если у нас текущая страница будет равна номеру страницы в цикле,

то тогда для этой лишки мы будем при помощи ClassList и метода Add добавлять класс Active.

А этот класс Active меняет ее цвет текста, цвет фона и границу.

То есть мы видим, что у нас сейчас активная первая страница.

Если поменяем здесь текущую страницу на вторую, то тогда уже ей добавится класс Active.

И в этой функции нам необходимо эту созданную лишку возвращать.

И в функции RenderPagination...

В цикле мы создаем константу li и в нее присваиваем результат вызова функции renderBtn.

И в эту функцию аргументом передаем значение счетчика i. И на каждой итерации он у нас будет увеличиваться.

И поэтому мы здесь видим номера страниц 1, 2, 3, 4, 5 и так далее.

И после этого нам остается...

Добавить лишку при помощи метода append в конец улки.

И дальше нам необходимо реализовать переключение между страницами по нажатию на какую-либо из этих страниц.

Для этого мы создадим отдельную функцию.

Назовем ее updatePagination и ниже после рендера контейнера с пагинацией ее вызовем.

Внутри этой функции мы на pagination, то есть на общий контейнер, при помощи addEventListener навешиваем слушатель события по событию клик.

И по этому событию у нас будет отрабатывать callback функция.

В эту callback функцию мы параметром передаем event, то есть событие.

И внутри этой функции добавляем условие.

Если у нас клик был не по лишке, то тогда мы будем просто выходить и ничего не делать.

А иначе мы в консоль будем выводить текст клик по лишке.

Вот у нас контейнер.

Если мы будем кликать внутри него, но при этом не по лишкам, то тогда у нас ничего происходить не будет.

Если мы будем кликать по странице, то в консоли увидим текст «клик по лишке».

И надо пояснить, что в самом условии.

В EventTarget у нас содержится элемент, на котором было событие.

При помощи метода closest мы проверяем, есть ли у него класс PaginationItem.

Если да,

то тогда в этом условии у нас будет true.

То есть это клик именно по лишке.

И при помощи восклицательного знака, то есть отрицания, мы это оборачиваем на обратное значение.

То есть если был клик по лишке, тогда это будет true и перевернется false.

И мы будем попадать в блок кода else.

Если был клик не по лишке, то тогда мы получим false.

При помощи отрицания перевернем это в true и попадем в блок кода thief.

Вот как-то так.

Вместо того, чтобы в консоль выводить текст, давайте мы будем менять значение переменной CurrentPage.

Для этого мы можем обратиться к свойству TextContent у элемента, на котором было событие.

То есть на котором был клик.

И обратите внимание, что я здесь именно меняю значение переменной CurrentPage, а не создаю при помощи Let новую переменную.

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

Сейчас значение переменной CurrentPage будет меняться по клику на лишке.

Обновление происходит, но еще необходимо добавлять класс Active в текущей лишке и при этом забирать его у той лишки, на которой он был.

Для этого мы можем создать переменную Currently и через DocumentQuerySelector получить лишку с классами PaginationItem и Active.

И у этой лишки при помощи класс List и метода Remove удалить этот класс Active, а лишке, на которой был клик, при помощи класс List и метода Add добавить этот класс Active.

И вот таким образом мы сделали переключение между страницами по клику на сами эти страницы.

И нам остается добавить логику переключения

По нажатию на кнопки «Вперед-назад» мы здесь заново будем отрисовывать продукты и помимо этого менять текущую страницу.

Поэтому после того, как мы отрисовали все лишки для страниц, можем через документ «QuerySelectorAll» по классу «PaginationItem» их все получить и записать в константу «LeaElements».

И ниже мы создаем функцию handle.pagination.

И на кнопке btn.next.pagination и btn.pref.pagination мы при помощи addEventListener навешиваем слушатель события по событию клик.

И по этому событию будем вызывать эту функцию.

И еще в эту функцию мы параметром передаем событие event.

Внутри этой функции мы создаем константу CurrentActiveLy и через DocumentQuerySelector получаем лишку с классами PaginationItem и Active.

Это у нас будет текущая активная лишка.

И ниже создаем переменную NewActiveLy.

В нее мы будем записывать новую активную лишку.

Как это сделать?

Мы можем добавить условие.

Если у нас элемент, на котором было событие, содержит класс JSPaginationBtnNext,

то тогда мы в переменную newActiveLie будем записывать следующую лишку.

Для этого нам необходимо у текущей лишки воспользоваться свойством nextElementSibling.

Это свойство содержит следующий соседний элемент.

А иначе мы в эту активную лишку

можем записывать предыдущий элемент.

Для этого необходимо у CurrentActiveLea воспользоваться свойством PreviousElementSibling.

Что значит иначе?

У нас эта функция будет вызываться либо по клику на кнопку вперед, либо по клику на кнопку назад.

И в else мы будем попадать тогда, когда нажимаем на кнопку назад.

Сейчас у нас текущая активная лишка

Первая и по нажатию на кнопку вперед новой активной лишкой становится вторая.

А по нажатию на кнопку назад у нас в new active.ly будет null, потому что перед первой страницей больше никаких нет.

Дальше давайте мы будем новой активной лишке добавлять класс active, а у текущей активной лишки его удалять.

Проверяем и все работает.

Но если мы будем на первой странице и нажмем на кнопку назад, или будем на последней странице и нажмем на кнопку вперед, то тогда у нас будет ошибка.

Для того, чтобы это исправить, перед тем как добавлять и удалять классы, мы можем добавить вот такое условие.

Если у нас в newRTF.ly будет now и при этом клик был по кнопке вперед,

с классом js-pagination-btn-next, то тогда мы в new-active-li будем записывать первый элемент из псевдо-массива li-elements.

А у первого элемента индекс равен нулю.

А если у нас будет просто в переменной new-active-li null, а это будет в том случае, когда мы находимся на первой странице и нажимаем на кнопку назад, то тогда в эту переменную мы записываем

Последний элемент из этого псевдомассива liElements.

Для этого нам необходимо из длины массива вычесть единичку.

И вот сейчас все работает.

и нам остается показывать те продукты которые должны быть на этой странице для этого мы можем по клику на кнопку вперед значение current page увеличивать на единицу а по клику на кнопку назад уменьшать на единицу и ниже мы также обрабатываем ситуацию когда находимся либо на последней странице и пытаемся попасть

на следующую либо на первой странице и пытаемся попасть на предыдущую то есть если у нас текущая страница будет больше чем длина псевдо массива li elements то тогда мы current page будем делать равным одному а если current page будет меньше единицы то тогда мы будем делать ее

равной длине псевдо-массива liElements.

И вот после того, как мы поменяли значение текущей страницы, нам необходимо снова вызвать функцию renderProducts.

И вот сейчас у нас все работает.

Текущая страница это обычно всегда первая, а вот количество продуктов мы можем выбирать сами.

Если бы у меня все 80 квадрокоптеров были разными, то есть с разными фотографиями, ценами, моделями, то я бы сделал по 8 или по 12 карточек на странице.

Но поскольку я 4 продукта раскопировал и они еще идут по 4 в строке, то мы даже визуально не увидим разницы.

Спикер 3

Поэтому на странице я буду показывать по 7 карточек.

Спикер 2

Само по себе у нас модальное окно состоит из двух элементов.

Это область затемнения слева и корзина справа.

И в стилях изначально для этой области затемнения задано свойство Display со значением None.

И помимо этого черный цвет фона, но при этом полностью прозрачный.

И нам необходимо по нажатию на иконку корзины...

добавить ему класс Active, где мы будем дисплей менять на блок, а у фона изменим значение прозрачности на 0.5.

Корзина у нас просто сейчас позиционирована, фиксирована, имеет ширину 400 пикселей и расположена на 400 пикселей справа.

То есть изначально ее просто не видно.

Поэтому нам необходимо ей добавить класс Active, в котором мы значение Write будем менять на 0.

В папке Modules давайте создадим новый модуль.

Назовем его CardPopup.

То есть он будет отвечать за модальное окно корзины.

Внутри этого модуля создадим две функции.

OpenCard и CloseCard.

Одна соответственно будет открывать модальное окно.

а вторая его закрывать.

И обе эти функции в этом модуле мы сразу экспортируем.

А в файле main.js импортируем их и вызываем.

Выше нам необходимо получить все элементы со страницы, с которыми мы будем работать.

Это корзина с классом JSCard.

Это область затемнения с классом JSOverlay.

Это кнопка корзины с классом JSCardBtn.

И это два элемента, у которых есть класс JSCloseCard.

А он есть у корестика внутри корзины и у дива с затемнением.

Поскольку у нас элементов с этим классом два, то мы их получаем при помощи QuerySelectorAll.

Внутри функции OpenCard мы на кнопку OpenCardButton при помощи AddEventListener навешиваем слушатель события по событию Click.

И по этому событию у нас будет отрабатывать Callback функция.

В ней мы для корзины при помощи ClassList и метода Add добавляем класс Active.

И то же самое делаем для затемнения.

И по нажатию на эту иконку у нас открывается корзина.

Причем сама корзина...

как бы плавно выезжает справа.

Так происходит потому, что мы ей добавляем transition именно для этого свойства и устанавливаем значение в 0,2 секунды.

Можем поменять значение на 2 секунды и увидим, что сейчас это происходит медленнее.

Но давайте вернем изначальное значение, потому что 2 секунды это слишком долго.

И внутри функции CloseCard мы при помощи метода forEach переберем псевдо массив CloseCardElements.

В callback функцию передадим элемент этого массива, item.

И на каждый item также навесим слушатель события по событию клик.

И вот по клику на эти элементы мы будем выполнять обратное действие.

То есть у корзины и у оверлея при помощи метода remove удалять класс active.

Если вы хотите сделать различную скорость открывания и закрывания корзины, то тогда можно указать transition и для класса active.

И здесь задать свое время.

За это время она будет открываться, а за это закрываться.

Спикер 3

Но я оставлю везде 0.2 секунды.

Спикер 2

И сейчас у нас корзина закрывается и по нажатию на крестик, и по нажатию на область затемнения.

Но при этом, при открытой корзине, мы можем скроллить содержимое страницы.

Давайте это запретим.

Для этого, когда мы открываем корзину для Body, через Style, для свойства Overflow, задаем значение Hidden.

А когда закрываем корзину...

то будем сбрасывать стиль на значение по умолчанию.

Таким образом, при открытом модальном окне скроллить мы больше не можем, но у нас появляется вот такой нежелательный сдвиг контента.

Этот сдвиг происходит на ширину скроллбара.

И чтобы это поправить, нам необходимо для body, для свойства margin-right, в качестве значения указать эту ширину.

Именно тогда, когда мы открываем корзину.

А когда ее закрываем, мы это значение меняем обратно на 0 пикселей.

В разных браузерах эта ширина scrollbar может быть различной, поэтому лучше ее вычислять.

Для этого мы можем воспользоваться вот такой вспомогательной функцией getScrollbarWith.

Здесь мы просто создаем некий вспомогательный элемент, задаем ему произвольное значение для ширины и высоты.

но при этом достаточно большое для появления scrollBar.

Дальше добавляем вертикальный scroll и делаем этот элемент невидимым для пользователей.

И после этого при помощи метода append добавляем его на страницу.

И дальше создаем переменную, которая будет вычислять эту ширину.

В свойстве offsetWidth у нас будет общая ширина div, включающая ширину scrollBar.

А в свойстве clientWidth будет только ширина видимой части div, без учета scrollBar.

И таким образом мы и получим эту необходимую ширину.

Больше нам этот вспомогательный div не понадобится и мы его удаляем.

И эта функция будет возвращать необходимую нам ширину.

Ниже мы создаем константу scroll и в нее присваиваем результат вызова функции getScrollBarVis.

И вот здесь передаем значение этой константы.

И таким образом мы избавились от этого скачка контента.

И сейчас мы полностью научились управлять появлением и исчезновением

модального окна на этом можно было бы закончить и оставить все так но у нас здесь так или иначе дублируются вот эти четыре строчки кода поэтому их можно вынести в отдельную функцию toggleCard здесь будет один и тот же код но в зависимости от того что мы будем передавать при вызове функции toggleCard true или false

У нас будет для Body для свойства Overflow либо добавляться значение Hidden, либо сбрасываться стиль.

Для свойства Margin Right, либо ширина, которую мы вычислили выше, либо 0 пикселей.

И для корзины и затемнения, либо добавляться, либо удаляться класс Active.

Счетчики необходимы для того, чтобы отслеживать или изменять количество позиций какого-либо продукта.

Позже мы научимся добавлять квадрокоптеры в корзину.

А пока что у нас здесь есть статичная верстка для двух продуктов.

И вот с ней мы и поработаем.

В файле index.html в диве с классом CardContainer есть два дива.

Один с классом CardEmptyContainer и второй с классом CardOdeContainer.

Так вот, для того, чтобы сейчас начать работать, первый div необходимо закомментировать, а второй раскомментировать.

Это на тот случай, если в исходниках у вас будет не так.

Помимо этого для корзины необходимо раскомментировать свойство Write со значением 0.

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

В самом конце необходимо вернуть это значение на минус 400 пикселей.

Вся логика связанная с корзиной у нас будет в модуле CardData.

В этом модуле мы создаем одноименную функцию CardData и ее здесь экспортируем.

А в файле main.js импортируем из этого модуля и здесь ее вызываем.

За обновление количества товаров в корзине у нас будет отвечать функция UpdateCardItemCount.

Здесь мы будем навешивать слушатель события не на каждый отдельный счетчик, а на всю корзину целиком.

То есть будем делегировать события.

Когда мы полностью закончим весь функционал для корзины...

Я на всех примерах, которые у нас будут, объясню, почему мы в той или иной ситуации использовали делегирование событий.

И заодно там мы еще допишем некоторые нюансы.

Внутри функции UpdateCardItemCount мы на нашу корзину Card навешиваем слушатель события по событию Click.

И по этому событию у нас будет отрабатывать Callback функция и в нее мы передаем Event, то есть событие.

Внутри этой callback функции мы добавляем условие.

Здесь мы будем проверять, на каком элементе у нас был клик в корзине.

Если таким элементом будет кнопка минус или кнопка плюс, то тогда мы в консоль будем выводить единичку.

Event – это событие.

Event target – это элемент, на котором было событие.

Событие у нас здесь клик.

И при помощи метода matches…

Мы проверяем, есть ли у этого элемента класс GS- или класс GS+.

Эти классы есть только у кнопки минус и у кнопки плюс.

В каждом счетчике я нажимаю на кнопку плюс, у нас появляется единичка.

Нажимаю на кнопку минус, у нас также появляется единичка.

Кликаю в любом другом месте, и у нас в консоли ничего нет.

Дальше, если мы попадаем в это условие, то есть если у нас был клик по кнопке минус или по кнопке плюс, то тогда мы создаем константу counter, и в ней у нас будет ближайший родительский элемент с классом jsCounter для элемента, на котором был клик.

Это мы можем сделать при помощи метода closest.

И ниже мы создаем переменную currentItems.

И в ней у нас будет текущее значение счетчика.

Мы через QuerySelector ищем элемент с классом GSCurrentItems и ищем его именно внутри нашего каунтера, а не внутри всего Document.

То есть смотрите, у нас был клик по кнопке минус или плюс.

Это EventTarget, то есть элемент, на котором было событие.

При помощи метода closest мы ищем ближайший родительский элемент с классом jsCounter.

Вот этот div.

И далее в нем мы уже ищем элемент с классом jsCurrentItems.

Это необходимо для того, чтобы когда мы нажимаем на кнопку плюс или кнопку минус, изменять именно это значение в этом счетчике.

а например не в этом.

Когда мы нажимаем вот здесь на плюс или минус, мы будем подниматься до вот этого родительского элемента и в нем искать вот это количество.

И сейчас ниже мы можем добавить два отдельных условия.

Если у нас элементом, на котором был клик, будет кнопка плюс, то тогда мы будем CurrentItems увеличивать на единицу.

Для этого мы должны обратиться к свойству TextContent и при помощи префиксной формы инкремента увеличить

то текущее значение которое там есть а если был клик по кнопке минус то мы будем уменьшать это значение на единицу при помощи префиксной формы декоромента но если мы сейчас проверим то у нас ничего работать не будет и появится ошибка потому что объявленная вот в этой области видимости переменная currentItems недоступна вот здесь поэтому объявить ее необходимо выше условия и вот здесь

Мы уже будем ее не объявлять при помощи слова let, а просто присваивать в нее вот этот элемент со страницы.

И вот сейчас у нас все работает.

Только по нажатию на кнопку минус мы можем сделать значение счетчика меньше единицы.

И это нам необходимо ограничить.

Помимо этого у нас кнопка минус сейчас всегда выглядит неактивной.

но по нажатию на нее значение изменяется так происходит потому что для кнопки минус добавлен класс disabled а в этом классе у нее такой слегка серый цвет текста давайте временно мы этот класс удалим поскольку он необходим для второго способа в первом способе мы сейчас эту кнопку сделаем неактивной при помощи атрибута disabled и у нас таких кнопок здесь две

Далее давайте мы в этом же условии по нажатию на кнопку минус или кнопку плюс, помимо того, что получаем текущее значение счетчика, еще будем получать кнопку минус именно в этом счетчике и записывать ее в переменную минус btn.

Это необходимо для того, чтобы по нажатию на кнопку плюс удалять атрибут именно у той кнопки минус, которая находится в том же счетчике.

Если был клик именно по кнопке плюс, то у кнопки минус мы можем при помощи метода remove attribute удалить атрибут disabled.

Я нажимаю на кнопку плюс и кнопка минус у нас становится активной.

Вот эта кнопка сейчас не активна, я на нее нажимаю и значение не изменяется.

Нажимаю на плюс и она становится активной.

Дальше необходимо подумать о том, в какой момент по нажатию на кнопку минус мы будем добавлять ей атрибут обратно.

А добавлять его необходимо тогда, когда вот здесь значение будет равно двум.

Мы нажимаем на кнопку минус.

У нас значение уменьшается до единички и в этот момент кнопка становится неактивной.

Поэтому тогда, когда у нас был клик по кнопке минус, мы можем добавить условие.

Если у нас текущее значение больше 2, то тогда мы будем просто это текущее значение уменьшать на единицу.

А иначе мы его будем уменьшать на единицу и при этом кнопке минус при помощи метода setAttribute добавлять атрибут disabled.

Вот в это условие else мы будем попадать тогда, когда текущее значение будет равно 2.

Здесь мы это текущее значение сначала уменьшаем до единички, а затем делаем кнопку неактивной.

И значение дальше мы уменьшать не сможем.

Проверяем и у нас все работает как надо.

И это был первый способ.

Для того, чтобы вам показать второй, этот атрибут disabled необходимо удалить и вместо этого добавить класс disabled.

И здесь, за исключением одного момента, все будет точно так же.

Только вместо методов removeAttribute и setAttribute, которые удаляли и добавляли атрибут, мы воспользуемся classListRemove и classListAdd, которые будут удалять и добавлять класс.

И у нас все будет точно так же работать.

А этот единственный момент вот в этом условии else if.

Здесь мы проверяли, чтобы значение было больше 2.

Здесь мы делаем то же самое.

Если во втором способе просто оставить else, то тогда к кнопке будет добавляться класс, в котором у нас задается серый цвет текста.

Но при этом значение мы также сможем уменьшать и дальше.

Поэтому здесь мы обязательно проверяем.

чтобы текущее значение было равно двум только в этом случае мы еще раз уменьшаем значение на единицу и добавляем класс disabled и в оставшемся случае когда значение будет равно одному мы по нажатию на кнопку минус ничего делать не будем и вот таким образом мы с вами научились изменять значение счетчика нам остается научиться изменять цену и понять почему мы используем делегирование событий

Для корзины у нас уже создан модуль CardData и в нем написана функция для обновления количества продуктов в корзине.

Сейчас вот таким образом по нажатию на кнопки плюс и минус мы можем изменять значение счетчика у каждого отдельного продукта.

Корзина у нас находится в модальном окне, и чтобы ее каждый раз не открывать по нажатию на иконку корзины, мы в стилях временно добавляем свойство Write со значением 0.

Сейчас у нас в корзине есть два продукта.

Но это просто для них добавлена верстка.

Давайте их мы закомментируем.

И у нас сейчас в корзине остается ваш заказ и того, и кнопка «Оформить заказ».

Первое, чему мы научимся, это добавлять продукты в корзину по нажатию на кнопку «В корзину».

Для этого со страницы мы через DocumentQuerySelector получаем улку с классом JSProductsList и записываем ее в константу ProductsList.

Ниже мы пишем стрелочную функцию, которая будет добавлять продукт в корзину.

И здесь же сразу ее вызываем.

Внутри этой функции...

на улку то есть на продукт лист мы навешиваем слушатель события по событию клик и по этому событию у нас будет отрабатывать callback функция и в нее параметром мы передаем event события то есть таким образом мы воспользовались

делегированием события.

Мы будем слушать события клика не на каждой отдельной кнопке, а на всем списке продуктов.

И если у нас EventTarget, то есть элемент, на котором было событие, содержит класс JSByButton, то тогда мы пока что в консоль будем выводить единичку.

Я кликаю по кнопкам, у нас в консоли появляется единичка.

Я кликаю в других местах, и у нас ничего не происходит.

Далее нам необходимо получать данные того продукта

на котором был клик по кнопке.

Для этого мы внутри условия для EventTarget, то есть в данном случае уже для кнопки, добавляем метод Closest.

И этот метод будет искать ближайший родительский элемент, который будет соответствовать классу GSProduct.

И его, этот элемент, мы будем добавлять в константу Product.

Дальше нам из этого продукта

необходимо получать его картинку, его модель, его цену и его ссылку, из которой мы будем брать ID.

Для этого мы создаем 4 константы и в них через querySelector по соответствующему классу получаем эти элементы.

Но здесь важный нюанс, что мы ищем не в document, а именно в product, потому что нам нужны данные именно этого продукта.

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

Проверяем и у нас все работает.

Отлично.

Сейчас мы можем все эти данные собирать в одном объекте.

Для того выше давайте создадим константу product.info и в ней у нас здесь будет пустой объект.

А тогда когда у нас был клик по кнопке, мы в него будем добавлять ключ id и значением будет id-шник у ссылки.

Для того чтобы его получить, мы можем воспользоваться методом getAttribute и в кавычках передать название атрибута id.

Таким образом мы получаем значение атрибута id.

Для ключа Model у нас будет содержимое константы ModelCard.

Для ключа Price содержимое константы PriceCard.

И для ключа Photo будет SRC константы ImageCard.

Что это за элементы мы можем посмотреть в модуле ProductCards.

И давайте весь этот объект мы выведем в консоль.

По клику на первую кнопку мы видим, что в этом объекте у нас данные для первого продукта.

Если мы кликнем на кнопку в корзину для второго продукта, то здесь уже будут его данные.

И после того, как мы собрали всю информацию, можно рендерить продукт в корзине.

Давайте за это у нас будет отвечать отдельная функция.

И здесь мы ее просто вызовем.

А напишем ниже.

Для этого функцию addProductToCard пока что свернем.

В этой функции мы должны создать разметку вот такой лишки.

Для этого при помощи метода createElement мы создаем тег li и записываем его в константу li.

Дальше этой лишке

При помощи класс List и метода Add добавляем три класса.

CartItem, Column и JSCartItem.

Все эти классы у нее были вот здесь.

Дальше мы этой лишке при помощи innerHTML добавляем следующую разметку.

Всю эту разметку я взял отсюда.

Поскольку мы будем использовать данные из объекта, то нам необходимо это все взять в косые кавычки.

И далее для значения IDшника подставить значение свойства ID у объекта ProductInfo.

Для значения SRC необходимо подставить значение свойства фото у этого же объекта.

Вместо модели у нас будет значение свойства Model объекта ProductInfo.

И в качестве цены значение свойства Price.

Помимо этого я еще создал атрибут DataPrice.

и передал в него это же значение когда мы будем подсчитывать стоимость он нам может понадобиться и последнее что нам осталось в этой функции сделать это в эту улку добавить нашу лишку у нее класс jsCardList по этому классу мы ее через querySelector получаем со страницы и записываем в константу CardList и после того как добавили разметку

В конец этой улки при помощи метода append добавляем лишку.

И сейчас у нас по нажатию на кнопку продукт добавляется в корзину.

Но при повторном добавлении в корзине появляется новая позиция с этим же продуктом.

Давайте мы научимся их объединять и при этом увеличивать значение счетчика.

Для этого давайте по нажатию на кнопку, после того как мы заполнили данными объект, будем создавать константу ProductInCard.

И в ней мы через SquareSelector внутри корзины CardList будем получать элемент с ID, значение которого будет совпадать со значением свойства ID объекта ProductInfo.

Вот мы добавили первый продукт в корзину.

У нас в продукт inCard будет now, потому что в этой корзине еще нет элемента с таким же id, поскольку мы сначала создаем такую константу, а уже потом добавляем этот продукт в корзину.

Если я впервые добавлю второй продукт, то в ProductInCard также будет Now.

Если я сейчас попытаюсь добавить первый или второй продукт, то в ProductInCard у нас уже будет элемент с таким ID.

Каждый раз по нажатию на кнопку у нас в этот объект записываются новые данные.

актуальная для добавленного продукта.

И сейчас, зная это, мы можем добавить следующее условие.

Если ProductInCard существует, то тогда мы создаем константу CurrentItemsProduct.

И в нее мы получаем через QuerySelector по классу JS CurrentItems элемент внутри ProductInCard.

То есть это вот этот элемент, в котором у нас находится значение счетчика.

И это значение мы можем изменить при помощи свойства TextContent.

В него мы записываем это же текущее значение.

При помощи метода parseInt преобразуем его в целое число, чтобы корректно складывать числа.

И прибавляем к нему единицу.

А иначе, то есть если такого продукта в корзине еще нет, то тогда мы его добавляем новой позицией.

Сейчас у нас корзина пустая.

И я в нее хочу добавить первый продукт.

Добавляю.

Такого продукта там еще не было.

И в продукт inCard мы получаем null.

Этот null в условии преобразуется к булиновому типу false.

И мы в этот блок кода не попадем.

а попадем в Elsa.

И Elsa отрендерит в корзине этот продукт.

Если мы попробуем его повторно добавить, то тогда в продукт Incarp у нас уже будет элемент.

Этот элемент в условии будет преобразован к булиновому типу Troll.

И тогда здесь мы значение

увеличим на единичку и при этом в блок кода элса не попадем то есть не будем рендерить еще одну такую же позицию и здесь не смотрите что при добавлении 5 продукта он появляется отдельной позиции несмотря на то что фото модель цена все одно и то же но айдишники у них отличаются

Мы эти продукты копировали для того, чтобы посмотреть, как работает погенация.

Но вообще каждый продукт должен иметь уникальную информацию.

То есть свою модель, свою цену, свое фото и все остальное.

Правда сейчас с объединением позиций для одного продукта у нас для кнопки минус по-прежнему остается неактивный класс.

Даже тогда, когда позиций 2 и больше.

Поэтому при добавлении продукта в корзину...

Нам понадобится еще вот эта строчка кода.

Единственное, что эту кнопку тут необходимо снова получить.

И сейчас при повторном добавлении продукта у кнопки минус пропадает этот неактивный класс.

Если вот здесь вместо класса вы захотите использовать атрибут Disabled, то вот здесь вместо того, чтобы удалять класс, нам необходимо будет удалять атрибут.

И у нас все будет точно так же работать.

Но я оставлю вариант с классом.

Следующее чему мы научимся это удалять продукты из корзины.

Для этого создадим новую функцию removeProductFromCard и ниже сразу ее вызовем.

В ней мы на улку CardList навешиваем слушатель события по событию клик.

Если у нас был клик в списке корзины, то тогда будет отрабатывать callback функция.

В нее мы снова передаем event и добавляем условия.

Если элемент, на котором было событие, содержит класс JSRemove, а этот класс есть только у корестиков внутри лишек.

Так вот, если клик был по корестику, то тогда мы создаем константу CardItem и в нее записываем всю лишку, которая содержала этот корестик.

И при помощи метода remove эту лишку удаляем.

Проверим, добавим продукты в корзину и попробуем удалить.

Нажимаю на крестик, у нас удаляется первый продукт.

Если кликаю не по крестику, а в любом другом месте, то ничего не происходит.

Нажимаю на крестик, продукт удаляется.

Дальше давайте мы будем менять статус корзины.

Если в ней нет продуктов, то будем показывать один блок.

Если в ней есть хотя бы один продукт, то покажем второй блок, в котором будет текст «Ваш заказ» и того, и кнопка «Оформить заказ».

У нас в верске в карт-контейнере есть два дива.

Один с классом «CartEmptyContainer» и второй с классом «CartOdeContainer».

Когда корзина пустая, мы будем показывать «CartEmpty», когда есть продукты «CartOde».

Для этого сейчас нам необходимо раскомментировать первый див.

И в корзине мы сейчас видим и то, и то.

В модуле CardData оба эти элемента мы получаем со страницы.

И ниже создадим функцию ToggleCardStatus.

В ней мы добавим такое условие.

Если у нас в корзине карт будет элемент с классом JSCardItem, то тогда для дива CardOdd мы удаляем класс Hidden, а для дива CardEmpty добавляем класс Hidden.

А иначе делаем обратное действие.

Для CartOd добавляем этот класс, а у CartEmpty его удаляем.

В классе Hidden у нас добавлено свойство Display со значением None.

И ниже мы здесь вызываем эту функцию.

Сейчас она вызовется только при загрузке страницы.

И мы видим, что корзина пуста.

Я пытаюсь добавить продукты, но при этом ничего не меняется.

Чтобы это исправить, нам необходимо вызывать эту функцию еще и тогда, когда мы нажимаем на кнопку в корзину.

Мы это делаем внутри функции Add Product to Cart.

И после того, как мы отрендерили продукт в корзине,

уже можно поменять ее статус.

И сейчас у нас уже при добавлении продуктов меняется ее состояние.

У вас может возникнуть логичный вопрос, а зачем ее тогда вызывать при загрузке страницы?

Ведь мы можем всегда вначале показывать пустую корзину.

На самом деле нет.

Если бы мы сохраняли продукты в корзине через Local Storage, то при обновлении страницы корзина была бы не пустой.

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

можем вызвать ее сразу после удаления лишки и сейчас при удалении последнего продукта у нас отображается информация о том что корзина пуста и последнее чему мы здесь научимся это подсчитывать итоговую стоимость для этого создадим стрелочную функцию calculate total cart value и давайте подумаем когда мы будем пересчитывать итоговую стоимость тогда когда добавляем продукт в корзину

Тогда, когда его удаляем.

И тогда, когда увеличиваем или уменьшаем количество позиций.

И помимо этого, если в дальнейшем мы будем сохранять продукты через Local Storage, то тогда эта функция еще должна вызываться при загрузке страницы.

Поэтому давайте вызовем ее вот здесь.

Также вот здесь, когда мы нажимаем на кнопку «плюс» или кнопку «минус».

Выше у нас есть общее условие для плюса и минуса.

И логично было бы вызвать ее в одном месте.

Но нам перед этим необходимо изменить значение счетчика.

Поскольку мы будем это количество умножать на стоимость одной единицы.

И далее эти итоговые стоимости отдельных продуктов будем складывать в одну общую итоговую стоимость.

Если вместо классов вы будете использовать атрибуты,

то соответственно ее нужно вызвать вот в этих местах.

При клике на кнопку в корзину мы ее вызовем вот здесь и по нажатию на крестик вот в этом месте.

Внутри этой функции мы будем получать через querySelectorAll все лишки с классом JSCardItem.

То есть все продукты.

И они у нас будут в константе CardItems.

А в константу CardTotalPrice мы получим элемент с классом JS CardTotalPrice.

Сейчас у нас в этом элементе 14000.

Ниже мы создаем переменную

totalCardValue и изначально она будет равна нулю мы будем пробегаться по каждому продукту в корзине и к этому нулю прибавлять его итоговую стоимость пройтись по псевдо массиву CardItems мы можем при помощи метода forEach

Этот метод принимает callback функцию, а эта callback функция параметром принимает item, то есть элемент этого псевдомассива.

И давайте для каждого элемента этого массива, то есть для каждого продукта, мы будем создавать константу itemCount и в ней у нас будет элемент,

с количеством штук и еще создадим константу itemPrice в которой будет элемент со стоимостью продукта и давайте обе эти константы мы выведем в консоль при добавлении первого продукта у нас в itemCount будет вот этот элемент а в itemPrice вот этот элемент при добавлении второго продукта у нас будет уже массив из двух элементов вот этот itemCount и вот этот itemPrice будут браться для первого элемента

А вот эти itemCount и itemPrice для второго элемента.

И нам необходимо для каждого продукта перемножить itemCount на itemPrice.

Для этого мы создадим константу itemTotalPrice, то есть это итоговая стоимость одного продукта.

В itemCount у нас элемент.

Чтобы получить его содержимое, мы можем воспользоваться свойством TextContent.

Если мы посмотрим на тип данных, то это будет строка.

Мы ее при помощи метода parseInt преобразуем к целому числу.

И то же самое делаем для цены.

И перемножаем их.

Но в itemTotalPrice у нас 129.

А должно быть 129 тысяч.

Так происходит потому, что у нас вот здесь...

Каждые три знака отделяются пробелом.

Для того, чтобы это исправить, мы можем к строке добавить метод split.

Он разобьет строку в массив по разделителю пробел.

И дальше мы этот массив при помощи метода join обратно объединяем в строку с указанным разделителем.

А поскольку там разделителя нет, то это будет одна строка.

И сейчас мы видим правильную итоговую стоимость для одного продукта.

При изменении количества штук она также правильно пересчитывается.

При добавлении двух продуктов для первого итоговая стоимость

будет 362 тысячи а для второго 129 тысяч и сейчас нам остается при переборе этого псевдо массива к общей итоговой стоимости прибавлять итоговую стоимость одного продукта и после метода forEach то есть после того как к этому значению

Прибавились все стоимости отдельных продуктов.

Мы уже можем в карт TotalPrice при помощи свойства TextContent записывать значение этой переменной TotalCardValue.

Проверим.

Добавляю первый продукт.

У нас в итоговую стоимость записалось 362 тысячи.

Добавляю второй продукт.

Итоговая стоимость пересчиталась.

У нас при каждом добавлении вызывается эта функция.

В ней каждый раз создается переменная TotalCardValue.

И изначально она всегда будет равна 0.

И когда мы добавили второй продукт, у нас появилась эта переменная, равная 0.

К ней прибавилось 362 тысячи и затем 129 тысяч.

Мы увеличили количество позиций на 1.

И у нас снова создалась переменная равная 0.

К ней прибавилось 362 тысячи.

И 129 умноженное на 2.

Каждый раз при любом изменении стоимость пересчитывается с 0.

Здесь я бы еще для стоимости каждые 3 знака

отделял пробелом, чтобы было легче воспринимать сумму.

Для этого вот здесь мы можем добавить вот такой форматор.

И перед тем, как выводить значение итоговой стоимости в карте TotalPrice, мы будем его передавать в метод формат.

И этот метод будет нам добавлять этот пробел.

В разных интернет-магазинах по-разному выводят эту стоимость.

Где-то бывает так, что это стоимость одной единицы, где-то это произведение стоимости единицы на количество,

а где-то бывает так, что слева добавляют стоимость одной единицы, а справа уже пересчитывают произведение.

Давайте мы слева ничего добавлять не будем, а сразу вот здесь пересчитаем эту стоимость.

Если мы сейчас попытаемся вот таким образом

вывести итоговую стоимость по одному продукту, то у нас будет ошибка.

Смотрите, третий продукт стоит 49 тысяч.

Я увеличиваю количество позиций 98.

Все правильно.

Увеличиваю дальше и стоимость становится 294 тысячи.

А должна быть 3 умножить на 49 тысяч.

То есть 147 тысяч.

Уменьшаем количество, а стоимость увеличивается.

А если уменьшим до 1, то могли бы купить по такой же цене, как и 2.

Неплохо.

Почему так происходит?

Когда мы увеличиваем значение счетчика...

то он становится равным 3.

И 3 умножается на 98 тысяч.

Хотя нам надо сейчас умножать на стоимость 1 единицы.

И из-за этого мы получаем 294 тысячи.

И вот чтобы это исправить, я специально в лишке добавил дата атрибут.

DataPrice.

И в этом дата атрибуте

у нас всегда будет стоимость одной единицы.

Поэтому если мы хотим здесь показывать стоимость одной единицы, то тогда нам этот вариант подходит.

Если хотим вот здесь показывать произведение стоимости единицы на количество, то тогда вместо TextContent мы будем использовать значение атрибута DataPrice.

Для этого нам необходимо прописать DataSet и указать название этого атрибута Price, потому что DataDefice нам писать не надо.

мы указываем название которое идет после и вот сейчас у нас три штуки стоят 147 тысяч вот так уже подсчитывается правильно единственное что эту стоимость я бы также обернул в форматор чтобы вот здесь был пробел и давайте проверим еще раз добавляем продукты в корзину изменяем их количество удаляем и у нас все работает все вот эти вспомогательные консоль логи

можно закомментировать.

И в конце давайте мы немного поговорим о делегировании событий.

Например, когда мы отслеживали клики по кнопкам плюс и минус, вместо того, чтобы навесить слушатель события на каждую отдельную кнопку, мы навесили его на корзину.

В принципе все хорошо, но когда мы кликаем в любом месте корзины, не по кнопкам плюс и минус, у нас все равно создаются вот эти две переменные.

Помимо этого, мы каждый раз проверяем все остальные условия.

Даже если мы в них не попадаем, то все равно у нас при каждом клике проверяется содержимое этих условий.

То есть был ли элемент, на котором был клик, плюсом или минусом.

И таких условий у нас здесь немало.

Поэтому при делегировании...

Лучше в начале всегда добавлять такое условие.

Если элементом, на котором было событие, будет не тот элемент, который мы хотим обработать, то тогда мы будем сразу останавливать выполнение функции.

Давайте, чтобы вам было понятно, мы перед Return в консоль будем выводить единичку, а после выводить двоечку.

Сейчас у меня был клик по кнопке плюс, поэтому в консоли мы видим двойку.

Кликаю в любом месте корзины,

нас выводится единичка но двоечка не выводится поскольку клик был не по кнопке минус и не по кнопке плюс поэтому мы единичку вывели и после этого сразу вышли из этой функции то есть у нас весь код ниже в данном случае не выполнится и такое же условие мы можем добавить везде где использовали делегирование событий это тогда когда навешивали

слушатель события не на каждую отдельную кнопку, а на весь список продуктов.

Поэтому вот здесь у нас будет такое условие, если клик был не по кнопке, то тогда код ниже мы выполнять не будем.

И то же самое мы делали при удалении продукта из корзины.

Вместо того, чтобы навешивать слушатель события на крестик, мы его добавляли на весь список продуктов в корзине.

И еще такое же условие

мы добавили в погенации при этом здесь у нас конструкция if-else при делегировании с удалением и добавлением продуктов можете сделать точно также и когда необходимо использовать делегирование событий первый момент если у нас много однотипных элементов например кнопок и на каждую необходимо навесить слушатель события то в данном случае лучше делегировать это событие их общему родителю

Много это хотя бы больше 10.

И второй случай, когда мы используем делегирование, это когда у нас есть динамически добавляемые элементы.

Если элементы добавляются в дом после начальной загрузки страницы, то нам не нужно навешивать обработчики на каждый новый элемент отдельно.

И давайте в конце свойство write со значением 0 у корзины закомментируем, чтобы работало модальное окно.