Image previews with DOM JavaScript (and PHP if wanted)

Get and talk about it

What it does:

If your browser supports the W3C DOM, you can see small camera icons next to each of the links in the following list. Activate these either via mouse or keyboard to see a preview of the linked image:

Activating either the camera icon link once more or the thumbnail will make it vanish.

Changes:

The task

Sometimes we want to offer a visitor a preview of what is lurking behind a link, especially when it is a huge picture and there might not be a need to load it. This is where thumbnails come in handy, and it is pretty easy to use CSS to show and hide a thumbnail in modern browsers:

Mouse over the following link to see the thumbnail: Black leather sofa d

HTML:
<p id="cssexample">
  <a href="leathersofa.jpg">Black leather sofa
  <img src="tn_leathersofa.jpg" alt="d" /></a>
</p>
CSS:
#cssexample a {
  position:relative;
}
#cssexample a img{
  display:block;
  position:absolute;
  top:-999px;
  left:0;
}
#cssexample a:hover img {
  top:-50px;
  left:0;
}		

However, this has several drawbacks:

Going further with the help of JavaScript

Using JavaScript, we can do a lot more:

Using this functionality

All that is needed to achieve the preview effect is embedding the script picpreview.js in the HEAD section of the page and adding a class called "preview" to the link:

<head>
  <script type="text/javascript" src="picpreview.js"></script>
</head>
[...]
<ul>
  <li><a href="leathersofa.jpg" class="preview">Leather sofa</a></li>
  [...]
</ul>

The script adds some markup to this link, and the LI will look like this before the preview link was clicked:

<li>
  <a href="leathersofa.jpg" class="preview">Leather sofa</a>
  <a href="#" class="previewlink">
  <img class="previewimage" alt="see a preview of this image" src="camera.gif">
  </a>
</li>

And when the preview link was activated:

<li>
  <a href="leathersofa.jpg" class="preview">Leather sofa</a>
  <a href="#" class="previewlink">
  <img class="previewimage" alt="see a preview of this image" src="camera.gif">
  <img src="thumb.php?i=leathersofa.jpg&amp;r=599"></a>
</li>

To make the effect work, we need the appropriate CSS:

.previewlink{
  position:relative;
  padding-left:5px;
}
.previewlink img{
  position:absolute;
  top:0;
  left:2em;
  z-Index:10;
  border:none;
}
.previewlink img.previewimage{
  left:0;
  border:none;
  position:relative;
}

Altering the script

Out of the box, the script relies on a small PHP script to generate the thumbnail on the fly. The article "How to create thumbnails with PHP and gd" explains in detail how that works. If you cannot use PHP and the GD library on your server, then you need to create the thumbnails with another program and name them accordingly. The script has various variables you can set to change - among other things - this behaviour:

// is PHP available? true of false
var hasphp=true;
// options for the url depending on the PHP availability
var hasphpurl='thumb.php?i=';
var thumb='tn_';
// class to identify links to be enhanced
var previewclass='preview';
// link added to the newly created preview links
var previewlinkclass='previewlink';
// class added to the preview image
var previewimageclass='previewimage';
// image to show next to each preview link + alternative
var previewimage='camera.gif';
var previewalternative='see a preview of this image';
// image to show while the thumb is loaded + alternative
var loadingimage='loading.gif';
var loadingalternative='Loading...';

If you cannot use PHP, then you need to set the hasphp variable to "false". The script will expect a thumbnail with the filename of the image preceeded by a "tn_", as defined in the thumb variable. If the image for example is "leathersofa.jpg", the thumbnail needs to be "tn_leathersofa.jpg", unless you change the thumb variable.

The other variables define the classes used and the preview and loading images and should be rather self-explanatory.

The anatomy of the script

Following is a step by step explanation how the script works (there is no need to copy and paste the parts, as you can download the example as one zip):

The first thing we should do is call the script that we want to use when the page has finished loading. We also check if the browser supports DOM, and simply don't do anything but "return" when this is not the case.


window.onload=function()
{
  if(!document.getElementById || !document.createTextNode){return;}
  picpreview()
}

Then we can start our function, and offer variables to change the output without the need to comb through the code.


function picpreview()
{
  // is PHP available? true of false
  var hasphp=true;
  // options for the url depending on the PHP availability
  var hasphpurl='thumb.php?i=';
  var thumb='tn_';
  // class to identify links to be enhanced
  var previewclass='preview';
  // link added to the newly created preview links
  var previewlinkclass='previewlink';
  // class added to the preview image
  var previewimageclass='previewimage';
  // image to show next to each preview link + alternative
  var previewimage='camera.gif';
  var previewalternative='see a preview of this image';
  // image to show while the thumb is loaded + alternative
  var loadingimage='loading.gif';
  var loadingalternative='Loading...';

We then tell the script to loop through all links of the document, and check which one has the preview class as one of its class values. We don't just compare the class name with the preview class, as elements can have more than one class. Each of these links get added to an array called "prevlinks". This step is needed, as we will later add new links for the previews, and each time we add one we change the original links array. By assembling this "cache" array, we don't end up in an endless loop


  var links=document.getElementsByTagName('a');
  var prevlinks=new Array();
  var c=0;
  var previewTest = new RegExp("(^|\\s)" + previewclass + "(\\s|$)");
  // Loop over all links and filter out links without the necessary class
  for(var i=0;i<links.length;i++)
  {
    if(!previewTest.test(links[i].className)){continue;}
    prevlinks[c]=links[i];			
    c++;
  }

We then loop through all of these new links


  for(var i=0;i<prevlinks.length;i++)
  {

We create the link and the image for the preview link and set their values.


    var newa=document.createElement('a');
    newa.className=previewlinkclass;
    var newimg=document.createElement('img');
    newimg.src=previewimage;
    newimg.alt=previewalternative;
    newimg.className=previewimageclass;
    newa.appendChild(newimg);
    newa.href="#";

When the link is clicked, we add a new function.


    newa.onclick=function()
    {

If there is already an second image in the link, we remove that one - this prevents us from having more than one image should the visitor activate the preview link twice..


      if(this.getElementsByTagName('img')[1])
      {
        this.removeChild(this.getElementsByTagName('img')[1]);
      } else {

Otherwise we create a new image and set the loading image values.


        var newimg=document.createElement('img');
        newimg.src=loadingimage;
        newimg.alt=loadingalternative;
        this.appendChild(newimg);

We then create the real image and add a random value to the URL, to prevent caching. The URL is read from the original link's href attribute


        var newimg=document.createElement('img');
        this.appendChild(newimg);
        var rand=parseInt(777*Math.random(0,1));
        newimg.src=hasphp?
        geturl(this.previousSibling.href)"&r="+rand:geturl(this.previousSibling.href);

When the new image is loaded, we remove the loading image.


        newimg.onload=function()
        {
          this.parentNode.removeChild(this.parentNode.getElementsByTagName('img')[1]);
        }
      }

As the newly created link does not point anywhere, we don't return to the HTML to follow it.


      return false;
    }

We add the newly created link after the real link inside the LI (the parent node of the link and before its next sibling).


    prevlinks[i].parentNode.insertBefore(newa,prevlinks[i].nextSibling);
  }

This tool method gets the file name from the link and adds the appropriate start to either show or create the thumbnail image.


  function geturl(url)
  {
    url=url.split('/');
    url=hasphp?hasphpurl+url[url.length-1]:thumb+url[url.length-1];
    return url;
  }
}  

That is all. You can Download the example as one zip and play around with it.

Comment on the blog