Build a Sliding Client Testimonials Carousel With jQuery

Many portfolio websites include a list of previous clients to build trust from other potential customers. Reading what other people have said about a service or product is one way to garner support from visitors who have never heard about your company before. (Of course, this design technique only works if you have previous clients to draw from!)

In this tutorial I want to demonstrate two distinct styles of rotating carousels. The first is a testimonial box which includes a small quote talking about the services. Then I’ve constructed a long horizontal carousel that rotates through a list of company logos. These would normally be companies you have done business with – but may also include corporate partners or resources.

Live DemoDownload Source Code

Building the Webpage

My first step is to include a local copy of jQuery in the document header. I’ve also created 2 separate resources – styles.css for the page stylesheet and carousels.js which holds the jQuery code.

<!doctype html>
<html lang="en-US">
<head>
  <meta charset="utf-8">
  <meta http-equiv="Content-Type" content="text/html">
  <title>Clients and Testimonials Carousel - Design Shack Demo</title>
  <meta name="author" content="Jake Rocheleau">
  <link rel="shortcut icon" href="https://designshack.net/favicon.ico">
  <link rel="icon" href="https://designshack.net/favicon.ico">
  <link rel="stylesheet" type="text/css" media="all" href="css/styles.css">
  <script type="text/javascript" src="js/jquery-1.10.2.min.js"></script>
  <script type="text/javascript" src="js/carousels.js"></script>
</head>

I’d like to break down the two carousels into sections making them easier to read. First is the clients testimonial box which is fitted to show a single testimonial at a time. This does not auto-rotate, but instead uses directional arrows for navigation.

<div class="carousel-nav clearfix">
  <!-- arrows http://findicons.com/icon/235460/forward?id=388672 -->
  <img src="images/prev.png" id="prv-testimonial" class="prevbtn">
  <img src="images/next.png" id="nxt-testimonial" class="nextbtn">
</div>
<div class="carousel-wrap">
  <ul id="testimonial-list" class="clearfix">
    <li>
      <p class="context">"Their services are beyond anything I could have imagined. It gets my highest recommendation."</p>
      <p class="credits">Johnny Appleseed, <a href="http://www.apple.com/">Apple, Inc.</a></p>
    </li>
    <li>
      <p class="context">"Simple. Efficient. Quick production and easy to work with."</p>
      <p class="credits">Smithy Smith, <a href="http://www.roughdraftstudios.com/">Rough Draft Studios, Inc.</a></p>
    </li>
    <li>
      <p class="context">"The marketing strategy is superb. These guys came through above and beyond expectations."</p>
      <p class="credits">Mr. Generic, <a href="http://www.google.com/">Google</a></p>
    </li>
    <li>
      <p class="context">"We needed a reliable team to help with some finishing touches. These guys are reliable, fair, and trustworthy... A+ quality."</p>
      <p class="credits">Seth MacFarlane, <a href="http://en.wikipedia.org/wiki/Fuzzy_Door_Productions">Fuzzy Door Productions</a></p>
    </li>
  </ul><!-- @end #testimonial-list -->
</div><!-- @end .carousel-wrap -->

The arrows are just plain image files which get triggered on each click event. The unordered list with an ID #testimonial-list contains all the internal testimonials. Each list item has a quote from the client, along with a snippet of accreditation to the client’s name and their company. The container will be set to a fixed width of 400px as to only display one testimonial at a time.

<div class="clients-wrap">
  <ul id="clients-list" class="clearfix">
    <li><img src="images/client01-cartoon-network.png" alt="Cartoon Network"></li>
    <li><img src="images/client02-rough-draft-studios.png" alt="Rough Draft Studios"></li>
    <li><img src="images/client03-spongebob-movie2.png" alt="SpongeBob Movie #2"></li>
    <li><img src="images/client04-apple-inc.png" alt="Apple Computers"></li>
    <li><img src="images/client05-google-talk.png" alt="Google chat talk"></li>
    <li><img src="images/client06-g4tv.png" alt="G4TV channel"></li>
    <li><img src="images/client07-wonka-candy.png" alt="Wonka Chocolates and Candy"></li>
  </ul>
