Skip to main content
  • Home
  • Blog
  • Sandbox

Alvin Pascoe

Website Developer

Alvin Pascoe
Loading.....
  • Drupal 7 Dynamic Content Carousel - Dynamic Next and Previous Links
    Drupal 7 Dynamic Content Carousel - Dynamic Next and Previous Links
  • JavaScript assert() Function
    JavaScript assert() Function

Drupal 7 Dynamic Content Carousel - Carousel Transitions

UPDATE: Complete files for the 'AJAX Content Loader' module, 'Previous Next Node' module and theme customisations can now be found at the bottom of this tutorial.

2nd May, 2015

This is part five of a five part series Drupal 7 Dynamic Content Carousel. If you haven't read part one yet, it may help to read it before moving forward - Part One - Introduction

In the previous part of the series, we added dynamically generated 'Next' and 'Previous' links to our content carousel...although currently it is more of a content generator. In this part, let's add a carousel and create transitions between content refreshes.

Research

Ideally, I wanted to use a tried and tested carousel implementation. It saves time and why reinvent the wheel? If I couldn't find anything suitable then I'd look more into creating a custom carousel.

I've previously worked with a few different carousels, both with and without Drupal. When using Drupal, I've tended to use either the Views Slideshow or, more recently Flex Slider modules. They're usually all you need when working with Drupal, however, I wanted to look into stand-alone carousels (not associated with a module) as I suspected I may need a lot of control over the implementation. Although you can customise the previously mentioned modules with theme overrides, you're still working within the Drupal framework (and I didn't want to risk needing to patch a module and maintain it indefinitely!).

When it comes to stand-alone carousels, Cycle is a no brainer. Mature, dependable, great documentation and plays nice with most other JavaScript. I wanted to see what else was out there before committing though.

While looking around I came across Slick. I was impressed by the demo page/docs and some of the features it comes with out-of-the-box (such as 'Filtering' and 'Responsive Display' - the ability to change carousel options at specified breakpoints).

I decided to play around with Slick for a while and see how it goes...and I'm glad I did!

Implementation

Step 1 - Download and include library

We need to download the Slick library and add it to our site.

I've always found adding external libraries to a Drupal site more tedious than it should be. I used to create a 'libraries' directory in my theme folder, add my libraries there and then use drupal_add_js or drupal_add_css in a THEME_preprocess_page(&$variables) function. However a better way is as follows:

a) Download and enable the Libraries API module.
b) Download the Slick library to the 'sites/all/libraries' directory. I created a new 'Slick' directory and placed the files in there.
c) Declare the library to Drupal. This is achieved by adding a THEME_libraries_info() function to your 'template.php' file.

function <THEME>_libraries_info() {
  // Paths are relative to the individual library's directory
  // sites/all/libraries/<LIBRARY>
  $libraries['slick'] = array(
    'name' => 'Slick',
    // Version callback is required, can just return TRUE in callback if unconcerned about library version
    'version callback' => 'short_circuit_version',
    'files' => array(
    // Located at: sites/all/libraries/slick/slick/slick.min.js
    // Careful of the two nested 'slick' directories
      'js' => array('slick/slick.min.js'),
      'css' => array('slick/slick.css'),
    ),
  );

  return $libraries;
}

/**
* Short-circuit the version argument.
*/
function short_circuit_version() {
  return TRUE;
}

template.php fragment

Clear the cache.

If you've installed Drush, you can check if the library has been correctly declared by running:

drush libraries-list

Your new library should be in the returned list with a status of 'OK'.

