Looping carousel doesn’t display overhanging ‘next’ image until navigation is clicked

I have a carousel that continuously loops, the next/previous slides move into view when clicking the relevant navigation link. For this I am using Keen Slider.

The edges of the next/previous image can be seen as overhanging edges on the left/right edges of the viewport.

The problem I have is, initially this works well but when you reach ‘Slide 8’, which is the end of the original group of slides, there is a gap on the right edge when the ‘next’ slide should be.

This won’t appear until you click the “Next” navigation link, when it jumps into view. It never appears to be an issue with the left edge (previous) slides, only the slides coming into view from the right edge, after you initially reach ‘Slide 8’.

As an aside, if you set the number of visible slides preView to 5 (example below) you don’t see the edge of the “Next” slide on load, just after clicking the navigation – but 4 seems to work ok before the loop and reaching Slide 8 – not sure if it’s relevant but thought I’d document.

'(min-width: 1024px)': {
    slides: {
        perView: 4,
        spacing: 8
    }
}

This wouldn’t be an issue if I had overflow: hidden on the carousel but as I want to see the edges of the next/previous slide this isn’t an option.

Commented Out JS

You might see some commented out javascript. I was thinking it could be cool to have the carousel autoplay, then drop after either the next/previous links are clicked – but that’s very much next steps and I’m not sure on that yet. Mentioning just incase people wondered why it’s there.

/* Lazy Load Effect */

const pixelImage = document.querySelectorAll(".pixel-load")

pixelImage.forEach(div => {
  const img = div.querySelector("img")

  function loaded() {
    div.classList.add("loaded")
  }

  if (img.complete) {
    loaded()
  } else {
    img.addEventListener("load", loaded)
  }
})


/* Keen Slider */

// var animation = { duration: 40000, easing: (t) => t }
new KeenSlider("#gallery-slider", {
  loop: true,
  mode: "free-snap",
  slides: {
    perView: 1.5,
    renderMode: "precision",
    spacing: 8
  },
  breakpoints: {
    '(min-width: 768px)': {
      slides: {
        perView: 3,
        spacing: 8
      }
    },
    '(min-width: 1024px)': {
      slides: {
        perView: 4,
        spacing: 8
      }
    }
  },
  created: function(instance) {
    document.getElementById("gallery-slider").classList.add("loaded");
    //s.moveToIdx(4, true, animation);
    document
      .getElementById("arrow-left")
      .addEventListener("click", function() {
        instance.prev();
      });
    document
      .getElementById("arrow-right")
      .addEventListener("click", function() {
        instance.next();
      });
  }
  // updated(s) {
  //    s.moveToIdx(s.track.details.abs + 4, true, animation);
  // },
  // animationEnded(s) {
  //    s.moveToIdx(s.track.details.abs + 4, true, animation);
  // }
});
/* ==========================================================================
   #BASE
   ========================================================================== */

html {
  font-size: 62.5%;
  margin: 0;
  padding: 0;
}

body {
  font-size: 12px;
  font-family: "Arial", sans-serif;
  margin: 0;
  padding: 64px 0 0;
  text-transform: uppercase;
}

h2 {
  font-size: 12px;
  font-weight: 400;
  margin: 0 16px 16px;
  padding: 0;
}

figure {
  margin: 0;
  padding: 0;
}

img {
  height: auto;
  width: 100%;
  max-width: 100%;
}


/* ==========================================================================
   #KEEN SLIDER
   ========================================================================== */

.keen-slider {
  display: flex;
  align-content: flex-start;
  overflow: hidden;
  position: relative;
  touch-action: pan-y;
  user-select: none;
  width: 100%;
  -webkit-tap-highlight-color: transparent;
}

.keen-slider .keen-slider__slide {
  min-height: 100%;
  overflow: hidden;
  position: relative;
  width: 100%;
}


/* ==========================================================================
   #GALLERY
   ========================================================================== */


/**
 * My overrides for the Keen Slider gallery.
 *
 * 1. Remove `overflow: hidden` from the slider and add it to the parent. This
 *    allows the slider to align with the grid but also bleed off the edges of
 *    the page.
 * 2. Align container with the global grid.
 */

.gallery {
  margin-bottom: 64px;
  overflow: hidden;
  /* [1] */
  padding: 0 32px;
  /* [2] */
}

.gallery .keen-slider {
  overflow: visible;
  /* [1] */
}


/**
 * As the widths for each slide are set in Javascript. We add widths to slides
 * before `.keen-slider` has loaded to keep the layout consistent and help with
 * the Cumulative Layout Shift (CLS) and performance.
 */

.keen-slider:not(.loaded) .keen-slider__slide {
  width: calc((100vw / 1.5) - 24px);
}

@media(min-width: 48em) {
  .keen-slider:not(.loaded) .keen-slider__slide {
    width: calc((100vw - 48px) / 3);
  }
}

@media(min-width: 64rem) {
  .keen-slider:not(.loaded) .keen-slider__slide {
    width: calc((100vw - 56px) / 4);
  }
}

/**
 * Navigation for the gallery (prev/next).
 */

.gallery__nav {
  display: flex;
  justify-content: space-between;
  margin: 0 0 16px;
  width: 100%;
}

.gallery__nav .arrow {
  cursor: pointer;
  user-select: none;
}


/* ==========================================================================
   #PIXEL LOAD
   ========================================================================== */


/**
 * Add a pixelated effect to images while the load.
 */

.pixel-load {
  overflow: hidden;
  position: relative;
}

.pixel-load__preload img {
  image-rendering: pixelated;
  position: absolute;
  inset: 0;
  opacity: 1;
  pointer-events: none;
}

.loaded .pixel-load__preload img {
  animation: loaded .32s .16s steps(1, end) both;
}

@keyframes loaded {
  0% {
    scale: 2;
  }

  33% {
    scale: 1.5;
  }

  66% {
    scale: 1;
  }

  100% {
    opacity: 0;
    z-index: 1;
  }
}
<script src="https://cdn.jsdelivr.net/npm/[email protected]/keen-slider.min.js"></script>
<!-- Keen Slider -->
<div class="gallery">
  <div class="gallery__nav grid">
    <div class="grid__col">
      <a id="arrow-left" class="arrow arrow--left shuffle">Previous</a>
    </div>
    <div class="grid__col">
      <a id="arrow-right" class="arrow arrow--right shuffle">Next</a>
    </div>
  </div>
  <div id="gallery-slider" class="keen-slider">
    <div class="keen-slider__slide">
      <figure data-label="Hover Label 1" class="has-label">
        <div class="pixel-load">
          <div class="pixel-load__preload">
            <img src="https://placebeard.it/18/24" width="18" height="24" loading="eager">
          </div>
          <img src="https://placebeard.it/768/1024" width="768" height="1024" loading="lazy" />
        </div>
        <figcaption>Slide 1</figcaption>
      </figure>
    </div>
    <div class="keen-slider__slide">
      <figure data-label="Hover Label 2" class="has-label">
        <div class="pixel-load">
          <div class="pixel-load__preload">
            <img src="https://placebeard.it/18/24" width="18" height="24" loading="eager">
          </div>
          <img src="https://placebeard.it/768/1024" width="768" height="1024" loading="lazy" />
        </div>
        <figcaption>Slide 2</figcaption>
      </figure>
    </div>
    <div class="keen-slider__slide">
      <figure data-label="Hover Label 3" class="has-label">
        <div class="pixel-load">
          <div class="pixel-load__preload">
            <img src="https://placebeard.it/18/24" width="18" height="24" loading="eager">
          </div>
          <img src="https://placebeard.it/768/1024" width="768" height="1024" loading="lazy" />
        </div>
        <figcaption>Slide 3</figcaption>
      </figure>
    </div>
    <div class="keen-slider__slide">
      <figure data-label="Hover Label 4" class="has-label">
        <div class="pixel-load">
          <div class="pixel-load__preload">
            <img src="https://placebeard.it/18/24" width="18" height="24" loading="eager">
          </div>
          <img src="https://placebeard.it/768/1024" width="768" height="1024" loading="lazy" />
        </div>
        <figcaption>Slide 4</figcaption>
      </figure>
    </div>
    <div class="keen-slider__slide">
      <figure data-label="Hover Label 5" class="has-label">
        <div class="pixel-load">
          <div class="pixel-load__preload">
            <img src="https://placebeard.it/18/24" width="18" height="24" loading="eager">
          </div>
          <img src="https://placebeard.it/768/1024" width="768" height="1024" loading="lazy" />
        </div>
        <figcaption>Slide 5</figcaption>
      </figure>
    </div>
    <div class="keen-slider__slide">
      <figure data-label="Hover Label 6" class="has-label">
        <div class="pixel-load">
          <div class="pixel-load__preload">
            <img src="https://placebeard.it/18/24" width="18" height="24" loading="eager">
          </div>
          <img src="https://placebeard.it/768/1024" width="768" height="1024" loading="lazy" />
        </div>
        <figcaption>Slide 6</figcaption>
      </figure>
    </div>
    <div class="keen-slider__slide">
      <figure data-label="Hover Label 7" class="has-label">
        <div class="pixel-load">
          <div class="pixel-load__preload">
            <img src="https://placebeard.it/18/24" width="18" height="24" loading="eager">
          </div>
          <img src="https://placebeard.it/768/1024" width="768" height="1024" loading="lazy" />
        </div>
        <figcaption>Slide 7</figcaption>
      </figure>
    </div>
    <div class="keen-slider__slide">
      <figure data-label="Hover Label 8" class="has-label">
        <div class="pixel-load">
          <div class="pixel-load__preload">
            <img src="https://placebeard.it/18/24" width="18" height="24" loading="eager">
          </div>
          <img src="https://placebeard.it/768/1024" width="768" height="1024" loading="lazy" />
        </div>
        <figcaption>Slide 8</figcaption>
      </figure>
    </div>
  </div>
</div>
<!-- End Keen Slider -->