Designing a Cool Slider with Minimal Effort

Background

The Childhood group has a couple of special needs for an important subset of their pages. Some of their pages focus on an object of group of objects, and they want to be able to have those images display at the top of the page in the following manner (approx.):

childhood-exhibit-mockup.png

Their initial plan was to install one of the many slider plugins – they chose one which was particularly lovely to look at. However, wordpress slider plugins are not really designed for automated use. They depend on the user to create slideshows and then attach the slideshows to individual posts, usually by adding a "shortcode" to the post. The slider will show up wherever the user adds the shortcode, so consistency of appearance will depend on the users' consistency.

For this project, in which the website will be handed over to people with no technical experience, it is probably better to give them a more constrained solution that restricts them to a uniform layout. It turns out that this is not at all a simple task, but in the end it was not so hard. Here's the process I followed when I was figuring it out.

Architecture

I had two goals:

  • simplest possible user experience
  • simplest and most maintainable code

These two goals are always in tension. In this case, the first goal is the most important, so we will do some slightly convoluted things to make our code work. Here's what we need:

  • a simple, obvious way to attach multiple images to pages – like the built-in "featured image" capability, but more powerful
  • a slider "library". Foundation actually comes with one, called Orbit, but it is deprecated and not as pretty as the Childhood group wanted. So I hunted around and found one called FlexSlider, which seemed powerful and pretty enough for the group's needs.
  • page templates that displayed those attached images using FlexSlider. That is, the templates have to produce HTML markup that FlexSlider recognizes and transforms into slider form. As you'll see, for most sliders this takes the form of a <ul> list of images that's wrapped in one or more <div class="something distinctive">'s
  • some bits of glue that tell WordPress how to include the FlexSlider code when pages are loaded, and a few other little tasks.

Multiple Attachments

There are a couple of wordpress plugins that do this kind of thing, and I settled more or less at random on Attachments, which is relatively simple and straightforward. I installed it and carefully read the documentation, which I always try to do when I install a plugin. In this case, it was clear and well-written, which is usually a good sign. The plugin is pretty powerful and allows you to set up complex entry fields on different post types; this is detailed here, but I didn't need this capacity so I ignored it for now. Much more important was learning how to get access to the attachments. I learned a few things.

First, I found out that you need to "create an Attachments instance" in order to access them in your template:

<?php 
  /* 'attachments' is the default name, don't change it */
  $attachments = new Attachments( 'attachments' );
?>

Second, I learned how to loop through attachments while creating some HTML code to wrap them in:

<?php if( $attachments->exist() ) : ?>
  <h3>Attachments</h3>
  <ul>
    <?php while( $attachment = $attachments->get() ) : ?>
      <li>
        <pre><?php print_r( $attachment ); ?></pre>
      </li>
    <?php endwhile; ?>
  </ul>
<?php endif; ?>

Finally, I learned how to search only for certain attachments. All of this was useful later on.

FLexSlider

It's common to want to include external code – libraries – in a theme. WHen you do this, you need to know:

  • where do you get the files, and where should you put them?
  • what markup does the code require in order to function properly?

Files

FlexSlider code is kept on the Github code-sharing website, and Grunterie has a sub-directory called "lib", where external code is supposed to go. So, I copied the FlexSlider files into the lib directory of my Grunterie theme. This was easier for me to do than it would be for you, since you don't have root access to the server! Well, so, I did that step for you…

Markup

The FlexSlider demo page shows the basic markup we need:

<!-- Place somewhere in the <body> of your page -->
<div class="flexslider">
  <ul class="slides">
    <li data-thumb="slide1-thumb.jpg">
      <img src="http://2014.hackinghistory.ca/wp-content/uploads/2015/02/wpid-slide1.jpg" />
    </li>
    <li data-thumb="slide2-thumb.jpg">
      <img src="http://2014.hackinghistory.ca/wp-content/uploads/2015/02/wpid-slide2.jpg" />
    </li>
    <li data-thumb="slide3-thumb.jpg">
      <img src="http://2014.hackinghistory.ca/wp-content/uploads/2015/02/wpid-slide3.jpg" />
    </li>
    <li data-thumb="slide4-thumb.jpg">
      <img src="http://2014.hackinghistory.ca/wp-content/uploads/2015/02/wpid-slide4.jpg" />
    </li>
  </ul>
