Home    posts    lpg imagelightbox

Posted on: February 9, 2018

How to implement Image lightbox on LPG?

Today we are going to look into how to implement a jQuery ImageLightBox plugin into Light-weight PHP gallery which was disscused in the previous tutorial. Firstly, we will look into the basics of how to configure the plugin to work on the gallery as well as some of the basic plugin's features, such as overlay and close button. While this should be sufficient for the lightbox to work, we will also re-design the gallery frontend and synchronize it with jQuery for easy configuration. It will add another dimension and a new outlook to the gallery.

PART 1 - Plugin configuration

Before we go into the details, it is appropriate to give credits to Osvaldas Valutis, the author of the plugin. I could have chosen any of the other plugins but this one was the most light-weight among others that I have looked into. Also easy for me to configure since I'm just beginning to use more of Javascript and jQuery. So to get started, first you'll need to download the plugin at the author's website. Make sure to download the minified version because for some reason the full version doesn't work for dynamically added items that we'll look into later.

After you've done that, also download the jQuery 3.0.0 version and save both files to newly created js/ folder in the LPG's main folder. Then open up the gallery.php file and append the following between <head> and </head> tags:

<script type="text/javascript" src="js/jquery-3.0.0.min.js"></script>
<script type="text/javascript" src="js/imglightbox.min.js"></script>

The link to CSS file should be left there as is. Now if you go to the author's demo site and view the source code, you'll see that all that you need to know is there, the CSS and jQuery code but nothing is explained in words. Not that we'll go through all of the features but I'll try to explain the ones that will be used for the LPG gallery.

The features used will be overlay, close button, arrows and dynamically added items of which the code for the last two will be modified to suit our needs. In this part, however, only the first two will be explained and if we start by appending more code to the head section of gallery.php file.

