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;
- Includes pagination to split a large gallery into smaller "pages" and make it more manageable
- Opens an image enlargement in a modal overlay/lightbox (by using the :target selector)
- Has "previous" and "next" controls to cycle through the gallery from within the modal overlay/lightbox
- Includes keyboard navigation to move back and forth through the gallery from within the modal overlay/lightbox
- On a small screen, the "previous" and "next" controls become extra large tappable areas for easy finger jabbing
- Uses CSS3 transitions to animate a gentle fade effect (on thumbs and modal overlay/lightbox)
- Includes optional image captions (inside the modal overlay/lightbox) - auto-generated with PHP
- Loads ALL images on demand instead of during initial page load (only those in the first page are loaded by default)
- A <noscript> fallback allows image enlargements (of thumbs in the first page) to be opened in a new window when JavaScript is disabled
- Shows a CSS3 loading spinner to indicate that larger images are loading
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
- Kind on screen space; Pagination breaks a large gallery into more manageable page chunks.
- Performance friendly; Loads ALL images on demand instead of during initial page load (except those in the first page).
- You can cycle through ALL images in the modal overlay, no matter which page is visible on the screen.
- You can bookmark or link to a larger image and it will load inside the modal overlay/lightbox.
- Browser history is not affected while viewing image enlargements, thanks to JavaScript's location.replace() method.
- A <noscript> fallback allows larger images (of thumbs in the first page) to be opened in a new window when JavaScript is disabled.
Cons
- Extra markup for the IE7/8 workaround. You can use something like Selectivizr to make older IE recognise the :target selector, but that is outside the scope of this demo, although here is a gallery demo using Selectivizr for IE8 support. You also need to set an explicit CSS height on #gallery for IE7.
- Minimal fallback for when JavaScript is disabled; Only image enlargements for thumbs in the first page are accessible.
- Use of "data-src" in place of "src" in the <img> tag invalidates markup.
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
- IE7+ *
- Chrome
- Safari
- Firefox
- Opera
- Android
- iOS
* 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;
- A set of thumb images (the ones visible in the web page) should be uploaded to a folder called "small/"
- A set of large images (the enlargements in the modal overlay) should be uploaded to a folder called "big/"
- Both sets of images should be named exactly the same thing (i.e. "small/My Pet Cat.jpg", and "big/My Pet Cat.jpg")
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\">◄◄</a>
<a onclick=\"$next\" class=\"next\" title=\"Next\">►►</a>
<a onclick=\"closeX()\" class=\"close\" title=\"Close\">×</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
- Version 1: Responsive CSS3 Modal Gallery (no JavaScript)
- Version 2: Responsive CSS3 Modal Gallery (with JavaScript on-demand images)
- Version 3: Responsive CSS3 Modal Gallery (with JavaScript on-demand images + keyboard controls)
- Version 4: Responsive CSS3 Modal Gallery (with JavaScript on-demand images, keyboard controls + pagination)
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.