Javascript, From thumbnail to fullsize image on the same page
Javascript keywords, functions and properties used :
function, var, new, return, while if, else, length, ++, parseInt(), addEventListener(), attachEvent(), onmouseover, onclick, setTimeout(), clearTimeout(), parentNode, this, Image.src, String.replace(), style, offsetWidth, offsetHeight, height, width, top, left, padding, paddingBottom, visibility, opacity, filter, createElement(), getElementsByTagName(), getElementById(), appendChild(), removeChild(), className, innerHTML, innerWidth, innerHeight, clientHeight, clientWidth, documentElement
A slightly different tack
While in past articles the code was display followed by a decomposition of the code, the approach used here will be to display the code with interspersed code comments. This method is used as some of the functions are rather long and involved making continually having to scroll up and down tedious. Explanations are to be found in-line with the code.
Please note that the same comments are present in the example ZIP file found at the end of this page.
In addition, the compressed file will include everything needed to display a demonstration page.
Getting ready to begin to commence to start doing something useful
To reduce as much as possible, the inclusion of Javascript code into the HTML code, addEventListerner()/attachEvent() are used to attach a handler to the window.onload event.
The onload event handler, init_images(), will then identify the area to be searched for target thumbnails and then within the identified area, of which there can only be one, the onload event handler will then cycle through all images attaching process initiating onmouseover event handlers to each identified target.
if (window.addEventListener) window.addEventListener('load',init_images,false); else if (window.attachEvent) window.attachEvent('onload',init_images);
Test whether window.addEventListener or window.attachEvent are defined and then use the one that is defined to attach the event handler.
Something useful
Called when the window completes loading, this function will attach the required event handler, init_expansion() to the required event, onmouseover.
function init_images () { /* Get a pointer to the area, id=opaqued that contains the images to be expanded. */ opaque_region = document.getElementById('opaqued'); var imgs = opaque_region.getElementsByTagName('img'); for (var x=0; x < imgs.length; x++) { // Check each link to see if it is a target, css class = "expando". if (imgs[x].className.indexOf('expando') != -1) { imgs[x].onmouseover = init_expansion; } } }
Initial parameters
There are a rather large number of global paramters used in this script. First the more important ones will be described, those that may be useful for application dependant adjustments.
Variables described after this are primarily placeholders used during processing and of little interest to the user.
/* The size, in pixels, of each step of the minor expansion process between in-page image size and "full" minor expansion size. */ var step = 6; /* The number of milliseconds between each minor and major expansion process iteration. */ var wait = 40; /* The number of iterations of the major expansion process between "full" minor expansion size and major expansion image full size. */ var steps=10; /* The size that in-page images will appear to expand to during the minor expansion process. */ var inter_size_width=150; // The full width the drop shadow will attain. var sm_shdwWidth=8; /* The level of greatest transparency the background will achieve during the major expansion process. */ var min_opacity=35; /* A text string in a given thumbnail image URL which identifies it as a thumbnail url. */ var thumbs_id='thmb'; /* A text string in a given full size image URL which identifies it as a full size image url. */ var source_id='full';
Note 1: Adjust the step, wait and steps parameters for the level of smoothness desired in the expansion processes. A shorter wait time, smaller step and larger steps values will generally cause the expansion to appear more smooth but at the cost of higher client side CPU usage.
Note 2: thumbs_id and source_id support programatically determining the full size image URL from the thumbnail image URL.
For example, if the thumbnails and full size images carry the same name but in different directories, set the thumbs_id parameter to the directory of the thumbnails and the source_id parameter to the directory containing the full size images, i.e. if the thumbnail image is http://some-domain.com/images/thumbnails/some_img.jpg and the full size image is http://some-domain.com/images/fullsize/some_img.jpg, you would set thumbs_id equal to "thumbnails" and source_id to "fullsize".
Alternately, if the thumbnails are in the same directory as the full size images and their repsective URLs differ only in some identifier embedded into the image name for example http://some-domain.com/images/some_img-thumb.jpg for the thumbnail and http://some-domain.com/images/some_img-full.jpg for the full size image, you would set thumbs_id equal to "thumb" and source_id equal to "full"
Variables primarily used to maintain state information.
// A pointer to the in-page thumbnail var thm_image=null; // Size, position and center of in-page thumbnail var thmWidth, thmHeight, thmLeft, thmTop; var thmCntrX, thmCntrY; // Height and width of full size image var full_height, full_width; /* Amount added or subtracted from current values during each major expansion iteration. */ var stepX, stepY, stepWidth, stepHeight; // The current values at any give point. var crntX, crntY, crntWidth, crntHeight; // Timer used to control expansion and collapse var expando_timer; /* The amount the drop shadow padding is adjusted on each minor expansion and its current value. */ var sm_shdwStep, sm_shdwCurrent; /* The amount the background opacity is adjusted on each major expansion and its current value. */ var opacity, opacity_step; /* Set during minor and major expansion and unset after complete collapse. */ var bShowing=false; /* Container div used to contain the minor expansion image as well as the padding and drop shadow elements and a pointer to where the minor expansion image is inserted into the minor expansion container. */ var thm_container= null; var thm_insertion=null; var small_expand=null; // Similar elements and pointers as for the minor expansion var full_container=null; var full_insertion=null; var full_expand=null; // Area whose opacity is effected during the major expansion var opaque_region=null; /* Used to properly locate elements with respect to the in-page thumbnail. */ var scrollParent=null; // Object used for pre-loading the major expansion image var pre_loader=null;
Starting out
The following code is executed when a mouseover event fires from an in-page thumbnail image.
function init_expansion () { // If we are already in the expansion process, no need to start it again. if (bShowing) return; /* Reality check, kill the timer so that multiple timer events don't fire while we are processing this one. */ if (expando_timer) clearTimeout(expando_timer); // If a minor expansion element already exists, delete it from the page. if (small_expand != null) { thm_container.parentNode.removeChild(thm_container); } /* Set the region that will be made semi-transparent during the major expansion process */ scrollParent = opaque_region.parentNode; /* The owner of this function call is the in-page thumbnail image. We will collect various data to be used to locate the minor and major expansion elements. */ thm_image = this; /* Kill the in-page thumbnail image onmouseover event so that it doesn't fire while the minor and major elements exist. */ thm_image.onmouseover = null; /* Collect height, width, top and left position information from the in-page thumbnail image. */ thmWidth = thm_image.offsetWidth; thmHeight = thm_image.offsetHeight; thmLeft = getOffsetLeft(thm_image); thmTop = getOffsetTop(thm_image) - scrollParent.scrollTop; // Identify the center of the in-page thumbnail image. strtCntrY = thmTop+thmHeight/2; strtCntrX = thmLeft+thmWidth/2; /* Create and populate the container that will contain the minor expansion image. */ small_expand = document.createElement('img'); small_expand.style.visibility = 'hidden'; small_expand.src = thm_image.src; small_expand.className = 'grow_img'; small_expand.title = 'Click to Expand'; thm_container = document.createElement('div'); // Locate the "body" element and add the container to it. document.getElementsByTagName('body')[0].appendChild(thm_container); thm_container.className = 'center2'; thm_container.id = 'thm_container'; // Setup the container with the drop shadow divs. thm_container.innerHTML = '<div class="fs_t">' + '<div class="fs_r">' + '<div class="fs_b">' + '<div class="fs_l">' + '<div class="fs_tr">' + '<div class="fs_br">' + '<div class="fs_bl">' + '<div class="fs_tl" id="thm_insertion">' + '</div>' + '</div>' + '</div>' + '</div>' + '</div>' + '</div>' + '</div>' + '</div>'; /* Locate the container image insertion point and insert the image. */ insertion_point = document.getElementById('thm_insertion'); insertion_point.appendChild(small_expand); /* Initialize parameters used to keep track of expansion size and set the initial size and position of the expansion image. */ crntWidth = thmWidth; crntHeight = thmHeight; small_expand.style.width = crntWidth+'px'; small_expand.style.height = crntHeight+'px'; thm_container.style.top = thmTop-2+'px'; thm_container.style.left = thmLeft-2+'px'; // Set the event handler for mouseout. small_expand.onmouseout = init_thumb_collapse; // Make it visible. small_expand.style.visibility = 'visible'; /* Initialize the current drop shadow step size and its current value to 0. */ sm_shdwStep = (sm_shdwWidth/(140 - thmWidth))*step/2; sm_shdwCurrent = 0; // Begin the iterative expansion process. expand_thumb(); }
The minor expansion process
The following function is called repeatedly until the thumbnail sized image has expanded to its intermediate size prior to expanding to full size if clicked on.
function expand_thumb () { // Another timer reality check if (expando_timer) clearTimeout(expando_timer); /* Increment the height and width variables and update expanding image's top and left position as well as adjusting the expanding image to the center of the in-page thumbnail. */ crntHeight += step; crntWidth += step; small_expand.style.height = crntHeight+'px'; small_expand.style.width = crntWidth+'px'; thm_container.style.top = parseInt(strtCntrY-crntHeight/2)+'px'; thm_container.style.left = parseInt(strtCntrX-crntWidth/2)+'px'; /* Increment the shadow width counter variable and update the left and bottom padding values giving the appearance that the shadow actually grows. */ sm_shdwCurrent += sm_shdwStep; insertion_point.style.padding = parseInt(sm_shdwCurrent)+'px'; insertion_point.style.paddingBottom = parseInt(sm_shdwCurrent/2)+'px'; /* Check to see if the expanding image has achieved the desired size, as set by "inter_size_width". If not, set the timer and do it again. */ if (crntWidth < inter_size_width) { expando_timer = setTimeout(expand_thumb,wait); } else { /* If the expanding image has reached the desired size, set the inter_size_height to the current height as the resulting height may be plus or minus a few pixel and we need to know exactly where we started so we can know where to end up on collapse. */ inter_size_height = crntHeight; /* Set the expanded image's onclick so that if the user clicks on the expanded image, it will then begin the process to fully expand. This is set at this time so that it can not be fired earlier when the image may be in the process of completing the initial minor expansion. */ small_expand.onclick = init_expand_full; } }
Collapsing the minor expansion
As has been mentioned, the process of collapsing the minor expansion image is essentially the reverse of the minor expansion process itself.
function collapse_thumb () { // And yet another timer reality check. if (expando_timer) clearTimeout(expando_timer); /* Basically do the opposite of what was done before, add where we subtracted and subtract where we added and put your right foot in, your right foot,,,, oh, sorry, back to work. */ crntWidth -= step; crntHeight -= step; small_expand.style.width = parseInt(crntWidth)+'px'; small_expand.style.height = parseInt(crntHeight)+'px'; thm_container.style.top = parseInt(strtCntrY-crntHeight/2)+'px'; thm_container.style.left = parseInt(strtCntrX-crntWidth/2)+'px'; sm_shdwCurrent -= sm_shdwStep; insertion_point.style.padding = parseInt(sm_shdwCurrent)+'px'; insertion_point.style.paddingBottom = parseInt(sm_shdwCurrent/2)+'px'; if (crntWidth > thmWidth) expando_timer = setTimeout(collapse_thumb, wait); else { /* There is a race condition that exists that was difficult to find the source of without this timer reality check. */ if (expando_timer) clearTimeout(expando_timer); /* Reset the mouseover event for the in-page thumbnail so that it can be expanded again. */ thm_image.onmouseover = init_expansion; // Remove the expansion image and its container from the page. thm_container.parentNode.removeChild(thm_container); small_expand = null; } }
Setting up the expansion to full size
Using the size and position of the expanded thumbnail, we initialize an image, image container and various parameters needed to perform the expansion to full size.
function prepare_expand_full () { /* At this point, we know the image is loaded so we create and populate a container for it just like we did for the minor expansion. */ full_expand = document.createElement('img'); full_expand.style.visibility = 'hidden'; full_expand.src = pre_loader.src; full_expand.title = 'Click to Reduce'; // Begin same old - same old full_container = document.createElement('div'); document.getElementsByTagName('body')[0].appendChild(full_container); full_container.className='center2'; full_container.id='full_container'; full_container.innerHTML = '<div class="fs_t">' + '<div class="fs_r">' + '<div class="fs_b">' + '<div class="fs_l">' + '<div class="fs_tr">' + '<div class="fs_br">' + '<div class="fs_bl">' + '<div class="fs_tl" id="full_insertion">' + '</div>' + '</div>' + '</div>' + '</div>' + '</div>' + '</div>' + '</div>' + '</div>'; full_insertion = document.getElementById('full_insertion'); full_insertion.appendChild(full_expand); full_insertion.style.padding = sm_shdwWidth+'px'; full_insertion.style.paddingBottom = sm_shdwWidth/2+'px'; full_height = full_expand.offsetHeight; full_width = full_expand.offsetWidth; full_expand.style.width = crntWidth+'px'; full_expand.style.height = crntHeight+'px'; full_expand.className='grow_img'; if (self.innerHeight) { // all except Explorer var availWidth = self.innerWidth; var availHeight = self.innerHeight; } else if (document.documentElement && document.documentElement.clientHeight) { // Explorer 6 Strict Mode var availWidth = document.documentElement.clientWidth; var availHeight = document.documentElement.clientHeight; } else if (document.body) { // other Explorers var availWidth = document.body.clientWidth; var availHeight = document.body.clientHeight; } // End same old -same old /* Here is where we diverge greatly from the minor expansion process. Besides having to check for available area, which we didn't do before, we also have to find not the center of the in-page thumbnail but instead, the center of the browser viewport. */ if ( (full_height+sm_shdwCurrent*2) > availHeight) { full_width = full_width * (availHeight) / (full_height+sm_shdwCurrent*2); full_height = availHeight-(sm_shdwCurrent*4); } if ( (full_width+sm_shdwCurrent*2) > availWidth) { full_height = full_height * (availWidth) / (full_width+sm_shdwCurrent*2); full_width = availWidth-(sm_shdwCurrent*4); } if (self.pageYOffset) { // all except Explorer var scrollX = self.pageXOffset; var scrollY = self.pageYOffset; } else if (document.documentElement && document.documentElement.scrollTop) { // Explorer 6 Strict var scrollX = document.documentElement.scrollLeft; var scrollY = document.documentElement.scrollTop; } else if (document.body) { // all other Explorers var scrollX = document.body.scrollLeft; var scrollY = document.body.scrollTop; } /* Now we go back to much the same process as for the minor expansion. */ var small_container = document.getElementById('thm_container'); full_container.style.top = getOffsetTop(small_container)+'px'; full_container.style.left = getOffsetLeft(small_container)+'px'; full_expand.style.visibility = 'visible'; thm_container.style.visibility='hidden'; small_expand.style.visibility = 'hidden'; startX = crntX = getOffsetLeft(full_container); startY = crntY = getOffsetTop(full_container); stepY = (crntY - ((scrollY + availHeight/2 - sm_shdwWidth) - full_height/2))/steps; stepX = (crntX - ((scrollX + availWidth/2 - sm_shdwWidth) - full_width/2))/steps; stepHeight = (full_height - small_expand.offsetHeight)/steps; stepWidth = (full_width - small_expand.offsetWidth)/steps; opacity = 100; opacity_step = (100-min_opacity)/steps; full_expand.onclick = collapse_full; expand_full(); }
Executing the expansion to full size
Now that everything is set up, we repeatedly call the expand_full() function until the image has reached it's full size.
function expand_full () { /* Again, basically the same as for the minor expansion except for two things, 1. we don't need to adjust the width of the drop shadow because it doesn't expand during this phase and 2. we instead need to adjust the opacity of the background. */ crntY -= stepY; crntX -= stepX; crntHeight += stepHeight; crntWidth += stepWidth; opacity -= opacity_step; /* Some browsers understand "style.opacity" and some "style.filter". */ opaque_region.style.opacity = opacity/100; opaque_region.style.filter = 'alpha(opacity='+opacity+')'; full_container.style.left = parseInt(crntX)+'px'; full_container.style.top = parseInt(crntY)+'px'; full_expand.style.width = parseInt(crntWidth)+'px'; full_expand.style.height = parseInt(crntHeight)+'px'; // Again, if we haven't reached full size, spin the bottle again. if (crntHeight < full_height) expando_timer = setTimeout(expand_full, wait); }
Collapsing the full size image
Similar to the thumbnail collapse and the opposite of the full size expansion, we now collapse the full size image until it has reached the size it started out as and then begin the collapse of the expanded thumbnail completing the collapse down to the in-page thumbnail.
function collapse_full () { /* Same as minor expansion collapse, add where previously one subtracted and vice versa. The opacity of the background must also be ramped back up to normal. */ small_expand.style.visibility = 'visible'; thm_container.style.visibility = 'visible'; full_expand.onclick = null; crntY += stepY; crntX += stepX; crntHeight -= stepHeight; crntWidth -= stepWidth; full_container.style.top = parseInt(crntY)+'px'; full_container.style.left = parseInt(crntX)+'px'; full_expand.style.height=parseInt(crntHeight)+'px'; full_expand.style.width = parseInt(crntWidth)+'px'; opacity += opacity_step; opaque_region.style.opacity = opacity/100; opaque_region.style.filter = 'alpha(opacity='+opacity+')'; if (crntHeight + sm_shdwCurrent > inter_size_height) expando_timer = setTimeout(collapse_full, wait); else { small_expand.style.visibility = 'visible'; thm_container.style.visibility = 'visible'; // Guess what, another timer reality check, bet you weren't expecting that! if (expando_timer) clearTimeout(expando_timer); bShowing = false; opaque_region.style.opacity = 1; opaque_region.style.filter = 'alpha(opacity=100)'; full_container.parentNode.removeChild(full_container); full_expand = null; collapse_thumb(); } }
Download the demo files
Download the demo files Here