d) Load the library when necessary. To do this, we simply call libraries_load('slick') in a preprocess function. I've added mine within my THEME_preprocess_page function (in Omega 4 based themes, this can be added to the 'sites/all/themes/<theme>/preprocess/page.preprocess.inc' file. In other themes, it can simply be added to the theme's 'template.php' file). I wrapped it inside a condition which checks the content type of the node before loading...the library is only loaded on 'article' node types (if you'd like the library loaded on all pages, you can safely remove the 'if' statement).

function THEME_preprocess_page(&$variables) {
  //Include files based on content type
  if (isset($variables['node']->type) && !empty($variables['node']->type) && ($variables['node']->type == 'article')){
    // Load 'Slick' carousel library on blog pages
    libraries_load('slick');
  }
}

template.php fragment.

Clear the cache.

The library should now be declared and loaded. You can test it by navigating to a page on your website where the library will be used and using the developer tools inspector to search the markup for 'slick.min.js' (or another file that is imported by the library).

Step 2 - Test carousel

Before proceeding, we should check the carousel is functioning correctly.

Add the following markup to the main content section of your theme's page.tpl.php file (maybe just above the render($page['content']) statement).

<div class="carousel">
  <div class="slide">Slide one</div>
  <div class="slide">Slide two</div>
  <div class="slide">Slide three</div>
</div>

page.tpl.php fragment.

In order to test the carousel, we'll need to initialise the Slick carousel. Since we're only testing, let's keep the initialisation code simple. In your theme's JavaScript file, add the following statement:

(function ($) {

  $('.carousel').slick();

})(jQuery);

/sites/all/themes/<THEME>/js/script.js.

After this, when you refresh your website, you should have a functioning (albeit basic) carousel on the page. Now we know the carousel works, let's remove the test HTML and JavaScript.

Step 3 - Add carousel markup to template

I intend to use the carousel on blog article pages. Ideally, I would like to only add the carousel markup to blog pages, it would be great if I could create a blog page specific 'page.tpl.php'. Thankfully, this is really simple process.

a) Add content type theme suggestions. By adding the following snippet to the <THEME>_preprocess_page function, we tell Drupal to look for content type specific page templates.

function <THEME>_preprocess_page(&$variables) {

  ...

  if (isset($variables['node'])) {
    // If the node type is "article" the template suggestion will be "page--article.tpl.php".
    $variables['theme_hook_suggestions'][] = 'page__'. str_replace('_', '__', $variables['node']->type);
  }
}

template.php fragment.

b) Create a content type specific page template.

Create a new template (tpl.php) file in your theme and name it based on the desired content type. For example, I have an 'Article' content type, I called my template page--article.tpl.php (if you have any doubt about where to save it, save it in the same directory as your theme's page.tpl.php).

Copy the contents of your theme's page.tpl.php into your new template and save.

c) Add carousel structure to page. Exactly where you place the wrapper code will depend on your individual design but if you're in doubt, just use trial and error to find the best spot (start by wrapping the <?php print render($page['content']); ?> line of code and test from there).

We need to wrap our main content in four div elements, the outer one (.main-carousel-wrapper) will be used to track the state of the AJAX and carousel updates, the next one (.main-carousel) will be used to initialise the carousel, the next element is for the slide (.slide) and the final one is to allow theme-agnostic targeting of the slide content (.slide-content).

...
<!-- Main carousel wrapper - this element will be used to track the AJAX and carousel state -->
<div class="main-carousel-wrapper">

  <!-- Main carousel - this element will be used ti initialise the carousel -->
  <div class="main-carousel">

    <!-- Slide wrapper - everything inside here will be part of a carousel transition -->
    <div class="slide">
    
      <!-- Slide content - easily target slide content -->
      <div class="slide-content">
      
        <!-- Main content -->
       <?php print render($page['content']); ?>
    
      </div>  <!-- slide-content end -->

    </div>  <!-- slide end -->

  </div>  <!-- carousel end -->

</div>  <!-- carousel wrapper end -->
...

page--article.tpl.php code skeleton

Step 4 - Initialise Slick carousel

Now we have the structure in place, let's initialise the carousel...for real this time. Switch to your theme's JavaScript file.
First let's add the Drupal behaviors skeleton to the file:

(function ($) {
  // Carousel initialisation
  Drupal.behaviors.carousel = {
    attach: function (context, settings){

    }
  };
})(jQuery);

sites/all/themes/<THEME>/js/script.js

Next, let's add the carousel initialisation script (and some helper variables).

(function ($) {
  // Carousel initialisation
  Drupal.behaviors.carousel = {
    attach: function (context, settings){
      
      // Selectors
      var carousel = '.main-carousel';
      var mainContentWrapper = '.main-carousel-wrapper';

      // Initialise carousel
      $(carousel, context).once().slick({
        prevArrow: previousLink,
        nextArrow: nextLink,
        infinite: true
      });

    }
  };
})(jQuery);

sites/all/themes/<THEME>/js/script.js

Now when you reload the website, the page should appear as normal. The pager won't work yet because our carousel only has one slide. Let's change that.

Step 5 - Dynamically add and remove slides.

Our aim here is that when the 'Next' or 'Previous' button is clicked we:

  • Create a new carousel slide, populate the slide and begin transition.
  • After transition, remove old slide.

To get things started, let's add a couple of selector variables and wrapper functions. initPreviousSlide() and initNextSlide() will be used to set up event handlers on the 'Next' and 'Previous' links. Let's also add a couple of callback functions (provided by Slick carousel), beforeChange will be used to update the content wrapper classes before a transition and afterChange will be used to reinitialise the classes and to remove the old slide after transition.

(function ($) {
  // Carousel initialisation
  Drupal.behaviors.carousel = {
    attach: function (context, settings){
      
      // Selectors
      var carousel = '.main-carousel';
      var mainContentWrapper = '.main-carousel-wrapper';
      var previousLink = '.js-carousel-pager .prev a';
      var nextLink = '.js-carousel-pager .next a';
      // 'mainContent' selects the content that needs to be updated.
      // This is the element directly below the '.slick-current' element.
      var mainContent = '.slick-current > .slide-content';

      // Initialise carousel
      $(carousel, context).once().slick({
        prevArrow: previousLink,
        nextArrow: nextLink,
        infinite: true
      });

      // Before slide transition
      $(carousel, context).on('beforeChange', function(event, slick, currentSlide, nextSlide){
        $(mainContentWrapper).removeClass('js-carousel-loaded');
        $(mainContentWrapper).addClass('js-carousel-loading');
      });

      // After slide transition
      $(carousel, context).on('afterChange', function(event, slick, currentSlide){
        // Remove old slide
        $(carousel).slick('slickRemove', currentSlide, true);
        $(mainContentWrapper).removeClass('js-carousel-loading');
        $(mainContentWrapper).addClass('js-carousel-loaded');
      });

      // Initialise next/prev event handlers
      initPreviousSlide(previousLink, carousel);
      initNextSlide(nextLink, carousel);

    }
  };
})(jQuery);

sites/all/themes/<THEME>/js/script.js

Now let's actually create the wrapper functions. We're going to use Slick carousel's built in functions to create a slide and manually transition to it. After initNextSlide(nextLink, carousel);, add the following:

function initPreviousSlide(trigger, carousel){
  $(trigger).once().on('click', function(){
    $(carousel).slick('slickAdd', _initSlideContent(mainContent));
    $(carousel).slick('slickPrev');
  });
}

function initNextSlide(trigger, carousel){
  $(trigger).once().on('click', function(){
    $(carousel).slick('slickAdd', _initSlideContent(mainContent));
    $(carousel).slick('slickNext');
  });
}

sites/all/themes/<THEME>/js/script.js - fragment.

Here we attach a 'click' event handler to the 'Next' and 'Previous' links. When either link is clicked, we use Slick's slickAdd method to add a new slide. The content of the new slide is created via a helper function _initSlideContent(). After the new slide is created, we manually trigger a carousel transition to the new slide.

The _initSlideContent() function duplicates the content from the current slide (using the function's parameter to pass the content root element), wraps it in a slide 'div' and returns the result. Add the following below the previous helper functions.

function _initSlideContent(selector){
  var content = $(selector)[0].outerHTML;
  var output = '<div class="slide">' + content +'</div>';
  return output;
 }

_initSlideContent() - sites/all/themes/<THEME>/js/script.js

Currently we duplicate the entire current slide into the new slide. You could alter this process by only copying sections that will not be updated via AJAX (so in my example, not copying the page title, featured image and node content), however my benchmark tests suggest that doing this may slow down the process considerably.

If you did want to remove non-essential elements, you can simply add the following line at the start of the function:

$(<selector>).empty();

where <selector> is the selector targeting the non-essential elements (but double check the performance of the alteration before committing).

Now try refreshing your page and testing the automated Next/Previous links. You should have a fully functioning AJAX content carousel!

Extra Step - Transition loader.

You'll notice that the content updates mid-transition...seeing as we've come this far, let's add a loader notification between transitions and hide the ugly mid-transition update.

a) Add the loading markup. Where you add the markup will depend on your structure, I added mind directly inside the .main-carousel-wrapper element in my page--article.tpl.php:

