Search

Thursday 30 June 2011

A D.I.Y. Lightbox


Introduction

OK, so JavaScript/jQuery lightboxes are ten-a-penny. Just go to the jQuery plugin repository and take your pick! Indeed for long time, I did just that. After all, there's no point re-inventing the wheel is there??

However, I recently had to write my own JavaScript lightbox from scratch. So I thought now was a good opportunity to share my experiences with you, as well as the code, which you are free to use should you want or need to develop your own lightbox or maybe want to develop my lightbox further.

LiteBox

The fruits of my labours I have (unimaginatively) named 'LiteBox', mainly to try and emphasise the fact that it makes no pretences to doing anything fancy! Also, as I am a shameless jQuery whore, I am also assuming prior experience of jQuery. If you are unfamiliar with jQuery, you can find more information here.

The Basics

LiteBox is implemented as a jQuery plugin. The signature is as follows:

jQuery.lightbox( url, [options] )

The url parameter is mandatory and specifies the URL of the image to be shown in the lightbox. The options parameter is a set of optional key/value pairs for configuring the lightbox:

KeyDescriptionDefault Value
titleThe text to be used for the tooltip and alt attribute of the image.The URL of the image.
showCloseButtonWhether or not to show the 'close' button on the lightbox.True.
closeButtonPositionThe position of the 'close' button.
Can be one of either 'top-left', 'top-right', 'bottom-left' or 'bottom-right'.
Ignored if showCloseButton is false.
'top-right'.
animationSpeedThe speed of the animation.
Can be one of either 'slow', 'medium' or 'fast'; or the length of the animation in milliseconds.
'medium' (≡ 500ms)

Getting into the Code

Before we get delve too deeply into the JavaScript, it's helpful to have a look at the CSS:

.jquery-litebox-lightbox 
{
 position: absolute;
 background-color: Transparent;
 z-index: 1001;
 margin: 0px;
 padding: 0px;
 border: 10px solid white;
}

.jquery-litebox-img
{
    margin: 0px;
    padding: 0px;
}

.jquery-litebox-close
{
    height: 25px;
    width: 25px;
    position: absolute;
    cursor: pointer;
    background-image: url(images/jquery-litebox_close.png);
    background-repeat: no-repeat;
    z-index: 1002;
}

.jquery-litebox-shadow 
{
 position: absolute;
 top: 0;
 left: 0;
 width: 100%;
 height: 100%;
 background-image: url(images/jquery-litebox_shadow.png);
 background-repeat: repeat;
}

As is customary with jQuery plugins, the first thing we need to do is merge any user options into a settings object:

var settings = {
    title: url,
    showCloseButton: true,
    closeButtonPosition: 'top-right',
    animationSpeed: 'medium'
};
$.extend(settings, options);

The next thing to do is create a <img> tag for the full-sized image, and add it to the DOM. It is important that we do this early on as we need the browser to have loaded the image in order to work with its properties, such as height and width etc. However, we make the image invisible so that it doesn't actually appear to the user yet:

var img = $('<img src="' + url + '" alt="' + settings.title + '" title="' + settings.title + '" class="jquery-litebox-img" style="display: none;" />');
$('body').append(img);

Once the browser has loaded them image, then the interesting stuff can start. By hooking into the image's load event we can create the lightbox and append the image to it:

var imgWidth = $(this).width();
var imgHeight = $(this).height();
$(this).detach().height('0px').width('0px');
var lightbox = $('<div class="jquery-litebox-lightbox"></div>').css('top', $(window).scrollTop() + 100 + 'px').css('left', ($(window).width() / 2) - (imgWidth / 2) + 'px');
var shadow = $('<div class="jquery-litebox-shadow"></div>');
$('body').append(shadow);
$('body').append(lightbox);
lightbox.append($(this));

As you can see, we firstly get the height and width of the image. We then detach the image from the DOM and set its height and width to 0px.

Next, we create the lightbox itself. Now that we know the height and width of the image, we can calculate and set the position of the top-left-hand corner of the lightbox such that it will be centred horizontally on the page and 100px from the top of the window.

We then create the lightbox shadow and append it to the DOM. We append the lightbox itself to the DOM and then append the image to the lightbox.

Now the time has come to animate our lightbox. The animation is very simple: we simply grow the image from top-left to bottom-right until the image is at its full size. This is achieved using jQuery's animate() function:

var animationTime = getAnimationTime(settings.animationSpeed);
$(this).show().animate({
    width: imgWidth,
    height: imgHeight
}, animationTime, function () {
    if (settings.showCloseButton) {
        showCloseButton(settings.closeButtonPosition);
        img.mouseover(function () {
            showCloseButton(settings.closeButtonPosition);
        });
        img.mouseout(function (e) {
            hideCloseButton(e);
        });
    }
});

When the animation is complete, we hook into the mouseover and mouseout events of the image to show and hide the close button respectively. The code for showing and hiding the button is as follows:

function showCloseButton(position) {
    var img = $("img.jquery-litebox-img");
    var imgPositionY = img.offset().top;
    var imgPositionX = img.offset().left;
    var imgHeight = img.height();
    var imgWidth = img.width();
    if ($("div.jquery-litebox-close").length == 0) {
        var close = $('<div class="jquery-litebox-close" title="Close the lightbox." style="display: none;"></div>');
        $('body').append(close);
        switch (position) {
            case 'top-left':
                close.css('top', imgPositionY).css('left', imgPositionX);
                break;
            case 'top-right':
                close.css('top', imgPositionY).css('left', (imgPositionX + imgWidth) - close.width());
                break;
            case 'bottom-left':
                close.css('top', (imgPositionY + imgHeight) - close.height()).css('left', imgPositionX);
                break;
            case 'bottom-right':
                close.css('top', (imgPositionY + imgHeight) - close.height()).css('left', (imgPositionX + imgWidth) - close.width());
                break;
            default:
                throw new Error("Buttom position must be one of either: 'top-left', 'top-right', 'bottom-left' or 'bottom-right'.");
        }
        close.click(function (e) {
            $(this).remove();
            closeLightBox();
        });
        close.show();
    }
}

function hideCloseButton(mouseEvent) {
    if (!isIn($("div.jquery-litebox-close"), mouseEvent))
        $("div.jquery-litebox-close").remove();
}

function isIn(obj, mouseEvent) {
    if (obj.length > 0) {
        var x = mouseEvent.pageX;
        var y = mouseEvent.pageY;
        var posX = obj.position().left;
        var posY = obj.position().top;
        var objX = obj.width();
        var objY = obj.height();
        return x > posX && x < posX + objX && y > posY && y < posY + objY;
    }
    else
        return false;
}

The animation time is determined by calling the getAnimationTime() function:

function getAnimationTime(speed) {
    if (typeof speed === 'string') {
        switch (speed) {
            case 'slow': return 1000;
            case 'medium': return 500;
            case 'fast': return 250;
            default:
                var parsedSpeed = parseInt(speed);
                if (!isNaN(parsedSpeed))
                    return parsedSpeed;
                else
                    throw new Error("Animation speed must be a number or one of: 'slow', 'medium' or 'fast'.");
        }
    }
    else if (typeof speed === 'number')
        return speed;
    else
        throw new Error("Animation speed must be a number or one of: 'slow', 'medium' or 'fast'.");
}

Summary

LiteBox is a very simple, lightweight jQuery lightbox, which can serve as an example for anyone wishing to develop their own solution; or as a base for anyone wishing to extend it further.

You can download the source code, along with a sample web page from here.

So can see a demo of LiteBox in action here.

2 comments: