Responsive Images in WordPress Using Zurb Foundation’s Interchange

I’ve been looking at using Zurb Foundation’s Interchange for a while. Interchange uses media queries to load content that is appropriate for different users’ browsers. I’ve been wanting to serve up images at an appropriate resolution for a user’s device, and ideally I want this to happen automatically when I insert images into posts within WordPress.

I’m assuming you’re already familiar with writing WordPress themes. If not, there’s lots of help over at WordPress’s Theme Development page. I’ll also assume you’re already using Foundation 5 in your theme, and have included all the necessary CSS and Javascript files to get it running. Again if not, plenty of help here.

If you want a basic theme to try this out quickly, check out my WordPress Foundation 5 Base Theme.

[GARD]

Getting Started

I’ve defined all the php and javascript that we’re going to use in separate files so they’re easy to maintain and quick to add and remove if required. Create a folder in your theme directory called inc/ and then a file called responsiveImageShortcode.php within the new directory.

Include the new file from your theme’s functions.php

require_once(get_template_directory() . '/inc/responsiveImageShortcode.php');

Generating Multiple Resolution Images

The whole idea behind this is that you have several different resolutions of the same image, and you want to serve the one that’s just large enough for the device the website is being viewed on. Handily, WordPress has the add_image_size() function to do this automatically. These image sizes will be generated whenever a new image is uploaded. If you’re adding this to an existing site, you’ll need something like the Force Regenerate Thumbnails plugin to make sure they’re generated for all your existing images. This can take a while!

You’ll probably already have a setup function of some sort in your functions.php file. An example is shown below. The important bits are the add_image_size functions. Make sure they’re included in your setup in functions.php. If you do not have a setup function in functions.php, copy the whole code into your responsiveImageShortcode.php file.

if (!function_exists('gc_basetheme_setup')):

  function gc_basetheme_setup() {

 // Other setup code here...

 /**
 * Custom image sizes
 * 
 * These should be named to match Foundations media queries as they will be automatically used in
 * Interchange for responsive images
 */
 add_image_size('small', 640, 9999);
 add_image_size('medium', 1020, 9999);
 add_image_size('large', 1440, 9999);
 add_image_size('xlarge', 1600, 9999);
 }

endif; // gc_basetheme_setup

add_action('after_setup_theme', 'gc_basetheme_setup');

As mentioned in the code, it’s also important to make sure that the names of these images match the names media queries used by Interchange. Interchange’s named media queries can be seen here. In my case, my grid is 1600px wide at maximum, so there’s no point in supporting any larger than xlarge.

Shortcodes

WordPress supports a feature known as shortcodes. They’re ideal for this case, because the HTML that we’re going to generate is generally extremely long, ugly, and difficult to read when a user is editing their post. Shortcodes replace all this with a short, easily read alternative.

Add the following to the responsiveImageShortcode.php file

/**
 * Responsive Images Shortcodes
 * 
 * Uses Foundation Interchange
 * @see http://foundation.zurb.com/docs/components/interchange.html
 */
if (!function_exists('interchange_shortcode')) :

  function interchange_shortcode($atts) {
    extract(shortcode_atts(array(
        'id' => 1,
        'class' => ''
                    ), $atts));

    $imageSizes = wp_get_attachment_metadata($atts['id']);

    //Output our image sizes using interchange for images format
    foreach ($imageSizes['sizes'] as $size => $info) {
      $attachment_info = wp_get_attachment_image_src($id, $size);

      $dataInterchange .= '[';
      $dataInterchange .= $attachment_info[0] . ', ';
      $dataInterchange .= '(' . $size . ')';
      $dataInterchange .= '],';

      //original image is smaller than small
      if ($size == 'small') {
        $smallIncluded = true;
      }

      //edge case where original image is exactly 1600px wide, so xlarge size not generated
      if ($size == 'xlarge') {
        $xLargeIncluded = true;
      }
    }

    $upload_dir = wp_upload_dir();

    //images smaller than the small size will not generate any sizes other than xlarge
    //make sure the image is made available to smaller devices
    if (!$smallIncluded) {
      $dataInterchange .= '[' . $upload_dir['baseurl'] . '/' . $imageSizes['file'] . ', (small)]';
    }

    //Full size image, only included when xlarge size not generated
    if (!$xLargeIncluded) {
      $dataInterchange .= '[' . $upload_dir['baseurl'] . '/' . $imageSizes['file'] . ', (xlarge)]';
    }

    //Build the interchange <img /> tag
    $html = sprintf('<img alt="%2$s" data-interchange="%1$s" width="%4$d" height="%5$d" class="%6$s" /><noscript><img src="%3$s"></noscript>', $dataInterchange, get_the_title($atts['id']), $imageSizes['file'], $imageSizes['width'], $imageSizes['height'], $atts['class']);

    return $html;
}