<!-- Start of template -->
<!-- ... -->

<div class="main-carousel-wrapper">

  <div class="ajax-loader-notice">
    <p>LOADING...</p>
  </div>

  <div class="main-carousel">
    <div class="slide">
      <div class="slide-content">

      <!-- Main content -->

      </div>
    </div>
  </div>
</div>

<!-- ... -->
<!-- Remainder of template -->

page--article.tpl.php fragment.

In this example, I've added the word 'LOADING...' but you can replace this with a gif or some other element.

b) Add loading styles. Remember those AJAX and carousel styles we manipulated on the .main-carousel-wrapper in order to track its state. Now we can use them to tell us when to hide and display the loader.

We'll use CSS to display the loader when a carousel transition or AJAX update begins (when .js-carousel-loading or .js-ajax-loading has been added to .main-carousel-wrapper) and we'll hide it when both the carousel transition AND the AJAX update have completed.

Add the following to your theme's stylesheet.

.main-carousel-wrapper{
  position:relative;
}

.ajax-loader-notice{
  /* Position loader. */
  position:absolute;
  width:100%;
  height:100%;
  background-color:#fff;
  /* Hide loader. */
  opacity:0;
  transition:opacity 0.25s ease-out;
}

.ajax-loader-notice p{
  text-align:center;
}

.main-carousel-wrapper.js-carousel-loading .ajax-loader-notice,
.main-carousel-wrapper.js-ajax-loading .ajax-loader-notice{
  /* Display loader if either the carousel or AJAX are still loading. */
  /* Hide only when BOTH are complete. */
  /* Use z-index to ensure loader appears above slides. */
  opacity:1;
  z-index:10;
}

/sites/all/themes/<THEME>/css/styles.css.

Refresh the website and click a next/previous link. You should see the loader appear over the content during the transition! Now you've got the basic loader working, you can jazz it up a little however you see fit.

So there you have it, if you've followed along from the first article you should now have a fully functional dynamic content carousel!


Since this was quite a long tutorial, I've added parts of the code on Github.

  • AJAX Content Loader custom module.
  • Previous Next Node custom module.

Throughout this tutorial I've been using a custom subtheme of 'Bartik' to make theme level customisations. You can find the subtheme below. If you intend to install the subtheme (in '/sites/all/themes'), remember to download the 'Slick' carousel library to '/sites/all/libraries' in order for the carousel to work.

  • myBartik subtheme.

Let me know how you found the process and if you have questions, feel free to leave comments below.

I'd also be interested in hearing any improvements or changes you've made.

  • Drupal
  • Tutorial
  • Previous Article: Drupal 7 Dynamic Content Carousel - Dynamic Next and Previous Links
    Previous Article: Drupal 7 Dynamic Content Carousel - Dynamic Next and Previous Links
  • Next Article: JavaScript assert() Function
    Next Article: JavaScript assert() Function

Blog Archives

  • December 2019 (1)
  • November 2019 (1)
  • August 2019 (1)
  • May 2019 (1)
  • October 2018 (1)
  • February 2017 (2)
  • January 2017 (3)
  • November 2016 (1)
  • July 2016 (1)
  • January 2016 (1)
More...

Categories

  • Angular
  • CSS
  • Development Environment
  • Drupal
  • Git
  • JavaScript
  • Mac
  • Programming
  • React
  • Tutorial
  • Workflow
  • Home
  • Blog
  • Sandbox
  • Pattern Library
© 2026 Alvin Pascoe
hello{at}alvinpascoe{dot}com
Made by Me, Alvin Pascoe