I'm working on a jQuery plugin for a client. Instead of finding one that was already out there I wanted to practice writing plugins since I hadn't in awhile. This plugin grabs all the images in a UL, creates a overlay for the page, and puts them in a image zoomer that gets opened when a user clicks on a thumbnail image that is also generated by the plugin.
You can see that I have some issues deciding where to put functions, some are inside the plugin function, others are inside the IIFE but outside of the plugin. I'd also like to know if there are any best practices or optimizations that I can put into this code. I'm aware of a few small bugs with the animation as well.
I tried to put a working copy of this on jsfiddle or jsbin but the popup doesn't render properly. http://jsfiddle.net/bittersweetryan/sEXMY/ There is a working copy on the github repo though, just download the zip and open index.html from here https://github.com/bittersweetryan/YADI
(function( ,ドル undefined ){
$.fn.imagezoom = function(base, options){
var $this = $(this),
defaults = {
width : 700,
height : 500,
previewHeight: 50,
previewDivWidth: 200,
thumbsWidth: 0,
imagePath: 'images'
},
$stageDiv = createDiv(),
$pictureFrame = null,
$overlay = getOverlay(),
$preview = null,
arrowsLoaded = false,
$thumbs = null;
if(typeof options === 'object'){
options = $.extend({},defaults,options);
}
else{
options = defaults;
}
//cache variables that point to the zoom elements
$pictureFrame = getPictureFrame(options);
$preview = getPreview(options);
$thumbs = getThumbs();
$("body").append($stageDiv);
//if no images were found in the li passed in return
if($this.find('li>img').length === 0){
$.error("No images were found under the root element.");
}
//remove the ul that has the images from the DOM
$this.remove();
//loop through images and process each one
$this.find('li>img').each(processImage);
$pictureFrame.prepend($thumbs);
var close_left = parseInt($pictureFrame.css("left"),10) + options.width;
var close_top = parseInt($pictureFrame.css("top"),10) - 10;
//add the close link
$pictureFrame.find("#iz_close > a").click(function(){
$pictureFrame.animate({height:0},function(){
$(this).animate({width:0},function(){
$pictureFrame.hide();
$overlay.fadeOut();
});
});
}).end().find("#iz_close").css({"left": close_left , "top": close_top}).
hide();
//hide the objects
$pictureFrame.hide();
$overlay.hide();
$pictureFrame.width(0).height(0);
$preview.append(getLink());
base.append($preview);
$("body").append($pictureFrame).append($overlay);
//inner functions, these use vars nested in the plugin
function processImage(){
var $span = $('<span></span>'),
$thumb = $(this),
$newImg = $thumb.clone().height(options.height - 100),
$previewImg = $thumb.clone().height(options.previewHeight);
$thumb.mouseover(function(){
$thumb.css("cursor","pointer");
});
$thumb.height(100);
$thumb.click(function(){
$("#iz_main").find("img").remove().end().append($newImg);
});
$previewImg.click(function(){
show($overlay,$pictureFrame,options);
}).
hover(function(){
$(this).css({"cursor" : "pointer"});
});
$span.append($thumb);
$preview.append($previewImg);
var $stagingImage = $thumb.clone();
$stagingImage.load(function(){
options.thumbsWidth += $(this).get(0).clientWidth;
//add the arrows
if(options.thumbsWidth > options.width && !arrowsLoaded){
$rightArrow = createArrow('right');
$leftArrow = createArrow('left');
$pictureFrame.append($rightArrow).append($leftArrow);
$pictureFrame.find(".arrow").bind("mouseover", function(){
if($(this).hasClass("right_arrow")){
scrollRight($thumbs);
}
else{
scrollLeft($thumbs);
}
});
arrowsLoaded = true;
}
});
$stageDiv.append($stagingImage);
$thumbs.append($span);
}
function scrollRight($target){
return scroll($target,'right');
}
function scrollLeft($target){
return scroll($target,'left');
}
function scroll($target, direction){
var scrollAmount = 0;
var currentPos = parseInt($target.css("right"),10);
if(direction === 'left' && (isNaN(currentPos) || currentPos <= 0)){
console.log(currentPos);
return false;
}
else if(direction === 'right' && currentPos + options.width > options.thumbsWidth){
return false;
}
if(isNaN(currentPos)){
scrollAmount = (direction === 'right') ? 100 : -100;
}
else{
scrollAmount = (direction === 'right') ? currentPos + 100 : currentPos - 100;
}
$target.stop().animate({"right": scrollAmount},{ "duration": 200, "easing": "linear" });
var to = setTimeout(function(){
scroll($target,direction);
},200);
}
function getLink(){
var $link = $('<div><a href="#">Detailed Images</a></div>');
$link.click(function(){
show($overlay,$pictureFrame,options);
});
return $link;
}
return $this;
};
function createArrow(direction){
return $('<div class="arrow ' + direction + '_arrow"> </div>');
}
function show($overlay,$pictureFrame,options){
$overlay.fadeIn('slow',function(){
$pictureFrame.height(0);
$pictureFrame.find("#iz_main > img").remove();
$pictureFrame.find("#iz_thumbs > span").find("img").eq(0).click();
$pictureFrame.find("#iz_thumbs").width(options.thumbsWidth);
$pictureFrame.show();
$pictureFrame.animate({width: options.width},function(){
$pictureFrame.animate({height: options.height},function(){
$pictureFrame.find("#iz_thumbs").slideDown('slow',function(){
$pictureFrame.find("#iz_close").show();
});
});
});
});
}
function getOverlay(){
return $('<div class="zoom_overlay" id="iz_overlay"> </div>');
}
function getPreview(argument){
return $('<div class="zoom_preview"></div>');
}
function getPictureFrame(options){
var $pictureFrame = $('<div class="zoom_pictureFrame" id="iz_pictureFrame"><div class="zoom_close" id="iz_close"><a href="#"><img src="' + options.imagePath + '/close.png"></a></div><div class="zoom_main" id="iz_main"></div></div>'),
height = $(window).height(),
width = $(document).width();
$pictureFrame.css({
'left' : width/2 - (options.width/2),
'top' : height/2 - (options.height/2)
}).
width(options.width);
return $pictureFrame;
}
function getThumbs(){
return $('<div class="zoom_thumbs" id="iz_thumbs"></div>');
}
function createDiv(){
return $('<div id="staging"></div>').appendTo("body");
}
})(jQuery);
1 Answer 1
Little Stuff
- One line functions that are only used once such as
scrollLeft
andscrollRight
should get inlined. - Functions that create divs like
getThumbs
could be calledcreateThumbs
instead - To your point, there is no good reason to have any functions outside of
$.fn.imagezoom = function(base, options){
- There are quite a few references to
10
and100
and200
, these numbers should be replaced with properly named constants - You could bind the
mouseover
of the arrows straight to$rightArrow
and$leftArrow
instead of usingfind(".arrow")
and checking the class.
Bigger picture
I think you need functions that group the following functionalities:
- Creation and setting up of
thumb
- Creation and setting up of
pictureFrame
- Creation and setting up of
overlay
- Creation and setting up of
preview
JsHint
$rightArrow
and$leftArrow
are set withoutvar
getPreview
does not useargument
(argument
is not a terribly descriptive name btw)- There is no need to capture the value of
setTimeout
intovar to
.