endif;
add_shortcode('responsiveimage', 'interchange_shortcode');

This will allow you to insert shortcodes in the format

[responsiveimage id="1" placeholder="image-file.jpg" class="alignleft"]
  • id is the WordPress attachment_id for the image that we wish to be responsive
  • placeholder (optional) is the image that we’ll use in the visual editor. More on this later.
  • class (optional) holds the classes that will apply to the final img tag.

If you know the attachment id of the image you want to insert, that’s it! You can now insert the shortcode in your posts and it’ll serve up an appropriate resolution image on the front-end.

[GARD]

Generating Shortcodes Automatically

Of course, it’s not very practical or user-friendly to keep having to look up the attachment id’s of the images you want to use. In fact, most WordPress users probably wouldn’t know where to look. It’s also a pain if we can’t use WordPress’ excellent ‘Add Media’ popup.

So we’re going to get WordPress to generate our shortcodes automatically. We can do this by hooking into the get_image_tag filter. Add the following to your responsiveImageShortcode.php file:

/*
 * Manipulate the <img /> tag inserted by the html editor
 * Should output images compatible with foundation interchange
 */
if (!function_exists('wf5bt_get_image_tag')) :

    function wf5bt_get_image_tag($html, $id, $title) {
        $imageSizes = wp_get_attachment_metadata($id);
        $placeholder = $imageSizes['file'];

        //keep img classes as set by wordpress
        $doc = new DOMDocument();
        @$doc->loadHTML($html);
        $tags = $doc->getElementsByTagName('img');
        foreach ($tags as $tag) {
            $class = $tag->getAttribute('class');
            $width = $tag->getAttribute('width');
        }

        // Responsive only if this is full size image 
        // && larger than small image size (or no point)
        if (strstr($html, 'size-full') && $width>640) {
            return "[responsiveimage id=\"$id\" placeholder=\"$placeholder\" class=\"$class\"]";
        } else {
            return $html;
        }
    }

endif;
add_filter('get_image_tag', 'wf5bt_get_image_tag', 10, 3);

Now, when a user clicks the ‘Add Media’ button, they’ll see the same WordPress popup screen they are already familiar with. If a full-sized image is placed, this is automatically turned into a [responsiveimage] shortcode. WordPress’ auto-generated classes are maintained. We’re assuming that if they’ve picked any other size of image that they want to use specifically that size, and not a responsive image.

It’s now easy to add responsive images to your site!

Using the Visual (TinyMCE) Editor

Our final task is to make the visual post editor (TinyMCE) show the inserted image in the way the user expects. They’ve just clicked ‘Add Media’, and they’re used to an image being displayed in the post editor. At the moment, all they’ve got is our new shortcode. They’re probably going to be confused!

Add the following to your responsiveImageShortcode.php file:

/**
 * Javascript for the TinyMCE plugin
 */
function gc_add_responsive_image_plugin_js($plugin_array) {
  if (current_user_can('edit_posts') && current_user_can('edit_pages')) {
    $plugin_array['responsiveImagePlugin'] = get_bloginfo('template_url') . '/js/responsiveImagePlugin.js';
  }

  return $plugin_array;
}
add_filter('mce_external_plugins', 'gc_add_responsive_image_plugin_js');

Then create the file /js/responsiveImagePlugin.js with the following code (WordPress 3.8):

/**
 * Responsive image tinymce plugin.
 * 
 * Lots of stuff borrowed from WordPress's own Gallery plugin
 * 
 * Replaces [responsiveimage ... ] shortcodes with full-sized image in tinymce visual editor
 */