<script type="text/javascript">
// Start the main function
$(function() {
	// OverlayOn to append the div to body at the start of the script.
	// OverlayOff to remove the id when script is closed.
	var overlayOn = function() {$('<div id="imagelightbox-overlay"></div>').appendTo('body');};
	var overlayOff = function() {$('#imagelightbox-overlay').remove();};

	// Append close button to body and when/if clicked, remove the button and close the script. Return false to prevent this as default
	var closeButtonOn = function(instance) {$('<button type="button" id="imagelightbox-close" title="Close"></button>').appendTo('body').on('click touchend', function() {

			return false; 

	// Remove the button if the script is closed by any other means than clicking the button
	var closeButtonOff = function() {$('#imagelightbox-close').remove();};

Perhaps you've noticed that the closeButtonOn function is expecting an instance. So now we have to create it for the plugin to run.

	// Instance of the imageLightbox that will apply to all hyperlinks with an attribute light="box".
	// Now the functions created above are passed to the plugin onStart where overlay and close button are initialized and onEnd they go off
	var instance1 = $('a[light="box"]').imageLightbox( {

		onStart:	function() {overlayOn(); closeButtonOn(instance1);},
		onEnd:		function() {overlayOff(); closeButtonOff();}

Next thing to do is to append the hyperlink attribute to the images generated by the PHP.

// Add the light="box" attribute to hyperlinks. Change lines 26 and 28 to

echo '<a light="box" href="'.$image.'"><img class="images" src="'.$thumb.'"/></a><br/>'."\n";

echo '<a light="box" href="'.$image.'"><img class="images" src="'.$thumb.'"/></a>'."\n";

And finally, in order to make it all work, we need to define the CSS for all the classes that will be appended to body when the script is started, as well as the default imageLightbox id. So open up gallery.css file and add the following:

/* The default id with fixed position */
/* White borders added to the image */
#imagelightbox {
	position: fixed;
	border:10px solid #FFF;
	z-index: 100;
	-ms-touch-action: none;
	touch-action: none;

/* Fullscreen fixed black overlay with halfed opacity */
#imagelightbox-overlay {
	background-color: #000;
	opacity: 0.5;
	position: fixed;
	z-index: 100;
	top: 0; right: 0; bottom: 0; left: 0;

/* Again a fixed position for the close button(image to be added) */
#imagelightbox-close {
	position: fixed;
	background: url(exit.png);
	width: 60px;
	height: 60px;
	z-index: 100;
	top: 20px; right: 40px;

That's it. If you've included the exit.png image to the same folder where CSS is, then everything should work as expected, which concludes this part.

PART 2 - LPG frontend re-design and plugin customization

In this part, we'll include arrows and the image numbers on arrow click to the plugin. We'll also disable scrolling by clicking on image and prevent re-cycling through 0 index which means that by reaching end of the gallery or trying to go from first image backwards will hide the arrow rather than cycle through images again. The other thing that we'll do is re-design the display which will no longer offer pagination but, instead, will show just one set of images and load another set when the button, load more, is clicked, all until it loads all sets generated by PHP. It will give a nice frontend outlook to the gallery.

The first thing we'll do is to define the arrows on and arrows off variables and the CSS for it. Note that this code is to be appended before instance is created.

The function expects instance and selector. It will then append left and right button to body script start
var arrowsOn = function(instance, selector) {$('<button type="button" class="imagelightbox-arrow imagelightbox-arrow-left"></button><button type="button" class="imagelightbox-arrow imagelightbox-arrow-right"></button>').appendTo('body').on('click touchend', function() {

		// Another function dictates what happens on click of the buttons that will be appended
		// First get the index of the image based on selector
		// Also get the length of all images with the provided selector
		var index = $(selector + '[href="' + $('#imagelightbox').attr('src') + '"]').index(selector);
		var len =  $(selector).length;

		// If the current image has left arrow available, start decrementing the index if clicked
		// Hide numbers class each time the button is clicked because a new number(of previous indexed image) will be appended
		// Prevent (negative)index from re-cycling, the last stop is 0 and if 0, hide left arrow.
		if($(this).hasClass('imagelightbox-arrow-left')) {
			if(!$(selector).eq(index).length) {index = 0;}
			if((index+1) == 0) {$('.imagelightbox-arrow-left').hide();return false;}
		else {

			// If the current image has right arrow available, start incrementing the index if clicked
			// Then repeat the process but for the right-arrow this time.
			if(!$(selector).eq(index).length) {index = 0;}
			if((index+1) == 1) {$('.imagelightbox-arrow-right').hide();return false;}

		// Switch to the next or previous image, it gets the index based on the above conditions
		// Then append the numbers, index (+ 1 because it starts with 0) and len(total images)
		// Return false to prevent default
		$(this).append('<span class="nmbr">Image '+(index+1)+' of '+len+'</span>');
		return false;
	// Function to remove arrows
	var arrowsOff = function() {$('.imagelightbox-arrow').remove();};

This now forces us to update the instance, to add both functions and define the selector separately before the instance since arrows function is expecting it for indexing and counting total. We will also turn quitOnImgClick to true so the scrolling will now entirely depend on arrows.

	// Note that instance also accepts selector, it's just defined separately
	var selector1 = 'a[light="box"]';
	var instance1 = $(selector1).imageLightbox( {

		onStart:	function() {overlayOn(); closeButtonOn(instance1); arrowsOn(instance1, selector1);},
		onEnd:		function() {overlayOff(); closeButtonOff(); arrowsOff();},
		quitOnImgClick: true

There are 3 classes that need the CSS design to make it work.

.imagelightbox-arrow-left { 
	left: 40px; 
	background:url(leftarrow.png) no-repeat center;
	background-position: middle center;
	background-color: #05092c;
	width: 60px;
	height: 120px;
	position: fixed;
	z-index: 101;
	top: 45%;

.imagelightbox-arrow-right { 
	right: 40px; 
	background:url(rightarrow.png) no-repeat center;
	background-position: middle center;
	background-color: #05092c;
	width: 60px;
	height: 120px;
	position: fixed;
	z-index: 101;
	top: 45%;

.nmbr {
	padding:10px 0px 0px 0px;
	top:20px; left:0; right:0;
	border:1px solid #fff;
	box-shadow: 1px 1px #ccc;

Yeah, arrows are images that are expected as input otherwise it will just be a 60x120 px rectangle that you'll be able to click on. Still better than nothing I suppose! Anyway, this is where we could end the tutorial because this would work as is but in such case pagination needs to stay in the code. Also, in this case, the max number of the images would always be equal to images per page. So instead, we are going to abandon pagination and add load more button. It will, when clicked, change the hyperlinks light attribute from "new" to "box" which will add new images to the total count length in arrowsOn function since light="box" is the chosen selector. We will also tell PHP exactly how it should generate the code because upon each click on load more button, next hidden div will be, beside attribute change, appended to the main div that is visible at all times. This is how it will load a new set of images to the site.

More will be explained as we progress creating the code. We'll start by re-designing the PHP. So at the top of the gallery.php file, which you should now be in, add the following:

<?php $perload = 16; ?>

This will be equal to per page, only difference is that it is now per load including the initial load. It will be used by both PHP and jQuery to know how to behave and how the whole frontend of the gallery will function. So between the body tags you can delete all the code because everything we've done with jQuery should be in between the head tags. We will add the following between body tags:


// Include functions, path to images and thumbs is still needed.
// Unpack the variables needed and sort the $images by filectime
list($path, $images, $thumbpath) = location();
usort($images, "byctime");

// Beside the unpacked variables also include the $perload variable
function display($images, $path, $thumbpath, $perload) {

	$count = 0;

	// Add a container and mainbox as static elements
	echo '<div id="container">'."\n";
	echo '<ul class="mainbox">'."\n";

	// loop through images, create the path to thumb from image name and thumb path and start counting
	foreach ($images as $image) {

		$imgprefix = strlen($path);
		$imgname = substr($image, $imgprefix);
		$thumb = $thumbpath.$imgname;

		// Treat the first element in the loop separately, just add a single li with light="box" attribute
		if(($count-1) === 0) {echo '<li class="imgs"><a href="'.$image.'" light="box"><img class="images" src="'.$thumb.'"/></a></li>';}

		// If counter, after being substracted by $perload, equals 0, which can happen only once, close the main ul, add the load more button and close the visible div
		// Also start by creating a new dynamic ul that will close itself by the next condition
		// The hyperlink attribute here is light="new" since this signifies the first next load
		elseif((($count - 1) - $perload) === 0) {echo "\n".'</ul>'."\n".'<button class="add btn">Load more</button>'."\n".'</div>'."\n".'<ul class="box'.(($count-1) / $perload).' is-hidden">'."\n".'<li class="imgs"><a href="'.$image.'" light="new"><img class="images" src="'.$thumb.'"/></a></li>';}

		// Close the ul if the counter can perfectly divide itself with $perload which results in remainder being 0 (modulo operator), of course only if not captured by previous condition 
		elseif((($count-1) % $perload) === 0) {
			// Inside we only need to know whether to load the hyperlinks with light="box" or light="new" attribute.
			// So if count is lower than $perload, load them with box, else with new.
			if(($count-1) < $perload) {echo "\n".'</ul>'."\n".'<ul class="box'.(($count-1) / $perload).' is-hidden">'."\n".'<li class="imgs"><a href="'.$image.'" light="box"><img class="images" src="'.$thumb.'"/></a></li>';}

			else {echo "\n".'</ul>'."\n".'<ul class="box'.(($count-1) / $perload).' is-hidden">'."\n".'<li class="imgs"><a href="'.$image.'" light="new"><img class="images" src="'.$thumb.'"/></a></li>';}


		// Else display only li elements, but again differentiate between hyperlink attributes
		else {

			if(($count-1) < $perload) {echo '<li class="imgs"><a href="'.$image.'" light="box"><img class="images" src="'.$thumb.'"/></a></li>';}

			else {echo '<li class="imgs"><a href="'.$image.'" light="new"><img class="images" src="'.$thumb.'"/></a></li>';}
	// Close the final ul since the final count can never modulo itself because of ($count-1)
	echo "\n".'</ul>'."\n";
echo display($images, $path, $thumbpath, $perload);

To elaborate a bit more, the load more button stays there because each of the other hidden boxes will be appended to the main ul box and closing of this main ul holds the button in place. But why did we need to the PHP to generate such code? Well, hopefully, everything will become clear after we add the jQuery in there. The following code should come just after the instance was created in the head section.

	// Set the clicks to 0
	// Define what happens when button load more is clicked, catch it on the button class
	var click = 0;
	$('.add ').on('click', function(e) {

		// This time prevent default with a method
		// Get the total of all li elements with imgs class
		// Get the perload from PHP variable with json_encode
		// Calculate expected clicks, how many times you'll need to click to load all images
		// Start counting clicks
		var len =  $('li.imgs').length;
		var perload =  <?php echo json_encode($perload); ?>;
		var expectedclicks = Math.ceil((len - perload) / perload);

		// If click is lower or equal to expected clicks, create items with with class box + the number of the click
		// To synchronize it with PHP generated ul boxes
		if(click <= expectedclicks) {var items = $('.box' + click);}

		// else remove the button
		else {$(this).remove(); return false;}

		// If the items(ul boxes) variable was created, find the items inside by the attribute and change the attribute from "new" to "box"
		// Add these new items with replaced attributes to lightbox
		// Also append them to the main ul box and remove the old items
		var replaced = $(items).find('a[light="new"]').attr('light', 'box');

So again, basically PHP generates images closed inside ul boxes based on $perload and each next box has a different class so that jQuery can capture it with each next click. The images inside each of the boxes are then appended to main ul box with a click, going from hidden to visible. For example, the images from box1 are appended on first click, images from box2 on second and so on. And when there is nothing to capture anymore, the button is removed. Just basic logic. Going through all this trouble just for one reason, for you to be able to control the gallery frontend by just changing one variable at line 1, and of course by modfiying CSS which reminds me that we still need to style a few classes:

#container {
	width: 640px; 
	text-align: center;
	padding: 5px; 
	margin: 0px auto; 

#container li {
	display: inline-block;
	margin: 6px; 

.is-hidden{display: none;}

.btn {
	text-transform: uppercase;
	color: #efefef;
	background-color: #520c0c;
	padding: 5px 10px 5px 10px;
.btn:hover {background-color: #000;}

That's it, alternatively you can download the modified LPG script here. Note that the Image lightbox library has to be downloaded from the author's site and put inside js/ folder. To view the demo of the modified gallery frontend, click here.


Be the first to comment.

Add a comment:

I have read and agree with the Privacy terms and conditions.