Flickity Carousel disable custom navigation when reaching last slide

flickity slider codepen
flickity vertical slider
draggable carousel

I'm currently using the Flickity Carousel to create a carousel with different film content panels.

The carousel is using a custom navigation to control it, rather than the standard one that comes with the carousel. However I'm struggling to disable the next navigation button when you reach the end of the carousel slides. Here is an example of what I'm trying to achieve and have based my code on this.

You will see from my example that the Previous button works correctly and is disabled when you first land on the carousel. However the Next button is never disabled when reaching the end.

Here is a JSFiddle

My code:

$(document).ready(function () {
  $('.carousel-container').each(function (i, container) {
        var options = {
            cellAlign:'left',
            groupCells:'3',
            pageDots: false,
            prevNextButtons: false
        };

        $('.carousel__slides').flickity(options);
        var $container = $(container);
        var $slider = $container.find('.carousel__slides');
        var flkty = $slider.data('flickity');
        var selectedIndex = flkty.selectedIndex;
        var slideCount = flkty.slides.length;
        var $prev = $container.find('.prev');
        var $next = $container.find('.next');

        // previous
        $prev.on('click', function () {
            $slider.flickity('previous');
        });

        // next
        $next.on('click', function () {
            $slider.flickity('next');
        });

        $slider.on( 'select.flickity', function() {

            // enable/disable previous/next buttons
            if ( !flkty.cells[ flkty.selectedIndex - 1 ] ) {
              $prev.attr( 'disabled', 'disabled' );
              $next.removeAttr('disabled'); // <-- remove disabled from the next
            } else if ( !flkty.cells[ flkty.selectedIndex +1 ] ) {
              $next.attr( 'disabled', 'disabled' );
              $prev.removeAttr('disabled'); //<-- remove disabled from the prev
            } else {
              $prev.removeAttr('disabled');
              $next.removeAttr('disabled');
            }
        });
    });
});
.carousel-container {
  position:relative;
  }

.carousel__slide {
  width: 20%;
  max-width:286px;
  opacity: 0.5;
    
}

.carousel__slide.is-selected {
  opacity: 1;
}


.carousel__nav {
  display:block;
}

.carousel__nav button {
  width:65px;
  height:50px;
  background:red;
  border-radius:0 100% 100% 0;
  position: absolute;
  top: 80px;
  cursor:pointer;
  border:none;
  outline:0;
  transition-duration: 0.3s;
  transition-property: all;
}

.carousel__nav button:hover,
.carousel__nav button:active,
.carousel__nav button:focus {
  background:green;
  outline:0;
}

.carousel__nav button:disabled {
  background:black;
   opacity: 0.5;
}

.carousel__nav button i {
  content:'';
  display:block;
  margin:0 auto;
  width: 0;
  height: 0;
  border-style: solid;
  border-width: 10.5px 0 10.5px 14px;
  border-color: transparent transparent transparent white;
}

.carousel__nav .prev {
  left:0;
}

.carousel__nav .prev i {
  transform:rotate(180deg);
}

.carousel__nav .next {
  right:0;
  border-radius:100% 0 0 100%;
}


.film-section {
  margin-top:50px;
}

.film-item {
  padding:0 15px;
}

.film-item p {
  font-size:1.4rem;
  line-height:2.6rem;
  margin-bottom:0;
}

.film-item__image {
    position:relative;
}

.film-item__play {
  width:65px;
  height:65px;
  border-radius:100% 0 0 0;
  position:absolute;
  right:0;
  bottom:0;
  background:rgba(0,0,0,0.4);
  border:none;
  transition-duration: 0.3s;
  transition-property: all;
}

.film-item__play:hover,
.film-item__play:active,
.film-item__play:focus {
    background:red;
    outline:0;
}

.film-item__play:after {
    content:'';
    display:block;
    margin:0 auto;
    width: 0;
    height: 0;
    border-style: solid;
    border-width: 10.5px 0 10.5px 14px;
    border-color: transparent transparent transparent white;
    position:absolute;
    top:31px;
    left:33px;
}

