Hello. Does anyone know how can I make to display dots or lines instead of 1/x?
Hello @AndreiGhetu ,
You can try to do this:
- Go to Online Store → Theme → Customize
- In the left-hand panel, click on the “Product pages” section. → product image gallery → Image navigation style
- Choose “Dots” or “Lines” from the drop-down menu to display dots or lines instead of counting numbers.
Hope this can help. Let us know if you need anything else.
Regards,
Ali Reviews team.
Hi. Thank you for your reply, but I can’t find any “Product pages” section.
Hello @AndreiGhetu ,
You can find it like in my screenshot.
Let us know if you need anything else.
Ali Reviews team.
I found it but I still can’t find “Image navigation style”, and I don’t have any drop-down menu to chose between dots and lines.
Hey Andrei! Did you find any solution? Im stuck in the same place…
I’m also stuck in this. Please can somebody help?
I found the answer in this thread: https://community.shopify.com/c/shopify-design/how-can-i-replace-slide-numbers-with-dots-on-my-website/m-p/1893507
I’ll duplicate the answer here:
-
Go to Store Online → theme → edit code
-
Assets/global.js
-
find class class SliderComponent after that replace with code below
class SliderComponent extends HTMLElement {
constructor() {
super();
this.slider = this.querySelector('[id^="Slider-"]');
this.sliderItems = this.querySelectorAll('[id^="Slide-"]');
this.enableSliderLooping = false;
this.currentPageElement = this.querySelector('.slider-counter--current');
this.pageTotalElement = this.querySelector('.slider-counter--total');
this.prevButton = this.querySelector('button[name="previous"]');
this.nextButton = this.querySelector('button[name="next"]');
if (!this.slider || !this.nextButton) return;
this.initPages();
const resizeObserver = new ResizeObserver(entries => this.initPages());
resizeObserver.observe(this.slider);
this.slider.addEventListener('scroll', this.update.bind(this));
this.prevButton.addEventListener('click', this.onButtonClick.bind(this));
this.nextButton.addEventListener('click', this.onButtonClick.bind(this));
this.sliderControlLinksArray = Array.from(this.querySelectorAll('.slider-counter__link'));
this.sliderControlLinksArray.forEach(link => link.addEventListener('click', this.linkToSlide.bind(this)));
}
initPages() {
this.sliderItemsToShow = Array.from(this.sliderItems).filter(element => element.clientWidth > 0);
if (this.sliderItemsToShow.length < 2) return;
this.sliderItemOffset = this.sliderItemsToShow[1].offsetLeft - this.sliderItemsToShow[0].offsetLeft;
this.slidesPerPage = Math.floor((this.slider.clientWidth - this.sliderItemsToShow[0].offsetLeft) / this.sliderItemOffset);
this.totalPages = this.sliderItemsToShow.length - this.slidesPerPage + 1;
this.update();
}
resetPages() {
this.sliderItems = this.querySelectorAll('[id^="Slide-"]');
this.initPages();
}
update() {
const previousPage = this.currentPage;
this.currentPage = Math.round(this.slider.scrollLeft / this.sliderItemOffset) + 1;
if (this.currentPageElement && this.pageTotalElement) {
this.currentPageElement.textContent = this.currentPage;
this.pageTotalElement.textContent = this.totalPages;
}
if (this.currentPage != previousPage) {
this.dispatchEvent(new CustomEvent('slideChanged', { detail: {
currentPage: this.currentPage,
currentElement: this.sliderItemsToShow[this.currentPage - 1]
}}));
}
if (this.enableSliderLooping) return;
if (this.isSlideVisible(this.sliderItemsToShow[0]) && this.slider.scrollLeft === 0) {
this.prevButton.setAttribute('disabled', 'disabled');
} else {
this.prevButton.removeAttribute('disabled');
}
if (this.isSlideVisible(this.sliderItemsToShow[this.sliderItemsToShow.length - 1])) {
this.nextButton.setAttribute('disabled', 'disabled');
} else {
this.nextButton.removeAttribute('disabled');
}
this.sliderControlButtons = this.querySelectorAll('.slider-counter__link');
if (!this.sliderControlButtons.length) return;
this.sliderControlButtons.forEach(link => {
link.classList.remove('slider-counter__link--active');
link.removeAttribute('aria-current');
});
this.sliderControlButtons[this.currentPage - 1].classList.add('slider-counter__link--active');
this.sliderControlButtons[this.currentPage - 1].setAttribute('aria-current', true);
}
isSlideVisible(element, offset = 0) {
const lastVisibleSlide = this.slider.clientWidth + this.slider.scrollLeft - offset;
return (element.offsetLeft + element.clientWidth) <= lastVisibleSlide && element.offsetLeft >= this.slider.scrollLeft;
}
onButtonClick(event) {
event.preventDefault();
const step = event.currentTarget.dataset.step || 1;
this.slideScrollPosition = event.currentTarget.name === 'next' ? this.slider.scrollLeft + (step * this.sliderItemOffset) : this.slider.scrollLeft - (step * this.sliderItemOffset);
this.slider.scrollTo({
left: this.slideScrollPosition
});
}
linkToSlide(event) {
event.preventDefault();
const slideScrollPosition = this.slider.scrollLeft + this.sliderItemOffset * (this.sliderControlLinksArray.indexOf(event.currentTarget) + 1 - this.currentPage);
this.slider.scrollTo({
left: slideScrollPosition
});
}
}
-
Sections/main-product.liquid
-
find code below
1
/
{{ 'general.slider.of' | t }}
{{ media_count }}
- Replace code of step 5 with code below
{%- assign featured_media = product.selected_or_first_available_variant.featured_media -%}
{% if section.settings.hide_variants and variant_images contains featured_media.src %}
{% endif %}
{%- for media in product.media -%}
{%- unless media.id == product.selected_or_first_available_variant.featured_media.id -%}
{% if section.settings.hide_variants and variant_images contains media.src %}
{% else %}
{%- endif -%}
{%- endunless -%}
{%- endfor -%}
Add This code in product-media-gallery.liquid on on main-product.liquid
{% if section.settings.enable_dots %}
<style>
.slider-dots {
display: flex;
gap: 6px;
justify-content: center;
margin-top: 8px;
}
.slider-dots .dot {
width: 12px;
height: 12px;
background: #ccc;
border-radius: 50%;
cursor: pointer;
}
.slider-dots .dot.active {
background: #000;
</style>
<script>
document.addEventListener('DOMContentLoaded', function () {
document.querySelectorAll('slider-component').forEach(function (slider) {
const dots = slider.querySelectorAll('.slider-dots .dot');
const slideTrack = slider.querySelector('.slider'); // UL with slides
const slides = slider.querySelectorAll('.slider__slide');
const currentCounter = slider.querySelector('.slider-counter--current');
function setActiveDot(index) {
dots.forEach((dot, i) => {
dot.classList.toggle('active', i === index);
});
if (currentCounter) currentCounter.textContent = index + 1;
}
function goToSlide(index) {
const targetSlide = slides[index];
if (!targetSlide) return;
slideTrack.scrollTo({
left: targetSlide.offsetLeft,
behavior: 'smooth'
});
}
// Dot clicks
dots.forEach((dot, i) => {
dot.addEventListener('click', () => {
goToSlide(i);
setActiveDot(i);
});
});
// Button clicks
const nextBtn = slider.querySelector('.slider-button--next');
const prevBtn = slider.querySelector('.slider-button--prev');
let activeIndex = 0;
function updateFromButtons(dir) {
activeIndex = (activeIndex + dir + slides.length) % slides.length;
goToSlide(activeIndex);
setActiveDot(activeIndex);
}
if (nextBtn) nextBtn.addEventListener('click', () => updateFromButtons(1));
if (prevBtn) prevBtn.addEventListener('click', () => updateFromButtons(-1));
// Swipe / grab scroll listener
let scrollTimeout;
slideTrack.addEventListener('scroll', function () {
clearTimeout(scrollTimeout);
scrollTimeout = setTimeout(() => {
let closestIndex = 0;
let closestDistance = Infinity;
slides.forEach((slide, i) => {
const distance = Math.abs(slide.offsetLeft - slideTrack.scrollLeft);
if (distance < closestDistance) {
closestDistance = distance;
closestIndex = i;
}
});
activeIndex = closestIndex;
setActiveDot(activeIndex);
}, 100); // debounce for smoother update
});
});
});
</script>
{% endif %}
Replace code in product-media-gallery
Replace this
<div class="slider-counter caption">
<span class="slider-counter--current">1</span>
<span aria-hidden="true"> / </span>
<span class="visually-hidden">{{ 'general.slider.of' | t }}</span>
<span class="slider-counter--total">{{ media_count }}</span>
</div>
with
{% if section.settings.enable_dots %}<div class="slider-dots" id="SliderDots-{{ section.id }}">
{% for i in (1..media_count) %}
<span class="dot{% if forloop.first %} active{% endif %}" data-slide="{{ forloop.index0 }}"></span>
{% endfor %}
</div>
{% else %}
<div class="slider-counter caption">
<span class="slider-counter--current">1</span>
<span aria-hidden="true"> / </span>
<span class="visually-hidden">{{ 'general.slider.of' | t }}</span>
<span class="slider-counter--total">{{ media_count }}</span>
</div>
{% endif %}
Now add checkbox in section settings:
{
"type": "checkbox",
"id": "enable_dots",
"default": false,
"label": "Enable dots",
"info": "Else it will show numbers and Arrows"
},
Hello @AndreiGhetu
Okay, sure. Please send me the collaborator code, and I will check and update you with the proper solution.