</div><!-- @end .clients-wrap -->

Looking over the clients carousel you will notice it is somewhat lighter in HTML. The logos don’t link anywhere and there isn’t any text to go along, either. I’m using a collection of logos to display corporations or projects which have been clients in the past. This carousel does auto-rotate except when the user is hovering in the container. Each logo is slightly dimmed using CSS3 opacity, and when hovering they animate to 100%. It’s a neat little effect so the logos don’t appear too strong or distracting.

Carousel CSS Styles

All of the HTML structure is fairly simple to understand. The CSS is even more important because this is how everything gets fitted to run like a carousel. I want to start with the testimonials because there is a bit more going on with that block of code.

/** testimonials **/
#testimonials {
  display: block;
  width: 100%;
  margin-bottom: 40px;
}
#testimonials h2 {
  display: block;
  text-align: center;
  font-weight: bold;
  margin-bottom: 3px;
}

#testimonials .carousel-wrap {
  display: block;
  width: 400px;
  margin: 0 auto;
  overflow: hidden;
}

#testimonials .carousel-nav {
  display: block;
  width: 300px;
  margin: 0 auto;
}
#testimonials .carousel-nav img {
  cursor: pointer;
}
#testimonials .carousel-nav img:active {
  position: relative;
  top: 1px;
}
#testimonials .carousel-nav .nextbtn {
  float: right;
}

#testimonials .carousel-wrap ul {
  display: block;
  list-style: none;
  position: relative;
}
#testimonials .carousel-wrap ul li {
  display: block;
  float: left;
  position: relative;
  width: 400px;
  margin-right: 15px;
}

#testimonials .carousel-wrap .context {
  font-size: 2.0em;
  line-height: 1.45em;
  color: #797670;
  font-style: italic;
  margin-bottom: 6px;
  padding-bottom: 4px;
  border-bottom: 1px solid #dcdcdc;
}

So the .carousel-wrap behaves like a viewport for the testimonials container. It stays at a fixed width of 400px and anything beyond this limit will be hidden from view. A little ways down you’ll see a selector for the .carousel-wrap ul element. This behaves like an overflow container which is dynamically resized using jQuery.

Basically we calculate the total number of list items and equate each one to about 415px (~15px for margins). We get a total value in pixels and this becomes the full width of the unordered list container. Now each testimonial floats along the same horizontal plane, even though we can only see one at a time. When animated the testimonials will slide right/left as they should.

/** client logos **/
#clients {
  display: block;
  margin-bottom: 15px;
}

#clients .clients-wrap {
  display: block;
  width: 700px;
  margin: 0 auto;
  overflow: hidden;
}

#clients .clients-wrap ul {
  display: block;
  list-style: none;
  position: relative;
}

#clients .clients-wrap ul li {
  display: block;
  float: left;
  position: relative;
  width: 140px;
  height: 55px;
  line-height: 55px;
  text-align: center;
}
#clients .clients-wrap ul li img {
  vertical-align: middle;
  max-width: 100%;
  max-height: 100%;
  -webkit-transition: all 0.3s linear;
  -moz-transition: all 0.3s linear;
  transition: all 0.3s linear;
  -ms-filter:"progid:DXImageTransform.Microsoft.Alpha(Opacity=65)";
  filter: alpha(opacity=65); 
  opacity: 0.65;
}
#clients .clients-wrap ul li img:hover {
  -ms-filter:"progid:DXImageTransform.Microsoft.Alpha(Opacity=100)";
  filter: alpha(opacity=100); 
  opacity: 1.0;
}

The clients carousel is structured in a very similar manner. But the full viewport container .clients-wrap is fixed at 700px to overtake more of the page. Same as before the .clients-wrap ul list will be stretched wider based on the total number of internal list items. Since we only need to display tiny logos these items are fixed to 140×55 pixels.

Not all the logos match this exact width/height ratio, so instead I’m using a bit of CSS to center everything vertically + horizontally. vertical-align: middle along with line-height is enough to keep everything organized in place. I’m also using transition properties to animate the image opacity when hovering each logo – the default is 65% but you could toy around with different values.