</div>

This is a really simple structure:

flexslider div 
  --> slides ul
     --> li with data-thumb attribute
         --> img tag
     --> close li
  --> close ul
close div

the data-WHATEVER attribute is a special notification for Javascript – it says "hey, I can be used for some data purpose. Do something with me." In this case, it says, "I am an image in the slider. But for the navigation row on the bottom, use the thumbnail version of me, which is saved separately and is much smaller."

This will be pretty easy to reproduce in a template – all we need to do is to get the right URL's for all the attached images.

Writing the page templates

My preference is to save functions in functions.php or another file, and then just call those functions from within a template file. That way, if it turns out I want to use the function somewhere else, I can still do so. (Note: I also stole most of this code from here).

So, first I write the function:

function hackhist_slider_template() {
    /* collect the attachments */
    $attachments = new Attachments('attachments');
    $search_args = array(
        'instance'      => 'attachments',       // search 'attachments' instance
        'post_id' => get_the_ID(), // only get the attachments for this post, not all posts
        'filetype'        => 'image'  // search through the images, but not documents like PDF's
    );

    $attachments->search( null, $search_args ); // don't actully search for anything -- return all the images
    if( $attachments->exist() ) : 
        // Start the Slider, provided there are some images attached!
        ?>
    <div class="flexslider row"> <!-- use the flexslider class, but also give slexslider its own row, so the page content doesn't compete with it  -->
      <ul class="slides"> <!-- make a list of slides -->
      <?php while( $attachments->get() ) :  ?>  <!-- loop through the attachments -->
         <li data-thumb="<?php echo $attachments->src('thumbnail');?>"> <?php  echo $attachments->image( 'large'); echo $attachments->field('caption'); ?></li> <!-- one li for each image -->
      <?php endwhile; ?>
      </ul><!-- end .slides -->
   </div><!-- end .flexslider .row -->

   <?php 
     endif;

    // Reset Post Data
    wp_reset_postdata(

    );
}

        ?>

Then I add the function to my template file. I will be using it with pages, so I add it to page.php:

<?php get_header(); ?>
        <?php get_sidebar(); ?>

<!-- Row for main content area -->
        <div class="small-12 large-8  columns" id="content" role="main">

        <?php /* Start loop */ ?>
        <?php while (have_posts()) : the_post(); ?>
                <article <?php post_class() ?> id="post-<?php the_ID(); ?>">
                        <header>
                                <h1 class="entry-title"><?php the_title(); ?></h1>
                                <?php reverie_entry_meta(); ?>
                        </header>

<!-- This is the function call!! --> <?php echo hackhist_slider_template(); ?>

                        <div class="entry-content">
                                <?php the_content(); ?>
                        </div>
                        <footer>
                                <?php wp_link_pages(array('before' => '<nav id="page-nav"><p>' . __('Pages:', 'reverie'), 'after' => '</p></nav>' )); ?>
                        </footer>
                </article>
        <?php endwhile; // End the loop ?>

        </div>
        <div class="row">
        </div>       
<?php get_footer(); ?>

Notice the function call in between the article header and the entry-content.

Adding Glue

Now we pull it all together by adding the glue code

functions.php

Rather than store all this code in functions.php, which is normally recommended, we can put almost all our glue in a separate file in lib/Flexslider. That way, if you ever want to change themes or something, you can quickly grab everything you need. But we will need to tell WordPress to load the file. so, we add this code to functions.php:

require( get_template_directory() . '/lib/FlexSlider/slider.php' );

Dead simple.

slider.php

Now we create slider.php, and add the function described above. But that's not enough. We also need our glue.

Activate FlexSlider

First, tell WordPress where to find flexslider, and to load it on every page:

// Enqueue Flexslider Files
function hackhist_slider_scripts() {
    wp_enqueue_script( 'jquery' ); 
    wp_enqueue_style( 'flex-style', get_template_directory_uri() . '/lib/FlexSlider/flexslider.css' );
    wp_enqueue_script( 'flex-script', get_template_directory_uri() .  '/lib/FlexSlider/jquery.flexslider-min.js', array( 'jquery' ), false, true );
}
add_action( 'wp_enqueue_scripts', 'hackhist_slider_scripts' );

Set Slider Options

Then, we set the default slider options & add them to the top of every page:

// Initialize Slider

function hackhist_slider_initialize() { ?>
    <script type="text/javascript" charset="utf-8">
    jQuery(window).load(function() {
        jQuery('.flexslider').flexslider({
                animation: "fade",
                   direction: "horizontal",
                   slideshowSpeed: 7000,
                   animationSpeed: 600,
                   //smoothHeight: true,
                   controlNav: "thumbnails"
                   });
    });
    </script>
<?php }
add_action( 'wp_head', 'hackhist_slider_initialize' );

Feel free to change those around a little, or add some other options from the many possibilities.

Summing up

That's it! The full content of lib/flexSlider/slider.php follows in the next code block. You can still customize this further:

  • add CSS properties to the thumbnails (borders, paddding, etc.)
  • style the captions so that they look a little less stupid; or remove them if you don't like them.
  • doubtless many other ways

Finally, here is the full code of my lib/flexSlider/slider.php, which you can just copy:

<?php

// Enqueue Flexslider Files

function hackhist_slider_scripts() {
    wp_enqueue_script( 'jquery' );

    wp_enqueue_style( 'flex-style', get_template_directory_uri() . '/lib/FlexSlider/flexslider.css' );

    wp_enqueue_script( 'flex-script', get_template_directory_uri() .  '/lib/FlexSlider/jquery.flexslider-min.js', array( 'jquery' ), false, true );
}
add_action( 'wp_enqueue_scripts', 'hackhist_slider_scripts' );

// Initialize Slider

function hackhist_slider_initialize() { ?>
    <script type="text/javascript" charset="utf-8">
    jQuery(window).load(function() {
        jQuery('.flexslider').flexslider({
                animation: "fade",
                   direction: "horizontal",
                   slideshowSpeed: 7000,
                   animationSpeed: 600,
                   //smoothHeight: true,
                   controlNav: "thumbnails"
                   });
    });
    </script>
<?php }
add_action( 'wp_head', 'hackhist_slider_initialize' );

// Create Slider

function hackhist_slider_template() {
    /* collect the attachments */
    $attachments = new Attachments('attachments');
    $search_args = array(
        'instance'      => 'attachments',       // search 'attachments' instance
        'post_id' => get_the_ID(), // only get the attachments for this post, not all posts
        'filetype'        => 'image'  // search through the images, but not documents like PDF's
    );

    $attachments->search( null, $search_args ); // don't actully search for anything -- return all the images
    if( $attachments->exist() ) : 
        // Start the Slider, provided there are some images attached!
        ?>
    <div class="flexslider row"> <!-- use the flexslider class, but also give slexslider its own row, so the page content doesn't compete with it  -->
      <ul class="slides"> <!-- make a list of slides -->
      <?php while( $attachments->get() ) :  ?>  <!-- loop through the attachments -->
         <li data-thumb="<?php echo $attachments->src('thumbnail');?>"> <?php  echo $attachments->image( 'large'); echo $attachments->field('caption'); ?></li> <!-- one li for each image -->
      <?php endwhile; ?>
      </ul><!-- end .slides -->
   </div><!-- end .flexslider .row -->

   <?php 
     endif;

    // Reset Post Data
    wp_reset_postdata(

    );
}

?>

PS!!

I added this code to my style.scss, just for fun:

ul.slides {
    li {

        p {
            position: absolute;
            background: none repeat scroll 0% 0% rgba(0, 0, 0, 0.3);
            left: 0px;
            width: 100%;
            text-align: center;
            color: white;
            padding: 15px;
            bottom: -20px;
        }
    }
}