.heading-content {
    display:none;
    opacity: 0;
    visibility: hidden;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<script src="https://unpkg.com/flickity@2/dist/flickity.pkgd.min.js"></script>
<link href="https://unpkg.com/flickity@2/dist/flickity.min.css" rel="stylesheet"/>

<div class="carousel-container">
  <div class="carousel__slides">
      <div class="carousel__slide">
        <div class="offset-slide"></div>
      </div>
      <div class="carousel__slide">
        <div class="film-item">
          <div class="film-item__image">
            <img class="w-100" src="http://placekitten.com/510/380" alt="">
          </div>
          <h3>Heading</h3>
          <p>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nunc volutpat augue at quam ultrices euismod vel ac sapien. Ut finibus posuere augue, eget condimentum nunc porttitor in. Praesent in ornare mi, at rhoncus felis. In iaculis viverra sem sit amet lacinia.</p>
        </div>
      </div>
      <div class="carousel__slide">
          <div class="film-item">
            <div class="film-item__image">
              <img class="w-100" src="http://placekitten.com/510/380" alt="">
            </div>
            <h3>Heading</h3>
            <p>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nunc volutpat augue at quam ultrices euismod vel ac sapien. Ut finibus posuere augue, eget condimentum nunc porttitor in. Praesent in ornare mi, at rhoncus felis. In iaculis viverra sem sit amet lacinia.</p>
          </div>
      </div>
      <div class="carousel__slide">
          <div class="film-item">
            <div class="film-item__image">
              <img class="w-100" src="http://placekitten.com/510/380" alt="">
            </div>
            <h3>Heading</h3>
            <p>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nunc volutpat augue at quam ultrices euismod vel ac sapien. Ut finibus posuere augue, eget condimentum nunc porttitor in. Praesent in ornare mi, at rhoncus felis. In iaculis viverra sem sit amet lacinia.</p>
          </div>
      </div>
      <div class="carousel__slide">
          <div class="film-item">
            <div class="film-item__image">
              <img class="w-100" src="http://placekitten.com/510/380" alt="">
            </div>
            <h3>Heading</h3>
            <p>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nunc volutpat augue at quam ultrices euismod vel ac sapien. Ut finibus posuere augue, eget condimentum nunc porttitor in. Praesent in ornare mi, at rhoncus felis. In iaculis viverra sem sit amet lacinia.</p>
          </div>
      </div>
      <div class="carousel__slide">
          <div class="film-item">
            <div class="film-item__image">
              <img class="w-100" src="http://placekitten.com/510/380" alt="">
            </div>
            <h3>Heading</h3>
            <p>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nunc volutpat augue at quam ultrices euismod vel ac sapien. Ut finibus posuere augue, eget condimentum nunc porttitor in. Praesent in ornare mi, at rhoncus felis. In iaculis viverra sem sit amet lacinia.</p>
          </div>
      </div>
      <div class="carousel__slide">
          <div class="film-item">
            <div class="film-item__image">
              <img class="w-100" src="http://placekitten.com/510/380" alt="">
            </div>
            <h3>Heading</h3>
            <p>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nunc volutpat augue at quam ultrices euismod vel ac sapien. Ut finibus posuere augue, eget condimentum nunc porttitor in. Praesent in ornare mi, at rhoncus felis. In iaculis viverra sem sit amet lacinia.</p>
          </div>
      </div>
      <div class="carousel__slide">
          <div class="film-item">
            <div class="film-item__image">
              <img class="w-100" src="http://placekitten.com/510/380" alt="">
              <h3 class="js-video-heading heading-content">Universitat Oberta de Catalunya</h3>
            </div>
            <h3>Heading</h3>
            <p>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nunc volutpat augue at quam ultrices euismod vel ac sapien. Ut finibus posuere augue, eget condimentum nunc porttitor in. Praesent in ornare mi, at rhoncus felis. In iaculis viverra sem sit amet lacinia.</p>
          </div>
      </div>
      <div class="carousel__slide">
          <div class="film-item">
            <div class="film-item__image">
              <img class="w-100" src="http://placekitten.com/510/380" alt="">
            </div>
            <h3>Heading</h3>
            <p>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nunc volutpat augue at quam ultrices euismod vel ac sapien. Ut finibus posuere augue, eget condimentum nunc porttitor in. Praesent in ornare mi, at rhoncus felis. In iaculis viverra sem sit amet lacinia.</p>
          </div>
      </div>
      <div class="carousel__slide">
          <div class="film-item">
            <div class="film-item__image">
              <img class="w-100" src="http://placekitten.com/510/380" alt="">
            </div>
            <h3>Heading</h3>
            <p>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nunc volutpat augue at quam ultrices euismod vel ac sapien. Ut finibus posuere augue, eget condimentum nunc porttitor in. Praesent in ornare mi, at rhoncus felis. In iaculis viverra sem sit amet lacinia.</p>
          </div>
      </div>
      <div class="carousel__slide">
        <div class="offset-slide"></div>
      </div>
  </div>
  <div class="carousel__nav">
    <button class="prev" disabled><i></i></button>
    <button class="next"><i></i></button>
  </div>
</div>

The issue with you code is that you set groupCells options but using flkty.cells. "Cells" represent all element. You need to use "slides" which represent the groups in your slider.

Here is a slightly modified version of your code:

$(document).ready(function() {
  $('.carousel-container').each(function(i, container) {
    var options = {
      cellAlign: 'left',
      groupCells: '3',
      pageDots: false,
      prevNextButtons: false
    };

    $('.carousel__slides').flickity(options);
    var $container = $(container);
    var $slider = $container.find('.carousel__slides');
    var flkty = $slider.data('flickity');
    var selectedIndex = flkty.selectedIndex;
    var slideCount = flkty.slides.length;
    var $prev = $container.find('.prev');
    var $next = $container.find('.next');

    // previous
    $prev.on('click', function() {
      $slider.flickity('previous');
    });

    // next
    $next.on('click', function() {
      $slider.flickity('next');
    });

    $slider.on('select.flickity', function() {
      // enable/disable previous/next buttons
      if (!flkty.slides[flkty.selectedIndex - 1]) {
        $prev.attr('disabled', 'disabled');
        $next.removeAttr('disabled'); // <-- remove disabled from the next
      } else if (!flkty.slides[flkty.selectedIndex + 1]) {
        $next.attr('disabled', 'disabled');
        $prev.removeAttr('disabled'); //<-- remove disabled from the prev
      } else {
        $prev.removeAttr('disabled');
        $next.removeAttr('disabled');
      }
    });
  });
});
.carousel-container {
  position:relative;
  }

.carousel__slide {
  width: 20%;
  max-width:286px;
  opacity: 0.5;
    
}

.carousel__slide.is-selected {
  opacity: 1;
}


.carousel__nav {
  display:block;
}

.carousel__nav button {
  width:65px;
  height:50px;
  background:red;
  border-radius:0 100% 100% 0;
  position: absolute;
  top: 80px;
  cursor:pointer;
  border:none;
  outline:0;
  transition-duration: 0.3s;
  transition-property: all;
}

.carousel__nav button:hover,
.carousel__nav button:active,
.carousel__nav button:focus {
  background:green;
  outline:0;
}

.carousel__nav button:disabled {
  background:black;
   opacity: 0.5;
}

.carousel__nav button i {
  content:'';
  display:block;
  margin:0 auto;
  width: 0;
  height: 0;
  border-style: solid;
  border-width: 10.5px 0 10.5px 14px;
  border-color: transparent transparent transparent white;
}

.carousel__nav .prev {
  left:0;
}

.carousel__nav .prev i {
  transform:rotate(180deg);
}

.carousel__nav .next {
  right:0;
  border-radius:100% 0 0 100%;
}


.film-section {
  margin-top:50px;
}

.film-item {
  padding:0 15px;
}

.film-item p {
  font-size:1.4rem;
  line-height:2.6rem;
  margin-bottom:0;
}

.film-item__image {
    position:relative;
}

.film-item__play {
  width:65px;
  height:65px;
  border-radius:100% 0 0 0;
  position:absolute;
  right:0;
  bottom:0;
  background:rgba(0,0,0,0.4);
  border:none;
  transition-duration: 0.3s;
  transition-property: all;
}

.film-item__play:hover,
.film-item__play:active,
.film-item__play:focus {
    background:red;
    outline:0;
}

.film-item__play:after {
    content:'';
    display:block;
    margin:0 auto;
    width: 0;
    height: 0;
    border-style: solid;
    border-width: 10.5px 0 10.5px 14px;
    border-color: transparent transparent transparent white;
    position:absolute;
    top:31px;
    left:33px;
}

.heading-content {
    display:none;
    opacity: 0;
    visibility: hidden;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<link href="https://unpkg.com/flickity@2.2.1/dist/flickity.min.css" rel="stylesheet"/>
<script src="https://cdnjs.cloudflare.com/ajax/libs/flickity/2.2.1/flickity.pkgd.min.js"></script>
<div class="carousel-container">
  <div class="carousel__slides">
    <div class="carousel__slide">
      <div class="offset-slide"></div>
    </div>
    <div class="carousel__slide">
      <div class="film-item">
        <div class="film-item__image">
          <img class="w-100" src="http://placekitten.com/510/380" alt="">
        </div>
        <h3>Heading</h3>
        <p>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nunc volutpat augue at quam ultrices euismod vel ac sapien. Ut finibus posuere augue, eget condimentum nunc porttitor in. Praesent in ornare mi, at rhoncus felis. In iaculis viverra sem sit amet lacinia.</p>
      </div>
    </div>
    <div class="carousel__slide">
      <div class="film-item">
        <div class="film-item__image">
          <img class="w-100" src="http://placekitten.com/510/380" alt="">
        </div>
        <h3>Heading</h3>
        <p>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nunc volutpat augue at quam ultrices euismod vel ac sapien. Ut finibus posuere augue, eget condimentum nunc porttitor in. Praesent in ornare mi, at rhoncus felis. In iaculis viverra sem sit amet lacinia.</p>
      </div>
    </div>
    <div class="carousel__slide">
      <div class="film-item">
        <div class="film-item__image">
          <img class="w-100" src="http://placekitten.com/510/380" alt="">
        </div>
        <h3>Heading</h3>
        <p>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nunc volutpat augue at quam ultrices euismod vel ac sapien. Ut finibus posuere augue, eget condimentum nunc porttitor in. Praesent in ornare mi, at rhoncus felis. In iaculis viverra sem sit amet lacinia.</p>
      </div>
    </div>
    <div class="carousel__slide">
      <div class="film-item">
        <div class="film-item__image">
          <img class="w-100" src="http://placekitten.com/510/380" alt="">
        </div>
        <h3>Heading</h3>
        <p>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nunc volutpat augue at quam ultrices euismod vel ac sapien. Ut finibus posuere augue, eget condimentum nunc porttitor in. Praesent in ornare mi, at rhoncus felis. In iaculis viverra sem sit amet lacinia.</p>
      </div>
    </div>
    <div class="carousel__slide">
      <div class="film-item">
        <div class="film-item__image">
          <img class="w-100" src="http://placekitten.com/510/380" alt="">
        </div>
        <h3>Heading</h3>
        <p>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nunc volutpat augue at quam ultrices euismod vel ac sapien. Ut finibus posuere augue, eget condimentum nunc porttitor in. Praesent in ornare mi, at rhoncus felis. In iaculis viverra sem sit amet lacinia.</p>
      </div>
    </div>
    <div class="carousel__slide">
      <div class="film-item">
        <div class="film-item__image">
          <img class="w-100" src="http://placekitten.com/510/380" alt="">
        </div>
        <h3>Heading</h3>
        <p>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nunc volutpat augue at quam ultrices euismod vel ac sapien. Ut finibus posuere augue, eget condimentum nunc porttitor in. Praesent in ornare mi, at rhoncus felis. In iaculis viverra sem sit amet lacinia.</p>
      </div>
    </div>
    <div class="carousel__slide">
      <div class="film-item">
        <div class="film-item__image">
          <img class="w-100" src="http://placekitten.com/510/380" alt="">
          <h3 class="js-video-heading heading-content">Universitat Oberta de Catalunya</h3>
        </div>
        <h3>Heading</h3>
        <p>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nunc volutpat augue at quam ultrices euismod vel ac sapien. Ut finibus posuere augue, eget condimentum nunc porttitor in. Praesent in ornare mi, at rhoncus felis. In iaculis viverra sem sit amet lacinia.</p>
      </div>
    </div>
    <div class="carousel__slide">
      <div class="film-item">
        <div class="film-item__image">
          <img class="w-100" src="http://placekitten.com/510/380" alt="">
        </div>
        <h3>Heading</h3>
        <p>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nunc volutpat augue at quam ultrices euismod vel ac sapien. Ut finibus posuere augue, eget condimentum nunc porttitor in. Praesent in ornare mi, at rhoncus felis. In iaculis viverra sem sit amet lacinia.</p>
      </div>
    </div>
    <div class="carousel__slide">
      <div class="film-item">
        <div class="film-item__image">
          <img class="w-100" src="http://placekitten.com/510/380" alt="">
        </div>
        <h3>Heading</h3>
        <p>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nunc volutpat augue at quam ultrices euismod vel ac sapien. Ut finibus posuere augue, eget condimentum nunc porttitor in. Praesent in ornare mi, at rhoncus felis. In iaculis viverra sem sit amet lacinia.</p>
      </div>
    </div>
    <div class="carousel__slide">
      <div class="offset-slide"></div>
    </div>
  </div>
  <div class="carousel__nav">
    <button class="prev" disabled><i></i></button>
    <button class="next"><i></i></button>
  </div>
</div>

Flickity Carousel disable custom navigation when reaching last slide, However I'm struggling to disable the next navigation button when you reach the end of the carousel slides. Here is an example of what I'm  // Disable dragging when only 1 slide // set by default Use one Flickity carousel as navigation for another. Set a custom shape by setting arrowShape to a SVG

try to set "if" as folows:

  if ((flkty.selectedIndex - 1) < 0 ) {              
      $prev.attr( 'disabled', 'disabled' );
      $next.removeAttr('disabled'); // <-- remove disabled from the next
  } else if ( (flkty.selectedIndex + 1) == slideCount){
      $next.attr( 'disabled', 'disabled' );
      $prev.removeAttr('disabled'); //<-- remove disabled from the prev
  } else {
      $prev.removeAttr('disabled');
      $next.removeAttr('disabled');
  }

Disable/Hide navigation arrows if all screens fully visible (contained , This is really the best slider out here :) I'm trying to do something: if all contained in slider container, disable or hide both navigation arrows. But i'm wandering if there's a better way to achieve that. .flickity-prev-next-button. previous { using JS/jQuery since I'm creating my custom next-prev handlers. When a slide has the endBound on his target, then is know it is the last slide that is going to move. slide.target = Math.min( slide.target, endBound ); And set that one as last slide. [Example: When you have 6 slides, and number 4 is the last one that slides. Number 4 is the "fake last slide"] Then u can bound the next button to a end.

Here's how I did it:

var PrevNextButton = Flickity.PrevNextButton;
    PrevNextButton.prototype.update = function () {
        // index of first or last cell, if previous or next
        var cells = this.parent.cells;
        // enable is wrapAround and at least 2 cells
        if (this.parent.options.wrapAround && cells.length > 1) {
            this.enable();
            return;
        }
        var lastIndex = cells.length ? cells.length - 1 : 0;
        var boundIndex = this.isPrevious ? 0 : lastIndex;
        var isEnabling;
        if (this.parent.options.contain) {
            var boundCell = cells[boundIndex];
            var selectedCell = cells[this.parent.selectedIndex];
            isEnabling = selectedCell.target != boundCell.target;
        } else {
            isEnabling = this.parent.selectedIndex == boundIndex
        }
        var method = isEnabling ? 'enable' : 'disable';
        this[method]();
    };

It's with an older version of Flickity, so it may need tests. With it, I'm able to disable/enable next/prev (disable next when the last slide is reached, disable prev if the first slide is on) controls and then handle design with CSS :disabled. This code goes just above my FLickity init el.flickity({...}).

This is from metafizzy/flickity/issues/289 (answered by author Desandro himself)

EDIT:

Ok I tested my sample in your code. I think it failed because of the groupCells, and the Math behind was still using complete cell number as a variable. I dirty edited it with some Math.floor to use the slide number instead of cell number (Desandro sample seems still to work when NOT using grouped cells, but a 1 cell = 1 slide config).

It works with included flickity next/prev feature, but I believe you could adapt it to use your external controls.

This is a quick hack, so it needs tests, better Maths, and flickity option handling. But it's a start.

https://jsfiddle.net/0c7xqrpw/20/

Disable prev/next buttons for contain · Issue #289 · metafizzy/flickity , desandro changed the title Event for when visual boundary is reached? Disable previous / next buttons if there are no other slides #576 var carouselInit = new Flickity( '.carousel-init', { cellSelector: '.carousel-cell', contain: true, cellAlign : 'left', Detect last cell with contain: true and custom nav btns #783. I’m currently using owl carousel but I like the look of flickity. It uses percentages and you can set the width of each slide in your css with media queries as well. I also like that you can preview the previous and next slides if you set the width to less than 100%.

Flickity · Touch, responsive, flickable carousels, Touch, responsive, flickable carousels. Flickity v2.2. fade option; Set initialIndex to a selector string; Better accessibility with aria-hidden on unselected slides  Touch, responsive, flickable carousels. Carousel. Add a carousel :focus style style to aid accessibility. When focused, users can navigate the carousel with their keyboard.

Flickity · Options, Enabled by default when carousel has 2 or more slides draggable: '>1' . freeScroll: true, contain: true, // disable previous & next buttons and dots prevNextButtons: false, pageDots: Selecting a cell in the content carousel will sync to the nav carousel. Set a custom shape by setting arrowShape to a SVG path string. Current slide / total number of slides CSS only; Custom navigation UI. With the Flickity API, you can build custom carousel navigation var flkty = new Flickity

Flickity · Extras, Extra demos; Custom navigation UI; Module loaders; Webpack; Browserify so that carousel can be keyboard navigated on initial page load; Previous & next buttons in top With the Flickity API, you can build custom carousel navigation UI . as individual slides; adaptiveHeight - change carousel height to selected cell   What I am here to talk about is the situation where you do want a carousel and to resist the temptation to reach for a wheelbarrow full of code to do so. I guarantee there are people who’ve picked an entire CMS because they thought they needed it to make a carousel. No shame. We’re all learning.

Comments
  • There seems to be some sort of issue with the < 0 ' Unexpected token <'
  • there need one more "(" at the first "if" i have edit the answer, now its must work