Testimonials Animation

Inside the carousels.js file I’ve written two separate blocks of code. They both act in a similar manner, but since each carousel was built with unique functionality I wanted to simplify the process. Let’s first take a look at the testimonials slider.

$(function(){
  // vars for testimonials carousel
  var $txtcarousel = $('#testimonial-list');
  var txtcount = $txtcarousel.children().length;
  var wrapwidth = (txtcount * 415) + 415; // 400px width for each testimonial item
  $txtcarousel.css('width',wrapwidth);
  var animtime = 750; // milliseconds for clients carousel

The variable txtcount becomes a reference to count the total number of internal testimonial list items. Then I’m creating the full wrapper width by calculating the total pixels required to hold everything on the same row. animtime is a value in milliseconds which dictates the animation time when sliding between testimonials.

 // prev & next btns for testimonials
  $('#prv-testimonial').on('click', function(){
    var $last = $('#testimonial-list li:last');
    $last.remove().css({ 'margin-left': '-415px' });
    $('#testimonial-list li:first').before($last);
    $last.animate({ 'margin-left': '0px' }, animtime); 
  });
  
  $('#nxt-testimonial').on('click', function(){
    var $first = $('#testimonial-list li:first');
    $first.animate({ 'margin-left': '-415px' }, animtime, function() {
      $first.remove().css({ 'margin-left': '0px' });
      $('#testimonial-list li:last').after($first);
    });  
  });

There are two click event handlers bound onto the two arrow images. The logic is very simple once you understand what’s happening. If the user clicks to go backwards then we need to display the last item sliding in from left-to-right. I’m using jQuery .remove() while appending a negative left margin to the last element. This keeps it just out of viewport over to the left side. The list item is then prepended .before() the very first element, and finally animated to a 0px left margin. Every other item in the list will be pushed down the chain and the last item now displays first in the list.

If the user clicks to go forwards a very similar effect will occur. The first element will be animated -415px over to the left which now puts the 2nd list item in view. But since the now-hidden first element needs to appear at the end of the list, we use jQuery’s remove method along with .after() to deal with positioning.

Client Logo Animations

This one is simpler because we aren’t waiting for the user to click anything. I’m still deriving a new pixel width value for the container based on each internal list item.

 var $clientcarousel = $('#clients-list');
  var clients = $clientcarousel.children().length;
  var clientwidth = (clients * 140); // 140px width for each client item 
  $clientcarousel.css('width',clientwidth);
  
  var rotating = true;
  var clientspeed = 1800;
  var seeclients = setInterval(rotateClients, clientspeed);

The variable clientspeed is also in milliseconds which dictates how often the client carousel should rotate. The variable seeclients initiates a setInterval() method calling the defined function rotateClients(). Naturally this will rotate every 1.8 seconds – but we need to halt rotation when the user is hovering in the logo container.

 $(document).on({
    mouseenter: function(){
      rotating = false; // turn off rotation when hovering
    },
    mouseleave: function(){
      rotating = true;
    }
  }, '#clients');
  
  function rotateClients() {
    if(rotating != false) {
      var $first = $('#clients-list li:first');
      $first.animate({ 'margin-left': '-140px' }, 600, function() {
        $first.remove().css({ 'margin-left': '0px' });
        $('#clients-list li:last').after($first);
      });
    }
  }
});

I setup a variable called rotating which defaults to true. When the page first loads we automatically begin the carousel rotation. But when the user has their mouse inside the #clients container, the variable is set to false. And this logic is checked within the rotateClients() function to see if we need to rotate or not.

So every 1.8 seconds this function will be run. If the user currently has their mouse anywhere inside the clients container then nothing happens. Otherwise we run the same code as before which moves the first item out of view and repositions at the end of the list.

Live DemoDownload Source Code

Closing

Both of these effects may be scaled out and manipulated to work differently in various layouts. I wanted these carousels to appear distinct from one-another, yet still be able to work in any typical portfolio website. Even larger corporations such as web hosts may include testimonials from “average joe” clients. The goal is to foster relatability and get people thinking about the many benefits of your company. Feel free to download a copy of my source code and play around with your own ideas.