Слайдер-превью изображений при наведении мыши
На сайтах интернет-магазинов (или тому подобных), где на страницах рубрик представлены карточки товаров или услуг, можно встретить мини-слайдеры, в которых показаны превью этих товаров и услуг. При наведении курсора и перемещении его влево-вправо можно посмотреть несколько таких изображений. Чтобы было более понятно, о чем идет речь, зайдите на сайты ozon.ru и realty.yandex.ru (актуально на момент написания данной статьи).
Хочу поделиться моей реализацией функционала этого слайдера в двух вариантах: на jQuery и в нативном JavaScript.
Из особенностей: на десктопе изображения меняются при перемещении курсора мыши над ним горизонтально, а на мобильных устройствах – если проводить пальцем по изображению.
Исходная структура HTML-кода
<div class="images">
<img src="image">
<img src="image">
<img src="image">
</div>
После выполнения скрипта она обретает следующий вид:
<div class="hvr">
<div class="hvr__images">
<div class="images">
<img class="image">
<img class="image">
<img class="image">
</div>
<div class="hvr__sectors">
<div class="hvr__sector"></div>
<div class="hvr__sector"></div>
<div class="hvr__sector"></div>
</div>
</div>
<div class="hvr__dots">
<div class="hvr__dot hvr__dot--active"></div>
<div class="hvr__dot"></div>
<div class="hvr__dot"></div>
</div>
</div>
CSS-код
Есть обязательные стили:
.hvr__images {
position: relative;
}
.hvr__sectors {
position: absolute;
top: 0;
left: 0;
right: 0;
bottom: 0;
display: flex;
}
.hvr__sector {
flex-grow: 1;
}
Также можно добавить допонительные стили:
- для изображений, чтобы до срабатывания скрипта отображалось только первое изображение;
- для точек, показывающих общее количество изображений и активный слайд.
.images {
display: flex;
overflow: hidden;
}
.image {
display: block;
}
.hvr__dots {
display: flex;
align-items: center;
justify-content: center;
}
.hvr__dot {
width: 5px;
height: 5px;
margin: 10px 2px 0;
border-radius: 50%;
background: #d6dbe0;
}
.hvr__dot--active {
background: #000;
}
Решение на jQuery
(function ($) {
$.fn.HvrSlider = function () {
return this.each(function () {
var el = $(this);
if (el.find('img').length > 1) {
var hvr = $('<div>', {
class: 'hvr',
append: [
$('<div>', {
class: 'hvr__images',
append: $('<div>', {
class: 'hvr__sectors',
}),
}),
$('<div>', {
class: 'hvr__dots',
}),
],
insertAfter: el,
prepend: el,
});
var hvrImages = $('.hvr__images', hvr);
var hvrImage = $('img', hvr);
var hvrSectors = $('.hvr__sectors', hvr);
var hvrDots = $('.hvr__dots', hvr);
el.prependTo(hvrImages);
hvrImage.each(function () {
hvrSectors.prepend('<div class="hvr__sector"></div>');
hvrDots.append('<div class="hvr__dot"></div>');
});
$('.hvr__dot:first', hvrDots).addClass('hvr__dot--active');
var setActiveEl = function (el) {
hvrImage.hide().eq(el.index()).show();
$('.hvr__dot', hvrDots).removeClass('hvr__dot--active').eq(el.index()).addClass('hvr__dot--active');
};
$('.hvr__sector', hvrSectors).hover(function () {
setActiveEl($(this));
});
hvrSectors.on('touchmove', function (e) {
var position = e.originalEvent.changedTouches[0];
var target = document.elementFromPoint(position.clientX, position.clientY);
if ($(target).is('.hvr__sector')) {
setActiveEl($(target));
}
});
}
});
};
})(jQuery);
Код для инициализации скрипта:
$('.images').HvrSlider();
Результат выглядит следующим образом:
See the Pen
Слайдер-превью изображений на jQuery by Dimox (@dimox)
on CodePen.
Решение на JavaScript
class HvrSlider {
constructor(selector) {
const elements = document.querySelectorAll(selector);
elements.forEach((el) => {
if (el.querySelectorAll('img').length > 1) {
const hvr = document.createElement('div');
hvr.classList.add('hvr');
const hvrImages = document.createElement('div');
hvrImages.classList.add('hvr__images');
hvr.appendChild(hvrImages);
const hvrSectors = document.createElement('div');
hvrSectors.classList.add('hvr__sectors');
hvrImages.appendChild(hvrSectors);
const hvrDots = document.createElement('div');
hvrDots.classList.add('hvr__dots');
hvr.appendChild(hvrDots);
el.parentNode.insertBefore(hvr, el);
hvrImages.prepend(el);
const hvrImagesArray = hvr.querySelectorAll('img');
hvrImagesArray.forEach(() => {
hvrSectors.insertAdjacentHTML('afterbegin', '<div class="hvr__sector"></div>');
hvrDots.insertAdjacentHTML('afterbegin', '<div class="hvr__dot"></div>');
});
hvrDots.firstChild.classList.add('hvr__dot--active');
const setActiveEl = function (targetEl) {
const index = [...hvrSectors.children].indexOf(targetEl);
hvrImagesArray.forEach((img, idx) => {
if (index == idx) {
img.style.display = 'block';
} else {
img.style.display = 'none';
}
});
hvr.querySelectorAll('.hvr__dot').forEach((dot, idx) => {
if (index == idx) {
dot.classList.add('hvr__dot--active');
} else {
dot.classList.remove('hvr__dot--active');
}
});
};
hvrSectors.addEventListener('mouseover', function (e) {
if (e.target.matches('.hvr__sector')) {
setActiveEl(e.target);
}
});
hvrSectors.addEventListener('touchmove', function (e) {
const position = e.changedTouches[0];
const target = document.elementFromPoint(position.clientX, position.clientY);
if (target.matches('.hvr__sector')) {
setActiveEl(target);
}
});
}
});
}
}
Код для инициализации скрипта:
new HvrSlider('.images');
Результат точно такой же, как и в примере выше:
See the Pen
Слайдер-превью изображений на JavaScript by Dimox (@dimox)
on CodePen.
Комментарии (18)
Добрый день. В данном слайдере есть один недостаток. При просмотре с телефона страницу можно скролить вверх и вниз только касаясь мимо превью которые используют данный слайдер. На самом превью можно только листать картинки влево, вправо. В общем создаются неудобства при прокрутке страницы вверх, вниз когда пытаешься прокручивать хватаясь за превьюшку. Можно как то поправить данную проблему, что бы скролить страницу вверх, вниз можно было как и раньше касаясь любого места экрана?
Да, есть такая проблема. Надо будет заняться поиском решения.
Обнаружил еще одну проблему. В Firefox все картинки показываются в одну строку. В слайдер они превращаются только после того как по ним поводить курсором мышки. После этого все работает как слайдер. После перезагрузки страницы картинки снова в строку.
Странно, у меня в Firefox всё работает, как положено.
Проблема решена. Достаточно было удалить строку:
Спасибо. Работает. В Firefox ваш оригинальный вариант и правда работает. Я в CSS вносил некоторые изменения что бы адаптировать к шаблону сайта, вот бяка и вылезла. В хром, опере, едж, везде нормально, а в Firefox бяка.
изменения вот такие
.image {
display: block; max-width: 450px; width: 100%; height: auto;
Это для того что бы блок слайдера был адаптивным. Как побороть это в Firefox не знаю.
Нужно добавить такие стили:
То есть по умолчанию показывать только первое изображение.
Спасибо. Сработало после небольшой доработки, так как на странице есть и другие картинки и в итоге они пропадали а в слайдере отображались. Вот это вариант работает в моем случае корректно:
.images img {
display: none;
}
.images img:first-child {
display: block;
}
Переключение слайдов тапом работает очень коряво.
В идеале должно работать так: https://codepen.io/i-did/pen/yLYRvVL
А как на JS сделать плавность сметы этих изображений?
Спасибо за код. Подскажите, как сделать, что бы автоматически показывался первый слайд, если убрать мышь?
Добрый день. А как такой превью сделать для категории товаров в woocomerce
Установил на сайт и возникла следующая проблема.
При загрузке страницы отображаются все фото товара, а уже после того как навожу курсор, фото скрываются и все отображается как и должно.
Как это исправить?
Скройте стилями все изображения, кроме первого. В статье есть пример.