Designing a Responsive Lightbox Photo Grid Gallery for Drupal 7


Lately, I've been designing and developing photo galleries for both clients and myself. I thought it would be nice to share some methods and practices that I've learned. This tutorial will focus on creating node photo grid galleries that can either be stand alone or attached to other content types.

Two parts

There are two parts to the gallery, the responsive grid of photo thumbnails and then the actual larger image style in a lightbox, both of which will be responsive. For the photo grid, we'll be going old school and simply use a combination of display: inline-block and text-align: justify with percentage based widths on list item elements. For the lightbox, I'll use Magnific Popup which is an MIT licensed responsive jQuery lightbox script.

Methods / First Steps

I'll be customizing a basic field template for the image grid as well as the Image formatter link to image style Module which allows you to link a small image from the photo grid to a larger image style in the lightbox. We'll also use a little jQuery to tweak things and put it all into action.

The first thing is to fire up a basic Drupal 7 install. Create a content type, we'll call it "Gallery", and add an image field. Allow unlimited images to be added and be sure to activate both alt and title tags. I remove the body field, but you may want to keep it if your gallery is going to be stand alone. In my case, I'm attaching galleries to other node types via the Drupal References Module which has node_reference built in as a sub-module. Either way is fine. I name my imagefield 'Gallery Image' with a machine name of field_gallery_image.

Edit the Display Style

Next, we need to tweak the display for our new Gallery content type; go ahead and download and enable the Image formatter link to image style Module.

drush dl image_formatter_link_to_image_style
drush en image_formatter_link_to_image_style

You can also download the tar file for the module, place it in /sites/all/modules and then enable it through Drupal's module page UI. Now manage the display of your Gallery content type and you'll see a new option called "Image link to image style". This allows you to set a style for the image and what it links to. It's up to you what styles you want to use but for the sake of the tutorial, I'll use medium to large but you can create your own image styles and use appropriately.

Customize the Field Template

Next, let's create our field template, this will override the rather typically nasty output that Drupal gives you and allows us for nice minimal HTML output. What I'll do is copy field.tpl.php from the Drupal core /modules/field/theme/ folder. You'll note that in the file, it says:

This file is not used and is here as a starting point for customization only.

Ha, that's us! Copy the file into your theme's template folder and rename it.

Digging in to the Code

We need to come up with a specific name for the file so Drupal will recognize it and correlate it with your custom image field you created above. There's a few ways to do this, you can make an educated guess or you can use the Devel Theme Developer module to figure it out. You can also use the old standard print variables method as well as such:

<?php
print '<pre>';
var_dump(get_defined_vars());
print '</pre>';
?>

From this, I learn that a good candidate file for my custom field template is:

field__field_gallery_image

What's in a Name?

From the above, we can extract the correct name for the template, convert double underscores to double hyphens and likewise single underscores into single hyphens. That would make our template file: field--field-gallery-image.tpl.php. Be careful if you are using the Semantic Fields module, it trumps naming of regular fields even if you don't have a semantic field set to the display here, but simply pay attention to the vars output and you'll be good.

We'll get back to this file later but first go ahead and create some Gallery content. Add a new node with some images and save it. It won't look like much right now, probably just a jumble of images on the page.

Go back to your custom field template and type in:

<?php print t('hello world'); ?>

… just above where the actual code starts after the comments. Clear cache and if all went well, you should see this print out. Great, now we have a custom field template override in our theme. I'm going to convert the code in the template to a nice unordered list that will output the image grid:

<ul class="node-image-gallery <?php print $classes; ?>"<?php print $attributes; ?>>
  <?php if (!$label_hidden): ?>
  <span class="field-label"<?php print $title_attributes; ?>><?php print $label ?>:&nbsp;</span>
<?php endif; ?>

<?php foreach ($items as $delta => $item): ?>
  <li class="field-item <?php print $delta % 2 ? 'odd' : 'even'; ?>"<?php print $item_attributes[$delta]; ?>>
    <?php print render($item); ?>
  </li>
<?php endforeach; ?>
<li class='placeholder'></li>
<li class='placeholder'></li>
</ul>

Two things of note here. I added custom class to the <ul> called node-image-gallery and then added two empty <li> "placeholders" at the end. These are key to make the grid work. For more info on that, see Barrel's blog post, "Text-align: Justify and RWD" which I've referenced at the end of the article. You'll now want to add the custom CSS to your theme relating to the responsive image grid itself. In this case, you'd have code like:

ul.node-image-gallery {
yada yada
}

For the image, I made sure to set the width at 100% and height to auto using CSS within the <li> container.

/* image thumb */
ul.node-image-gallery img {
  width: 100%;
  height: auto;
}

