PHP Responsive CSS3 Modal Gallery (with JavaScript pagination)

This is version 5. Also see all other versions to see how this gallery evolved.

Markup for this gallery demo is auto-generated with PHP, from images in a folder. There are 2 sets of images; small ones in the "small/" folder, and big ones in the "big/" folder - both are identically named. File names become the image captions.

Click on an image below to bring the gallery to life (and resize the browser window for slinky responsiveness)...

Features

A CSS3, responsive image gallery that;

Please view the source of this web page to grab the JS, CSS and HTML markup. Additional PHP code for markup auto-generation can be found towards the bottom of this page.

Pros

Cons

Notes (JavaScript for on-demand images, keyboard controls + pagination)

I won't go through all the evolutionary changes to the gallery again - it has everything that version 3 (JS on-demand images + keyboard controls) has so you can recap on the notes there.

The main thing I'd like to draw your attention to is the HTML5 "data-src" attribute which now references ALL images instead of the standard "src" attribute. With "data-src" in place, the images will not load until JavaScript changes it to "src". This happens at a few points in the script so that the small images only load when pagination links are clicked (into the visible "page" only), and larger images only load when thumbs are clicked, or when previous/next controls take your there.

To demonstrate the on-demand image script, see this YSlow component report showing only 9 images downloaded - 8 thumbs from the first page of the gallery, plus the header logo (there are 80 gallery images in total, weighing in at 3.02MB!).

UX consideration 1... what happens when JS is disabled?

The deleted chunk of info below can be ignored now that alternative markup is included in <noscript> tags. Thinking along the same lines as the IE7/8 workaround (more on that later), where those users can open image enlargements in a new browser window instead of the modal overlay/lightbox, we can do something similar for folks with JavaScript turned off;

<!--[if gte IE 9]><!--><a onclick="load('img-1')"><!--<![endif]--> <!-- no href - use location.replace('#pic-'+id); in load() function -->
<!--[if lte IE 8]><a href="/path/to/big/image.jpg" target="_blank"><![endif]-->
    <img class="small-img" data-src="/path/to/small/thumb.jpg" alt="" />
    <noscript>
         <a href="/path/to/big/image.jpg" target="_blank">
         <img class="small-img" src="/path/to/small/thumb.jpg" alt="" />
    </noscript>

    <span><b>Preview</b></span>
</a>

Please note, this is a minimal fallback that only provides a reduced, token gallery for the first page of thumbnail images - there's a bit of a trade-off between performance, and giving non-JS users something to look at. Just to confirm, and paraphrasing the W3C noscript specs, "the contents of the noscript element will load when JavaScript is unavailable", hence why here it has only been provided for some of the thumbs (those in the first page), and not all of the thumbs.

Unfortunately, nothing. The gallery relies on JavaScript to switch in the "src" attribute of all images, so there is no fallback for when it isn't available. The CSS below simply hides it in those cases;

/* #### - extra css for js enhancement - #### */
#gallery { display:none } /* hide gallery if js is disabled */
.js #gallery { display:block; overflow:hidden } /* show gallery if js is on */

UX consideration 2... is it loading?

The "on-demand" image script takes care of the performance dilemma (thumbs up for mobile), but it could leave users with slower internet connections wondering when that big ol' image is going to appear on their screen. So let's add a simple loading spinner to indicate something is happening... Add this to the CSS;

@-webkit-keyframes rotation { from {-webkit-transform:rotate(0deg)} to {-webkit-transform:rotate(359deg)} }
@keyframes rotation { from {transform:rotate(0deg)} to {transform:rotate(359deg)} }
#gallery .overlay:before {
    content:""; position:absolute; top:40%; margin-top:-0.5em; left:50%; margin-left:-2em; display:block; height:3em; width:3em;
    border:0.5em solid rgba(255,255,255,.15); border-top:0.5em solid rgba(255,255,255,.5); border-radius:100%;
    -webkit-animation:rotation .75s infinite linear; animation:rotation .75s infinite linear
    }

This places a CSS pseudo element loading spinner on the #gallery .overlay. It's always present, but then when an image has loaded, the picture will sit in front of the spinner and hide it!

UX consideration 3... how many thumbs?

Although you can have as many thumbnails in a pagination area as you wish, bear in mind that while 20 of them might look pretty spiffy on a desktop monitor, they become a bit unmanageable on small screens - you'll end up with a long column of pics with the pagination links floated way up off the top of the screen. A good way to counteract this (to a degree) is to resize thumbnails using CSS in mobile view, and then reset them for desktop. A happy side-effect of this is that half-size thumbs on small retina displays look ultra crispy! ;) Of course, you still need to be sensible about the number of thumbs to show per page for performance reasons - I'd personally go with 10 or 12 max.

UX consideration 4... where's the grid?

For predictable results, set a max-width on the pagination areas to encourage thumbnail images to fall into a nice neat grid;

#gallery .pagination { max-width:48.75em } /* fix thumbs to grid */

Compatibility

* The :target selector is not supported in IE7/8 but a bit of markup-mashing with IE conditional comments provides a workaround so those users can open image enlargements in a new browser window instead of the modal overlay/lightbox.

