Adding product image lightbox effect using jQuery plugin (a better way)
This is not a copy-paste kind of tutorial. It is made for beginners so please try to understand what is going on before you start copying the code. If you are an experienced developer you can skip to the end and inspect the final code.
Nowadays every client wants some kind of lightbox effect for product images on the product page. Although there are a lot of ways to do it, I often stumble upon Magento stores that have a buggy and sometimes useless implementation of this feature. In my opinion, it should be simple, fast, easy to use and above all intuitive.
After using several different lightbox plugins for both jQuery and PrototypeJS, I finally found one that is easy to use and implement with just enough features to make it useful. I know what you are thinking, “why don’t you make your own plugin?”. It’s quite simple really. I don’t want to reinvent the wheel. If there is a good enough solution that can do the job and it’s free why should I lose time and budget making it from scratch? No matter what your client says this is not a key site feature and you should not waste your time on it. The solution I found is to use Fancybox 1.3.4. I know that there is Fancybox 2 but it is not free and the old one works just fine. This is a jQuery plugin and some of you might think that we should only use PrototypeJS (because it is the framework that Magento uses) but let’s be honest here; you will need to add jQuery to Magento sooner or later. I mean, Magento EE uses jqzoom for product images.
Magento and jQuery
As I mentioned above, adding jQuery to Magento is pretty much inevitable these days because more and more clients are asking for some advanced JS stuff that could be done much faster with jQuery than with PrototypeJS. One way or another, you will end up using jQuery more often than you think. On the other hand, many third party extensions use jQuery so chances are you already have jQuery in you project.
The main problem with two JS frameworks on the same website is that they use the same function names and symbols (“$” for example) which creates a conflict between them. Luckily, jQuery has a “noConflict” function made to deal with these kind of issues.
Another thing you should pay attention to when adding jQuery to your Magento site is to make sure that it is not already there. Some Magento extension use jQuery and having two jQuery libraries on the same site will also create conflicts. I recommend you remove all jQuery libraries from other extensions and add a global one that will be accessible from any page on the site. This way you can be sure that you have jQuery available for all third party extensions and there won’t be any conflicts with duplicate jQuery libraries.
Adding jQuery to Magento is quite easy. I like to use the local.xml layout file in all my projects so I will use it to add jQuery and all other JS and CSS files into my theme. If you don’t have the local.xml file in your theme layout folder I recommend you create it or if you don’t like the idea you can use page.xml instead but you have to be careful because the JS file that contains the jQuery “noConflict” function has to be added after both jQuery and PrototypeJS are loaded. I will use the local.xml in this article because I like it that way 🙂
Adding the required files
Follow these simple steps to add all of the required files:
- Download the newest jQuery library and add it to your skin folder (skin/frontend/YOUR_PACKAGE/YOUR_THEME/js/).
- Download and extract Fancybox plugin to skin/frontend/YOUR_PACKAGE/YOUR_THEME/js/fancybox/
- Create a default.js file in your skin js folder (skin/frontend/YOUR_PACKAGE/YOUR_THEME/js/default.js). I like to add this file to all my projects for JS that needs to be available globally. We will use it to call jQuery “noConflict” function and setup Fancybox.
- Add the following to the “default” layout handler in the local.xml file (app/design/frontend/YOUR_PACKAGE/YOUR_THEME/layout/local.xml):
<reference name="head"> <action method="addItem"><type>skin_js</type><name>js/jquery-1.8.0.min.js</name></action> <action method="addItem"><type>skin_js</type><name>js/fancybox/fancybox/jquery.fancybox-1.3.4.pack.js</name></action> <action method="addCss"><stylesheet>js/fancybox/fancybox/jquery.fancybox-1.3.4.css</stylesheet></action> <action method="addItem"><type>skin_js</type><name>js/default.js</name></action> </reference>
NOTE: Be sure to add default.js after the jQuery library because it will contain the jQuery “noConflict” function. Also make sure to change the jQuery version to the one you’ve downloaded. In this example I used 1.8.0.
Now we have all the files we need to start implementing Fancybox. To check if everything is added correctly, clear your Magento and your browser cache, go to any page on the front-end and view the page source in your browser. You should see the all the JS and CSS files added to the head section. Please note that if you turned on the “Merge JavaScript Files” and “Merge CSS Files” in back-end you will need to turn them off to be able to see what’s going on when you view the page source. Don’t forget to turn them on again when you are ready to publish your site.
Setting up “noConflict” and Fancybox
We will use the default.js that we created earlier to call the jQuery “noConflict” function and setup Fancybox. Add the following code to default.js:
jQuery.noConflict();
jQuery(document).ready(function(e) {
jQuery('.fancybox-img').fancybox();
});
Make sure that “noConflict” function is at the top of the file before any other JS code. Fancybox has some options that you can use but the default setup looks good enough so I will leave it as it is. If you want to play around you can find the list of all options and APIs here.
The code above will apply Fancybox effect to all HTML elements that have the “fancybox-img” class. This will let you use Fancybox effect anywhere on the site. It does not have to be the product page. You can even use it on CMS pages because it is defined globally in default.js that is loaded on every page.
We are now ready to add Fancybox effect to product images on the product page.
- Open the media.phtml file from your template folder (app/design/frontend/ YOUR_PACKAGE/YOUR_THEME/template/catalog/product/view/media.phtml). If you don’t have the file copy it from the base/default theme (follow the same directory path: app/design/frontend/base/default/catalog/product/view/media.phtml)
- Open the media.phtml file in your favorite text editor.
- Remove the default Magento zoom functionality. If you are working with default Magento media.phtml you just need to remove the following block of code:
<p class="zoom-notice" id="track_hint"><?php echo $this->__('Double click on above image to view full picture') ?></p> <div class="zoom"> <img id="zoom_out" src="<?php echo $this->getSkinUrl('images/slider_btn_zoom_out.gif') ?>" alt="<?php echo $this->__('Zoom Out') ?>" title="<?php echo $this->__('Zoom Out') ?>" class="btn-zoom-out" /> <div id="track"> <div id="handle"></div> </div> <img id="zoom_in" src="<?php echo $this->getSkinUrl('images/slider_btn_zoom_in.gif') ?>" alt="<?php echo $this->__('Zoom In') ?>" title="<?php echo $this->__('Zoom In') ?>" class="btn-zoom-in" /> </div> <script type="text/javascript"> //<![CDATA[ Event.observe(window, 'load', function() { product_zoom = new Product.Zoom('image', 'track', 'handle', 'zoom_in', 'zoom_out', 'track_hint'); }); //]]> </script>
- Add a link around the img tag in <p class=”product-image product-image-zoom”> … </p> tag with the following attributes:href: $this->helper(‘catalog/image’)->init($_product, ‘image’)
class: “fancybox-img”
rel: “product-gallery”You might not see it at first because it is located inside the $_img variable.If you remember we used the “fancybox-img” class to invoke the Fancybox effect. rel=”product-gallery” will create a Fancybox group that we are also going to use on the “More Views” thumbnail images so that we can have Fancybox next and previous image functionality. - Inside the link element we just created we need to resize the main image because we are not using default Magetno zoom functionality so we don’t need the full size image. Just add “resize(265)” to the $this->helper(‘catalog/image’)->init($_product, ‘image’) for the “src”attribute value. If you used the default Magetno media.phtml file you should end up with something like this:
$_img = '<a href="' . $this->helper('catalog/image')->init($_product, 'image') . '" class="fancybox-img" rel="product-gallery"><img id="image" src="'.$this->helper('catalog/image')->init($_product, 'image') ->resize(265).'" alt="'.$this->htmlEscape($this->getImageLabel()).'" title="'.$this->htmlEscape($this->getImageLabel()).'" /></a>';
Now the basic Fancybox effect should work for the main product image on the product page. Clear the Magento and browser cache, go to your store front-end and open a product that has some images. You should be able to see the Fancybox effect when you click on the main product image.
- Remove the “onClick” event, replace the “href” value with <?php echo $_image->getUrl() ?> and add the “fancybox-img” class and “product-gallery” rel to the link element wrapping the thumbnail image in the “More Views” section. Your thumbnail element should look something like this:
<a href="<?php echo $_image->getUrl() ?>" class="fancybox-img" rel="product-gallery" title="<?php echo $this->htmlEscape($_image->getLabel()) ?>"><img src="<?php echo $this->helper('catalog/image')->init($this->getProduct(), 'thumbnail', $_image->getFile())->resize(56); ?>" width="56" height="56" alt="<?php echo $this->htmlEscape($_image->getLabel()) ?>" /></a>
Fancybox should now work for both main image and “More Views” thumbnail images. You should also be able to move through all the images by using next and previous arrows in the Fancybox modal window. Go to any product page that have more than one product image (I suggest at least three) and check it out.
Duplicate image issue
Using Fancybox with next and previous options requires one instance of every product image but Magento (by default) adds the main product image in the “More Views” section. This means that we basically have two instances of main product image on the page and that creates bad user experience. To resolve this issue we need to remove the main product image from the “More View” section with some small changes in the media.phtml file.
- Add the following line of code below the echo $_helper->productAttribute($_product, $_img, ‘image’); in the product image P tag with “product-image-zoom”class:
$main_image = basename($this->helper('catalog/image')->init($_product, 'image'));
This will save the main product image filename to the $main_image variable.
- Change
<?php if (count($this->getGalleryImages()) > 0): ?>
to
<?php if (count($this->getGalleryImages()) > 1): ?>
This is the statement that determines whether to display the “More Views” section or not. We changed the condition to more than one because we want to remove the main product image from the “More Views” section so we will not display this section if there is only one product image (the main one).
- Add the following line of code below the foreach loop that renders all the thumbnail images in the “More Views” section:
<?php $thumb_image = basename($_image->getUrl()); ?>
This will save the thumbnail image filename to the $thumb_image variable so that we can compare it with the $main_image so we can ignore it when adding images to the “More Views” section.
- Wrap the thumbnail LI element in a new if statement like shown below:
<?php if($main_image != $thumb_image): ?> <li> … </li> <?php endif; ?>
This will compare the current thumbnail image file name with the main image filename and render all the thumbnails except the one for the main product image.
End result
At the end your media.phtml file should look something like this:
<?php
$_product = $this->getProduct();
$_helper = $this->helper('catalog/output');
?>
<?php if ($_product->getImage() != 'no_selection' && $_product->getImage()): ?>
<p class="product-image product-image-zoom">
<?php
$_img = '<a href="' . $this->helper('catalog/image')->init($_product, 'image') . '" class="fancybox-img" rel="product-gallery"><img id="image" src="'.$this->helper('catalog/image')->init($_product, 'image')->resize(265).'" alt="'.$this->htmlEscape($this->getImageLabel()).'" title="'.$this->htmlEscape($this->getImageLabel()).'" /></a>';
echo $_helper->productAttribute($_product, $_img, 'image');
$main_image = basename($this->helper('catalog/image')->init($_product, 'image'));
?>
</p>
<?php else: ?>
<p class="product-image">
<?php
$_img = '<img src="'.$this->helper('catalog/image')->init($_product, 'image')->resize(265).'" alt="'.$this->htmlEscape($this->getImageLabel()).'" title="'.$this->htmlEscape($this->getImageLabel()).'" />';
echo $_helper->productAttribute($_product, $_img, 'image');
?>
</p>
<?php endif; ?>
<?php if (count($this->getGalleryImages()) > 1): ?>
<div class="more-views">
<h2><?php echo $this->__('More Views') ?></h2>
<ul>
<?php foreach ($this->getGalleryImages() as $_image): ?>
<?php $thumb_image = basename($_image->getUrl()); ?>
<?php if($main_image != $thumb_image): ?>
<li>
<a href="<?php echo $_image->getUrl() ?>" class="fancybox-img" rel="product-gallery" title="<?php echo $this->htmlEscape($_image->getLabel()) ?>"><img src="<?php echo $this->helper('catalog/image')->init($this->getProduct(), 'thumbnail', $_image->getFile())->resize(56); ?>" width="56" height="56" alt="<?php echo $this->htmlEscape($_image->getLabel()) ?>" /></a>
</li>
<?php endif; ?>
<?php endforeach; ?>
</ul>
</div>
<?php endif; ?>
Now you are ready to test everything but be sure to clear your Magento and browser cache before you do.
I hope this will be useful to you as much as it is for me. I use it all the time because it is quite simple and it does not require any third-party Magento extensions. Anyway, we must know that any little help in bringing “beauty” to product photography for eCommerce online store is going to help with ROI (return of investment) of entire eCommerce project in the end. However, if you’re still feeling overwhelmed by all Magento info on the web, you can reach out to us to help you with Magento development.