Slide counter for carousel?

We would like to add a counter to our Builder carousel component, to show the user they are on Slide X of Y. Is this possible? Could it be done with some custom javascript?

@Canuckaholic definitely, this shouldn’t be too complicated, and there are many ways you could do it. You could create your own custom component that includes this logic, or if you wanted to still use the Builder component you could just add some custom JS in the Custom JS + CSS window:

Hopefully that gives you a few directions you could go…Take a look and try it out and let us know if you have any follow up questions!

Thanks for the quick reply! We are on the same page in that I am looking to add some custom JS in the Custom JS + CSS window, however the trick is going to be determining how many slides have been added to the carousel in order to show the user they are on slide 5 / 7, for example. So my custom JS code is going to need some information from the carousel in order to display the right numbers.

One idea I have is to use the Content API, fetch my symbols (my carousel is in a symbol), and then count the number of slides in the JSON result for the carousel object.

Is there a way to use the Content API (ie. do a fetch) directly from inside Builder?

@Canuckaholic that would work. You could hit the Content API in a fetch() within the JS+CSS window

    await fetch(someUrl)
      .then(res => res.json())
      .then(data => {
        // set the data on the state object so you can access it from your components
        state.someData = data;
     });

or add it as an API call in the +API Data section, but I think the most straight forward way could be to just add a class name to each carousel slide in the CSS Class section of the Style tab

Screen Shot 2021-12-09 at 2.57.23 PM

and then you can grab the items using document.getElementsByClassName(className).length and use that to figure out the length and where you are within the carousel, etc.

Let me know if that works for you!

I have the following code almost working. What I did is add my Carousel symbol as a data source for itself using the “Connect data from popular sources” section. It works if I edit the JS, close the edit window, and see the UI respond. However, I’m noticing that state.symbol is undefined on page load, so it doesn’t actually work.

    var blocks = state.symbol?.data?.blocks
    if (blocks?.length) {
      var carousel = blocks.find(block => {
        return block.component?.name === 'Builder:Carousel'
      })

      var currentSlide = 1
      var totalSlides = carousel.component.options.slides.length
      var counterEl = document.getElementById('counter')
      counterEl.textContent = currentSlide + " / " + totalSlides

      function incrementCount() {
        currentSlide++
        if (currentSlide > totalSlides) {
          currentSlide = 1
        }
        counterEl.textContent = currentSlide + " / " + totalSlides
      }

      function decrementCount() {
        currentSlide--
        if (currentSlide < 1) {
          currentSlide = totalSlides
        }
        counterEl.textContent = currentSlide + " / " + totalSlides
      }

      var nextBtn = document.getElementById('next')
      nextBtn.addEventListener('click', incrementCount)

      var prevBtn = document.getElementById('prev')
      prevBtn.addEventListener('click', decrementCount)
    }
  }
1 Like

I have the following in my JS, but on page load it’s not finding the elements in the DOM:

if (Builder.isBrowser) {

    function getSlidesByClassName() {
      return document.getElementsByClassName('slide').length
    }

    var totalSlides = getSlidesByClassName()
}

totalSlides is undefined when the page first loads. Could something be off with the part that waits for the DOM to be loaded before running the JS?

Well for anyone wanting to do this who stumbles upon this thread, here’s my current code that seems to work. It’s not the best implementation though, I would say, so perhaps Builder could add this feature for the carousel to their roadmap?

    await fetch('https://cdn.builder.io/api/v2/content/symbol?apiKey=<API_KEY>&query.id=<COMPONENT_ID>&limit=1')
      .then(res => res.json())
      .then(data => {
        // set the data on the state object so you can access it from your components
        state.carousel = data.results
    })

   // In case the fetch doesn't work, we can try to get the total from the class names
    function getSlidesByClassName() {
      return document.getElementsByClassName('slide').length
    }

    var totalSlides = getSlidesByClassName()
    var currentSlide = 1
    var wait = false

    var blocks = state.carousel?.[0].data?.blocks || state.symbol?.data?.blocks
    if (blocks?.length) {
      var carousel = blocks.find(block => {
        return block.component?.name === 'Builder:Carousel'
      })
      if (carousel.component.options.slides.length) {
        totalSlides = carousel.component.options.slides.length
      }
    }
    
    var counterEl = document.getElementById('counter')
    counterEl.textContent = currentSlide + " / " + totalSlides

    function startTimer() {
      wait = true
      setTimeout(() => wait = false, 700)
    }

    function incrementCount() {
      if (!wait) {
        startTimer()
        currentSlide++
        if (currentSlide > totalSlides) {
          currentSlide = 1
        }
        counterEl.textContent = currentSlide + " / " + totalSlides
      }
    }

    function decrementCount() {
      if (!wait) {
        startTimer()
        currentSlide--
        if (currentSlide < 1) {
          currentSlide = totalSlides
        }
        counterEl.textContent = currentSlide + " / " + totalSlides
      }
    }

    var nextBtn = document.getElementById('next')
    nextBtn.addEventListener('click', incrementCount)

    var prevBtn = document.getElementById('prev')
    prevBtn.addEventListener('click', decrementCount)
1 Like