tinymce.PluginManager.add('responsiveImagePlugin', function(editor, url) {

  /**
   * Fired when content is added to the tinymce editor
   */
  editor.onBeforeSetContent.add(function(ed, o) {
    o.content = replaceResponsiveImageShortcodes(o.content);
  });

  /**
   * Fired after content from tinymce editor is processed.
   * i.e. when moving to html text editor or when content saved
   */
  editor.onPostProcess.add(function(ed, o) {
    if (o.get)
      o.content = restoreResponsiveImageShortcodes(o.content);
  });

  /**
   * Replace [responsiveimage ... ] shortcodes with the placeholder image in the tinymce visual editor
   * 
   * @param {type} content The content of the tinymce editor
   * @returns String The new content for the tinymceeditor
   */
  function replaceResponsiveImageShortcodes(content) {
    return content.replace(/\[responsiveimage([^\]]*)\]/g, function(match, attrs) {
      placeholder = getAttr(attrs, 'placeholder');

      return '<img src="' + tinymce.documentBaseURL + '../wp-content/uploads/' + placeholder + '" class="responsiveimage mceItem" title="Responsive image" data-shortcode="responsiveimage' + tinymce.DOM.encode(attrs) + '" />';
    });
  }

  /**
   * Return the placeholder <img /> tags to [responsiveimage ... ] shortcodes
   */
  function restoreResponsiveImageShortcodes(content) {
    return content.replace(/(?:<p[^>]*>)*(<img[^>]+>)(?:<\/p>)*/g, function(a, imageHtml) {
      var cls = getAttr(imageHtml, 'class');

      if (cls.indexOf('responsiveimage') !== -1)
        return '[' + tinymce.trim(getAttr(imageHtml, 'data-shortcode')) + ']';

      return a;
    });

  }

  /**
   * Get the value of an attribute given an html-style string of attributes
   * e.g. attr1="value" attr2="value2"
   * 
   * @param {type} s
   * @param {type} n
   * @returns {String}
   */
  function getAttr(s, n) {
    n = new RegExp(n + '=\"([^\"]+)\"', 'g').exec(s);
    return n ? tinymce.DOM.decode(n[1]) : '';
  }

});

Or this for WordPress 3.9:

/**
 * Responsive image tinymce plugin.
 * 
 * Lots of stuff borrowed from WordPress's own Gallery plugin
 * 
 * Replaces [responsiveimage ... ] shortcodes with full-sized image in tinymce visual editor
 */
tinymce.PluginManager.add('responsiveImagePlugin', function(editor, url) {
    
    /**
     * Fired when content is added to the tinymce editor
     */
    editor.on('BeforeSetContent', function(event) {
        event.content = replaceResponsiveImageShortcodes(event.content);
    });

    /**
     * Fired after content from tinymce editor is processed.
     * i.e. when moving to html text editor or when content saved
     */
    editor.on('PostProcess', function(event) {
        event.content = restoreResponsiveImageShortcodes(event.content);
    });

    /**
     * Replace [responsiveimage ... ] shortcodes with the placeholder image in the tinymce visual editor
     * 
     * @param {type} content The content of the tinymce editor
     * @returns String The new content for the tinymceeditor
     */
    function replaceResponsiveImageShortcodes(content) {
        return content.replace(/\[responsiveimage([^\]]*)\]/g, function(match, attrs) {
            placeholder = getAttr(attrs, 'placeholder');

            return '<img src="' + tinymce.documentBaseURL + '../wp-content/uploads/' + placeholder + '" class="responsiveimage mceItem" title="Responsive image" data-shortcode="responsiveimage' + tinymce.DOM.encode(attrs) + '" />';
        });
    }

    /**
     * Return the placeholder <img /> tags to [responsiveimage ... ] shortcodes
     */
    function restoreResponsiveImageShortcodes(content) {
        return content.replace(/(?:<p[^>]*>)*(<img[^>]+>)(?:<\/p>)*/g, function(a, imageHtml) {
            var cls = getAttr(imageHtml, 'class');

            if (cls.indexOf('responsiveimage') !== -1)
                return '[' + tinymce.trim(getAttr(imageHtml, 'data-shortcode')) + ']';

            return a;
        });

    }

    /**
     * Get the value of an attribute given an html-style string of attributes
     * e.g. attr1="value" attr2="value2"
     * 
     * @param {type} s
     * @param {type} n
     * @returns {String}
     */
    function getAttr(s, n) {
        n = new RegExp(n + '=\"([^\"]+)\"', 'g').exec(s);
        return n ? tinymce.DOM.decode(n[1]) : '';
    }

});

So now, when using the visual editor, you see the full-sized image where the [responsiveimage] shortcode is place. In fact everything is visually the same as it was before we started messing about! The user may have no idea that they’ve just inserted an all singing, all dancing, responsive image!

[GARD]

Issues

Support My Site

If you’ve found anything in this article helpful, please consider supporting my site by clicking on any of the exciting adverts!

WordPress Foundation 5 Base Theme

I have included responsive images in my WordPress Foundation 5 Base Theme. If you’re looking to design your own WordPress theme using Foundation 5 elements, it provides a good starting point. It’s open source, so you can clone the git repository for free.


COMMENTS