Add all your other Text-align: Justify grid CSS as per the documentation from Barrel and their code pen. Once you add this, you'll have your basic responsive grid up and running. For my list widths, I used 31% which gives me a nice three-across grid of thumbnails but you can really do anything you want here. I also wrote a media query to make the width 48% for smaller screen size devices such as phones. Again, you can go to town customizing here. The beauty in all of this is that you don't need to use floats or margin left and right, this is big.

A view of the responsive thumbnail grid

Script and CSS Preprocessing Functions

Now that the grid is all set, we can go ahead and add the lightbox code. From the Magnific Popup download (see link in the resources below), you need two files, magnific-popup.css and jquery.magnific-popup.js. Place these in your theme's CSS and JS folders respectively or alternatively add add them to a folder in /sites/all/libraries. You'll also need a custom scripts file to instantiate Magnific. We can call all the scripts using preprocess functions in our theme's template.php file. You could also call them from your .info file but I like doing it from template.php as you have more control over when and where these get called, i.e. only certain content types. I use preprocess_node to call magnific for only the gallery content type:

function MYTHEME_preprocess_node(&$vars, $hook) {
  // Add scripts to the gallery content type.
  if (isset($vars['node']) && $vars['node']->type == 'gallery') {
    // Add js.
    drupal_add_js(drupal_get_path('theme', 'MYTHEME') . '/js/jquery.magnific-popup.js');
    $vars['scripts'] = drupal_get_js();
    // Add css.
    drupal_add_css(drupal_get_path('theme', 'MYTHEME') . '/css/magnific-popup.css');
    $vars['styles'] = drupal_get_css($css);
  }
}

… and then preprocess_html for my scripts file.

function MYTHEME_preprocess_html(&$vars, $hook) {
  // Custom scripts file.
  drupal_add_js(path_to_theme() . '/js/scripts.js',
    array(
      'scope' => 'footer',
      'preprocess' => TRUE,
      'weight' => '9999',
    )
  );
}

For the above, be sure to replace MYTHEME with the actual machine name of your theme. Note that I weight the scripts file to come last in the footer for good measure.

The jQuery

Now in our scripts file as mentioned above, we instantiate Magnific and a few custom jQuery functions.

(function ($) {
   // Do this the Drupal 7 way!
   Drupal.behaviors.miscTheme = {
      attach: function(context, settings) {
          // End drupal 7 calls

// Add the magnific popup class to the image links.
$('ul.node-image-gallery li a').addClass('magnific-popup');

// Render the image title as a title in the image href so it can be used as a caption in the lightbox.
$('ul.node-image-gallery li img').each(function () {
    $(this).closest('ul.node-image-gallery li a').attr('title', $(this).attr('title'));
});

// Magnific Popup.
if($().magnificPopup) {
  $('.magnific-popup').magnificPopup({
    type: 'image',
    midClick: true,
    removalDelay: 300,
    mainClass: 'my-mfp-slide-bottom',
    gallery: {
      enabled: true, // set to true to enable gallery
      preload: [0,2], // read about this option in next Lazy-loading section
      navigateByImgClick: true,
      titleSrc: 'title',
      arrowMarkup: '<button title="%title%" type="button" class="mfp-arrow mfp-arrow-%dir%"></button>',
      tPrev: 'Previous (Left arrow key)', // title for left button
      tNext: 'Next (Right arrow key)', // title for right button
      tCounter: '<span class="mfp-counter">%curr% of %total%</span>'
  }
});
}
  }}})
    (jQuery);

Well, that's all a bit long winded but let's analyze a few key points. We add a magnific-popup class to the image lists for later instantiation of Magnific. Next, we grab the image title and pop it in the image href so it can be used as a caption in the lightbox. We then initialize Magnific itself but note the if($().magnificPopup) {, I don't want to call this unnecessarily on pages where it does not exist, that could throw some errors so we first check to see the if jquery.magnific-popup.js exists within the page. It's time to clear cache and check to see if everything is working.

Magnific popup in action

If not, look at your debugging tools in Chrome or Firefox to see if there are any errors. Also check all your CSS and script paths. One thing that got me initially, the default field output was minified and the methods outlined here won't work with that. It's why we use a custom field template to 'unminify' the HTML.

Extra Credit

At the beginning, I mentioned that you can attach galleries to any other content types. You'd do this with the Drupal References Module, it works really great. Create a new field, set "Gallery" to the content type to be referenced, set your display to "Rendered Node" with an auto complete text field and you are good to go.

Attaching a gallery to other node types

Please be sure to check out and read all the documentation and resources provided below. It took me several weeks to really get dialed in to these concepts so it may not happen for you overnight if you are a beginner. Intermediate to advanced developers should be ok with the concepts here.

Resources

Tags