Фотогалерея товаров для интернет-магазина.
Вступление.
В различных интернет-магазинах, в карточке товара, очень часто используется фотогалерея, с помощью которой этот товар можно рассмотреть подробнее, в разных проекциях. В этой статье мы подробно рассмотрим создание такой галереи для интернет-магазина. Так же мы создадим удобную, универсальную систему управления этой галереей.
Для начала, давайте определимся со структурой фотогалереи и способами управления ею.
Итак, структура галереи для интернет-магазина будет состоять из трёх основных блоков:
1. блок с тумбами картинок;
2. блок, куда будет выводится полноразмерная картинка;
3. блок с кнопками навигации по галереи.
Для управления сменой картинок, давайте используем максимально возможные варианты:
— кнопками навигации «предыдущая», «следующая», «первая», «последняя»;
— кликом по тумбе картинки;
— кликом по большой картинке, при этом будет выводится следующая картинка;
— клавишами со стрелочками «влево» и «вправо»;
— колёсиком мышки.
Вроде со всем определились, ничего не упустили. Давайте приступим непосредственно к созданию фотогалереи.
HTML-вёрстка галереи для интернет-магазина.
HTML-вёрстка галереи очень простая и, как писалось ранее, состоит всего из трёх блоков:
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 |
<div class="wrap"> <h1>Просмотр фотографий</h1> <div class="flex" data-gallary> <div class="container"> <div class="viewport"> <div class="thumbs content-box"> <img src="images/thumbnails/IMG_2212.jpg"> <img src="images/thumbnails/IMG_2222.jpg"> <img src="images/thumbnails/IMG_2227.jpg"> <img src="images/thumbnails/IMG_2235.jpg"> <img src="images/thumbnails/IMG_2259.jpg"> <img src="images/thumbnails/IMG_2269.jpg"> <img src="images/thumbnails/IMG_2273.jpg"> <img src="images/thumbnails/IMG_2287.jpg"> </div> </div> </div> <div> <div class="photo-box"> <img src="images/photos/IMG_2212.jpg"> </div> <div class="flex control-row"> <button type="button" class="btn" data-control="first">First</button> <button type="button" class="btn" data-control="prev">Previous</button> <button type="button" class="btn" data-control="next">Next</button> <button type="button" class="btn" data-control="last">Last</button> </div> </div> </div> </div> |
В данном варианте, блок с тумбами фотографий расположен слева от основного фото. Вы его можете расположить и справа, и вверху, и под основной фотографией — это ни как не повлияет на функционирование галереи.
Тумбы и большие фото разнесены по разным папкам — thumbnail и photos соответственно. Их можно было бы положить и в одну папку, добавив именам картинок префиксы, чтобы различать, где тумба, а где полноразмерное фото. При этом сам js-скрипт не изменится.
Как видите из вёрстки, я не стал оборачивать ни тумбы, ни кнопки навигации в дополнительные <div>
или <li>
— в этом нет совершенно никакой необходимости и незачем усложнять вёрстку и замусоривать её лишними элементами.
Ещё один немаловажный момент. В блоке <div class="photo-box"></div>
по умолчанию находится фотография. Это необходимо для того, чтобы пока не загрузился скрипт, в галерее не было пустого места.
Не нужно оборачивать тумбу ссылкой, используя её
href
, как указатель на полноразмерную фотографию. Как с точки зрения семантики, так и с точки зрения СЕО — тэг <a>
должен использоваться только для формирования ссылок, ведущих на другие страницы сайта или другой интернет-ресурс.JS-скрипту, для вывода полноразмерного фото, достаточно
src
тумбы.
Таблица стилей галереи для интернет-магазина.
Представлены только стили, относящиеся непосредственно к самой галерее.
Для уменьшения размера таблицы стилей, я не буду приводить свойства с вендорными префиксами, обеспечивающими кроссбраузерность. Не забывайте прописывать их при реализации своих проектов.
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 56 57 58 59 60 61 62 |
button { width: 100px; height: 30px; display: block; font-weight: 500; font-size: 12px; line-height: 24px; color: #eee; font-family: 'Roboto', sans-serif; text-decoration: none; text-transform: uppercase; text-align: center; float: left; margin-right: 10px; user-select: none; border: solid 1px #357ebd; border-radius: 3px; outline: none; background: #428bca; cursor: pointer; transition: all 0.2s; } button:hover { border-color: #285e8e; background: #3276b1; } .photo-box { width: 640px; height: 426px; outline: solid 1px #666; cursor: pointer; } .container { width: 176px; height: 426px; position: relative; margin-right:12px; } .viewport { overflow: hidden; position: absolute; left: 2px; right: 3px; top: 0; bottom: 0; } .content-box { height: 100%; position: absolute; left: 0; right: -17px; overflow-y: scroll; } .thumbs { overflow: auto; } .thumbs img { cursor: pointer; } .thumbs img + img { margin-top: 2px; } .control-row { padding: 20px 0 30px; } |
Пишем JS-скрипт фотогалереи для интернет-магазина.
И вот мы подошли к написанию JS-скрипта вывода и управления галереей интернет-магазина. Первое, с чего мы начнём, это создадим анонимную самозапускающуюся функцию, внутри которой и будет расположен наш код. Вообще, надо взять за правило ограничивать область видимости скрипта, чтобы не было конфликтов с другими JS-скриптами подключенными к странице. Мало ли, совпадут названия переменных или функций…
1 2 3 4 5 6 |
;(function() { 'use strict'; })(); |
При написании JS-скрипта мы будем использовать конструкцию Class. Это позволит создать несколько экземпляров галереи товаров для интернет-магазина на одной странице.
Создание экземпляров фотогалерей товаров для интернет-магазина.
В-первую очередь, создадим коллекцию фотогалерей, которые расположены на странице. Далее, с помощью метода for...of
переберём полученную коллекцию, при этом мы выполним следующие действия:
— создадим экземпляр текущей галереи, используя конструктор класса Gallery
;
— зарегистрируем обработчики событий.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 |
;(function() { 'use strict'; class Gallery { constructor(gallery) { } } // выбираем все фотогалереи на странице const galleries = document.querySelectorAll('[data-gallary]'); // перебираем полученную коллекцию элементов for (let gallery of galleries) { // создаём экземпляр фотогалереи товаров для интернет-магазина const goodsgallery = new Gallery(gallery); } })(); |
Весь дальнейший JS-код мы будем писать внутри конструкции class Gallery { ... }
.
Прежде всего рассмотрим конструктор класса Gallery
. Конструктор инициализирует ряд объектов и переменных, содержащих информацию об экземпляре галереи. В качестве аргумента конструктор принимает объект галереи товара, экземпляр которого создаётся в данный момент.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 |
class Gallery { constructor(gallery) { // контейнер для маленьких картинок (тумб) this.thumbsBox = gallery.querySelector('.thumbs'); // коллекция маленьких картинок (тумб) this.thumbs = this.thumbsBox.querySelectorAll('img'); // объект, в который будем выводить большую картинку this.image = gallery.querySelector('.photo-box img'); // объект (родительский элемент), содержащий кнопки навигации this.control = gallery.querySelector('.control-row'); // кол-во фотографий в галереи this.count = this.thumbs.length; // индекс отображаемой фотографии, при инициализации скрипта // он по умолчанию равен 0 this.current = 0; // регистрируем обработчики событий на странице с фотогалерей this.registerEventsHandler(); } } |
Конструктор, кроме инициализации объектов и переменных, вызывает функцию registerEventsHandler()
, которая регистрирует обработчики событий на странице:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
registerEventsHandler(e) { // клик по контейнеру '.control-row', в котором находятся кнопки // управления this.control.addEventListener('click', this.buttonControl.bind(this)); // клик по большой картинке this.image.addEventListener('click', this.imageControl.bind(this)); // вращение колёсика мыши this.image.addEventListener('wheel', this.wheelControl.bind(this)); // управление стрелками 'влево' и 'вправо' document.addEventListener('keydown', this.keyControl.bind(this)); // клик по тумбе this.thumbsBox.addEventListener('click', this.thumbControl.bind(this)); } |
Вывод полноразмерной фотографии в галерее для интернет-магазина.
Теперь рассмотрим функцию вывода полноразмерной фотографии — showPhoto()
. Аргументом этой функции является индекс или текущей тумбы, или рассчитанный функциями управления выводом полноразмерного фото.
Итак, на вход функции showPhoto
поступил индекс. Считываем у тумбы с таким индексом значение атрибута src
и заменяем в нём название папки, в которой хранятся фотографии с thumbnails
на photos
. В результате такой нехитрой операции получаем путь (src
) к большой фотографии. Полученное значение присваиваем атрибуту src
объекта image
, в результате чего, на экран выводится фото в полный размер.
Код функции showPhoto()
и комментарии к нему:
1 2 3 4 5 6 7 8 9 10 11 12 |
showPhoto(i) { // используя полученный в качестве аргумента индекс // получаем 'src' тумбы в коллекции const src = this.thumbs[i].getAttribute('src'); // полученный 'src' прописываем у большой картинки, предварительно // изменив путь (название папки) this.image.setAttribute('src', src.replace('thumbnails', 'photos')); // устанавливаем текущий индекс равным индексу тумбы в коллекции this.current = i; } |
Ранее говорилось, что можно и тумбы, и полноразмерные фото можно положить в одну папку, присвоив им разные префиксы. Давайте посмотрим, что нужно изменить в функции showPhoto
для этого. Обратите ещё раз внимание на строку:
1 2 3 |
this.image.setAttribute('src', src.replace('thumbnails', 'photos')); |
Замените в ней thumbnails
и photos
на префикс_тумбы
и префикс_фото
соответственно. Больше ничего исправлять не нужно.
На данном этапе у нас уже есть вполне работоспособная фотогалерея товаров для интернет-магазина. Полноразмерные фото переключаются при клике по тумбе, но мы хотели сделать расширенное управление галереей. Давайте расширим функционал нашего js-скрипта.
Расширенное управление фотогалереей для интернет-магазина.
Прежде чем начинать разбираться с функциями управления галереей, нужно вспомнить основы JavaScript, а именно, взятие остатка %
. Результат a % b
– это остаток от деления a на b. На этом основано получение индекса очередной фотографии, который является аргументом функции showPhoto
.
Листаем галерею с помощью кнопок навигации.
Для управления фотогалереей кнопками навигации будем использовать делегирование события. Обработчик события будем вешать не на каждую кнопку навигации, а на их родительский элемент control
, сократив таким образом, значительное количество JS-кода. При срабатывании обработчика вызывается функция buttonControl()
, в которой реализован весь алгоритм управления.
Код функции buttonControl()
и комментарии к нему:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
buttonControl(e) { // если click был сделан вне кнопок, прекращаем работу функции if (e.target.tagName != 'BUTTON') return; const ctrl = e.target.dataset.control; // каждому свойству объекта соответствует формула расчёта // индекса, учитывающая кол-во фотографий и текущий индекс let argControl = { first: 0, last: this.count - 1, prev: (this.count + this.current - 1) % this.count, next: (this.current + 1) % this.count }; // индекс следующей фотографии, в зависимости от функционала // кнопки навигации const i = argControl[ctrl]; this.showPhoto(i); } |
Листание галереи кликом по полноразмерной фотографии.
Вешаем на объект image
обработчик события. Передаём функции showPhoto
индекс очередного фото, равный остатку от деления увеличенного на единицу текущего индекса на количество фотографий в галерее.
При срабатывании обработчика события вызывается функция imageControl()
.
1 2 3 4 5 6 |
imageControl(e) { // показываем следующее фото this.showPhoto((this.current + 1) % this.count); } |
Теперь можно кликать по полноразмерному фото, листая галерею по кругу.
Листаем галерею стрелками «влево» и «вправо» на клавиатуре.
С помощью функции addEventListener
вешаем на страницу с фотогалереей обработчик события keydown
— нажатие на любую клавишу клавиатуры. При срабатывании обработчика события keydown
, вызывается функция keyControl()
.
Поучаем код нажатой клавиши. Если этот код равен 37 (влево) или 39 (вправо), вызываем функцию showPhoto
с соответствующим аргументом. В противном случае, прекращаем работу функции.
Код функции keyControl()
и комментарии к нему:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
keyControl(e) { // отключаем действия по умолчанию e.preventDefault(); // код нажатой клавиши const code = e.which; // если код клавиши не соответствует коду клавиш вправо или влево, // то прекращаем работу функции if (code != 37 && code != 39) return; // каждому свойству объекта соответствует формула расчёта // индекса, учитывающая кол-во фотографий и текущий индекс let argControl = { 37: (this.count + this.current - 1) % this.count, 39: (this.current + 1) % this.count } this.showPhoto(argControl[e.which]); } |
Листаем галерею вращая колёсико мышки.
Управление выводом фото колёсиком мышки будет срабатывать только в том случае, если было наведение курсора на полноразмерную фотографию. Для этого обработчик события повесим на объект image
.
Если наступило событие wheel
— прокручивание колёсика мышки, вызывается функция wheelControl()
.
По значению свойства deltaY
события wheel
определяем направление вращения колёсика. Далее, вызываем функцию showPhoto
с аргументом, зависящим от направления вращения.
Код функции wheelControl()
и комментарии к нему:
1 2 3 4 5 6 7 8 9 |
wheelControl(e) { // отключаем поведение по умолчанию - скролл страницы e.preventDefault(); let i = (e.deltaY > 0) ? (this.current + 1) % this.count : (this.count + this.current - 1) % this.count; this.showPhoto(i) } |
Листаем галерею кликом по «тумбе» — маленькой фотографии.
Учитывая, что малоразмерных фотографий может быть очень много, снова используем делегирование событий. Повесим обработчик события на родительский элемент — объект thumbsBox
и при срабатывании обработчика вызовем функцию thumbControl()
.
В функции thumbControl()
, в-первую очередь, определяем картинку, по которой был сделан клик. Далее, нам необходимо определить индекс картинки в коллекции thumbs
, чтобы передать этот индекс в функцию showPhoto()
для вывода полноразмерного фото. И вот здесь у нас возникает проблема.
Индекс можно определить с помощью встроенного метода indexOf
, но этот метод есть только у массива, у коллекции он отсутствует. Есть два варианта решения этой проблемы:
1. Преобразовать коллекцию thumbs
в массив и далее спокойно использовать метод indexOf
.
2. Заимствование (одалживание) метода. Мы берём (заимствуем) метод indexOf
из обычного массива [].indexOf
. И используем [].indexOf.call
, чтобы выполнить его в контексте thumbs
.
Посмотрим JS-код первого и второго вариантов:
1 2 3 4 5 6 7 8 |
// вариант 1 // Array.from(this.thumbs) преобразует коллекцию в массив const i = Array.from(this.thumbs).indexOf(target); // вариант 2 //[].indexOf.call - одалживание метода indexOf const i = [].indexOf.call(this.thumbs, target); |
Я не могу сказать, какой вариант более предпочтительный. Хотя мне кажется, что преобразование коллекции в массив при каждом клике по тумбе, более затратный по сравнению с одалживанием метода. Поэтому я выбрал вариант 2.
Хотелось бы в комментариях к статье прочитать ваше мнение. Возможно я ошибаюсь, а возможно, при таком количестве фото, без разницы, какой вариант использовать.
Полный код функции thumbControl()
и комментарии к нему:
1 2 3 4 5 6 7 8 9 10 11 |
thumbControl(e) { // получаем элемент по которому был сделан клик const target = e.target; if (target.tagName != 'IMG') return; // получаем индекс фотографии в коллекции // [].indexOf.call - одалживание метода indexOf const i = [].indexOf.call(this.thumbs, target); this.showPhoto(i); } |
Заключение.
Итак, мы создали фотогалерею с универсальным управлением, которую можно использовать в интернет-магазине для вывода фотографий товаров. Конечно, это упрощённый по функционалу вариант, для того, чтобы вам было легче разобраться с работой галереи. Её функционал можно расширить, например, выделить тумбу текущей (просматриваемой) фотографии или заставить прокручиваться тумбы, при смене полноразмерного фото, можно использовать различную анимацию при появлении новой фотографии и т д. Возможно, в одной из следующих статей мы это всё и рассмотрим.
Комментарии
-
Комментарии должны содержать вопросы и дополнения по статье, ответы на вопросы других пользователей.
Комментарии содержащие обсуждение политики, будут безжалостно удаляться. -
Для удобства чтения Вашего кода, не забываейте его форматировать. Вы его можете подсветить код с помощью тега
<pre>
:
—<pre class="lang:xhtml">
- HTML;
—<pre class="lang:css">
- CSS;
—<pre class="lang:javascript">
- JavaScript. - Если что-то не понятно в статье, постарайтесь указать более конкретно, что именно не понятно.
-
-
Данный скрипт и не предусматривает это, т. к. написан в функциональном стиле. Чтобы на одной странице работало несколько галерей, скрипт необходимо переписать в прототипном стиле: создать конструктор, который будет создавать экземпляры галерей, от которых будут наследоваться методы, отвечающие за работу галереи.
При таком подходе, количество галерей ограничено только пределами разумного.-
Это трудно сделать? Можете мне помочь, для меня это очень важно.
-
Отправил письмо на ваш e-mail.
-
-
-
-
Можете мне тоже помочь? не работает с двумя галереями..
-
Проверьте уникальность id у каждой галереи и их соответствие при вызове конструктора.
-
-
Не как не получается сделать две разные галереи на одной странице.
-
Обновил архив. Оказывается в нём не доставало файлов. Смотрите HTML и JS с расширением prototype.
Приношу извинения, за неполный архив.
-
-
загружаю страницу с компа, ни работает увеличение превью фото при клике на них, в чём может быть дело, все пути прописал верно, все фото отображаются.
-
Так трудно, что-либо посоветовать не видя вашей сборки. Посмотрите в консоли, есть ли какие-то ошибки скрипта js.
-
-
Здравствуйте. Спасибо за галерею, нужная вещь. Подскажите как добавить такой стиль, присвоение класса выбранной тумбе для выделения ее своим стилем?
-
В ф-ии showPhoto() вычисляется текущая тумба this.thumbs[i]. Нужно перебрать с помощью for…of коллекцию тумб this.thumbs и сравнить каждый элемент коллекции с this.thumbs[i]. Если они совпадают, то добавить элементу коллекции класс ‘active’, если нет — убрать.
Совет. Не делайте рамку для выделения непосредственно самой тумбе для избежания дёргания изображений. Используйте для этого псевдоэлемент ‘::before’ или ‘::after’.
-
К сожалению не знаком с JS настолько, чтобы взять и сделать. Поиск примера в инете, тоже ничего не дал. Поэтому просьба к вам, написать чуть подробнее код присвоения класса. И наверное было бы не плохо включить данный функционал в код галереи по умолчанию. В любом случае спасибо.
-
Обнаружил еще один баг. На странице, где размещена галерея в полях формы (input) не вводится текст с клавиатуры. Как только отключаю файл со скриптом — все нормализуется не могли бы помочь мне разобраться с этой проблемой. очень жаль отказываться от данной галереи, очень долго искал рабочий вариант. При необходимости дам ссылку на страницу где наблюдается баг.
-
-
Если установить 2 такие галереи на одну страницу то работает только первая, как это исправить. Чтобы работали все. На второй галереи фото не переключаются.