Dawn theme Display dots instead of numbers

Hello. Does anyone know how can I make to display dots or lines instead of 1/x?

1 Like

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.

1 Like

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:

  1. Go to Store Online → theme → edit code

  2. Assets/global.js

  3. 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
    });
  }
}
  1. Sections/main-product.liquid

  2. find code below


  1
   / 
  {{ 'general.slider.of' | t }}
  {{ media_count }}

  1. 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.