<!--[if gte IE 9]><!--><a onclick="load('img-1')"><!--<![endif]--> <!-- no href - use location.replace('#pic-'+id); in load() function -->
<!--[if lte IE 8]><a href="/path/to/big/image.jpg" target="_blank"><![endif]-->
    <img class="small-img" data-src="/path/to/small/thumb.jpg" alt="" />
    <noscript>
         <a href="/path/to/big/image.jpg" target="_blank">
         <img class="small-img" src="/path/to/small/thumb.jpg" alt="" />
    </noscript>
    <span><b>Preview</b></span>
</a>

* IE7/8 users will not see the magnifying glass icon when they hover over a thumbnail. However, there is fallback for a plain "Preview" bar that they will see instead.

I haven't been able to test in other browsers/devices but feedback is always welcome. Contact me if you spot anything hinky.

PHP Code

The follow PHP code will automatically generate the markup for the gallery, from images in a folder. Requirements;

Drop this somewhere into the body of your PHP web page, remembering to include the CSS and JavaScript too, and change the 4 variables ($dir, $pag, $sort and $caps) near the top to suit. Image paths are relative, so based on the sample below, your web page should be located at "http://www.mywebsite.com/gallery/gallery.php".

<div id="gallerylinks"></div>
<div id="gallery">
<div id="galleryfirst" class="pagination">
<?php // PHP Responsive CSS3 Modal Gallery - Beverley Hooton @ Focus on Function Web Design
$dir = '/home/www/mywebsite.com/gallery/small/'; // root path to small images
$pag = '8'; // images per paginated page
$sort = 'date'; // 'date' = newest first / leave blank ('') for a-z
$caps = 'yes'; // use image captions? 'yes' / 'no'


$exts = '/\.(jpg|JPG|jpeg|JPEG|png|PNG)$/'; // only these file types
if ($hd = opendir($dir)) {
    while (false !== ($fname = readdir($hd))) {
        if (preg_match('/^\.{1,2}$/', $fname)) continue; // exclude current directory, parent directory
        if (!preg_match($exts, $fname)) continue; // exclude file types not in $exts
         $files_array[] = $dir.$fname;
        }
    }
if ($sort == 'date') { // Sort files by modified time, newest to oldest
    array_multisort(array_map('filemtime', $files_array), SORT_NUMERIC, SORT_DESC, $files_array); // SORT_ASC oldest first, SORT_DESC newest first
    } else { // sort files a-z
    sort($files_array);
    }
for ($i = 0; $i < count($files_array); $i++) {
    $next = ($i != count($files_array) - 1) ? "load('img-".($i+2)."')" : "closeX()"; // if next item exists in array, set $next as 'load('img-".($i+2)."')' else 'closeX()'
    $prev = ($i == 0) ? "closeX()" : "load('img-".($i)."')"; // if first item = 0, set $prev as 'closeX()' else 'load('img-".($i)."')'
    $img = str_replace($dir, '', $files_array[$i]);
    $cap = substr($img, 0, strrpos($img, '.')); // strip the extension
    $caption = ($caps == 'yes') ? "<div><p><span>[ ".($i+1)." / ".count($files_array)." ]</span> $cap</p></div>" : "";
    if ($i < $pag) { // no js fallback for thumbs in first page
        $noscr = "<noscript>
            <a href=\"big/$img\" target=\"_blank\">
            <img class=\"small-img\" src=\"small/$img\" alt=\"\" />
        </noscript>";
    } else {
    $noscr = '';
    }
    echo "<div class=\"li\">
        <!--[if gte IE 9]><!--><a onclick=\"load('img-".($i+1)."')\"><!--<![endif]-->
        <!--[if lte IE 8]><a href=\"big/$img\" target=\"_blank\"><![endif]-->
            <img class=\"small-img\" data-src=\"small/$img\" alt=\"\" />
            $noscr
            <span><b>Preview</b></span>
        </a>
        <div id=\"pic-".($i+1)."\" class=\"overlay\">
            <img id=\"img-".($i+1)."\" class=\"big-img\" data-src=\"big/$img\" alt=\"\" />
            $caption
            <a onclick=\"$prev\" class=\"prev\" title=\"Prev\">&#9668;&#9668;</a>
            <a onclick=\"$next\" class=\"next\" title=\"Next\">&#9658;&#9658;</a>
            <a onclick=\"closeX()\" class=\"close\" title=\"Close\">&times;</a>
        </div>
    </div>\n";
    if ($i % $pag == $pag-1 && $i != count($files_array) - 1) { // create pagination after each iteration of $pag, only if next item exists
           echo "</div>\n<div class=\"pagination\">\n";
        }
    }
closedir($hd);
?>
</div>
</div>

Feel free to use this code for any of the previous gallery versions too. The markup changed a bit here to accommodate the pagination markup, so you'll need to tweak it back in the PHP, but aside from that it should work straight out of the box.

Acknowledgement

Inspired by the "CSS3 Modal Popups" tutorial at Script Tutorials. Starter pagination JavaScript sourced via a JSFiddle.

Related

Freebies

Looking for more freebies for your website? Grab a bunch of free PHP, CSS and JavaScript goodies, from flat file CMS' to RSS managers to responsive CSS menus, galleries and sliders.