Adding product image lightbox effect using jQuery plugin (a better way)

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:

  1. Download the newest jQuery library and add it to your skin folder (skin/frontend/YOUR_PACKAGE/YOUR_THEME/js/).
  2. Download and extract Fancybox plugin to skin/frontend/YOUR_PACKAGE/YOUR_THEME/js/fancybox/
  3. 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.
  4. 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.

  1. 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)
  2. Open the media.phtml file in your favorite text editor.
  3. 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>
  4. 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.

  5. 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.

  6. 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.

  1. 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.

  2. 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).

  3. 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.

  4. 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.


20 comments

  1. Hello, thankyou for your article.
    I tried to use your suggest, but when i click the image, it opens directly the image..and in the chrome inspector i see an error that says:
    “default.js:3 Uncaught TypeError: jQuery(…).fancybox is not a function”

    How can i solve this?
    I tried to insert the js file in the head, before or after the jQuery file, but nothing changed.
    Thankyou.

  2. close button not display in the fancy box.
    how to add close button in it.
    please give any solution . I and waiting for any reply.

  3. great tutorial, however, it does not work with jquery 1.11.1
    It worked when I used 1.8.0 as you did.
    Thanks from Berlin

  4. Thanks so much! Much better to install it like this instead of using an extension because you actually understand how it works !

  5. this tutorial is the simplest one i’ve seen. manually injecting an extension i think is better than installing via admin or backend, in terms of tracing errors if there came any.
    anyway, thanks! exactly what i am looking for 🙂

  6. Hi Mike and thank you for your comment.
    Implementing a prototype version should not be that hard. As a matter of fact it would be much easier because you won’t have to add jQuery. The problem is, I don’t know any good (and free) prototype lightbox plugins and I don’t want to waste my time reinventing the wheel when there are lots of good jQuery ones especially because I always end up using jQuery in my projects one way or another.

  7. Hi Eric and sorry for the late reply.

    That should be quite easy because you don’t need to do complex Magento integration for it. All you need to do is add jQuery and Fancybox, go to [link]http://fancybox.net/howto[/link] and see how to set Fancybox for videos (or any other type of data).

  8. Srdjan,

    Thanks for the tutorial!! Would you know how to modify the following script to use fancy box instead of the silly window it uses now?

    //<![CDATA[
    	Event.observe('li-<?php echo $_item->getId(); ?>', 'click', function(event) {
    		videoPlayer = new Window({
    			className:'magento',
    			title:'<?php echo $_item->getTitle(); ?>',
    			content:$('video-player'),
    			width:<?php echo  Mage::getStoreConfig('video/sizes/video_player_width'); ?>,
    			height:<?php echo  Mage::getStoreConfig('video/sizes/video_player_height')+5; ?>,
    			minimizable:false,
    			maximizable:false,
    			showEffectOptions:{duration:0.4},
    			hideEffectOptions:{duration:0.4},
    			onClose:function(event) { $('video-player').src=""; }
    		});
    		videoPlayer.setZIndex(100);
    		videoPlayer.showCenter(true);
    
    		$('video-player').src="http://www.youtube.com/v/<?php echo $_item->getImageIdentifier(); ?>&hl=fr&autoplay=1";
    		videoPlayer.getContent().update($('video-player'));
    	});
    //]]>

    It currently opens in a crummy overlaid window with no close features, and when the vid is clicked on it requires a page reload to close the video. I would like it to just use your fancybox implementation to display the video.

    Thanks for your help

  9. Thank you for the kind words Milan. We do everything to help the community and give something back.

  10. Each and every tutorials posted in inchoo is of great one and easy to learn. This tuts just make my day go…! Thanx for this awesome post 🙂

  11. Thanks Matt, you are right.

    It was a bit misleading. You should add the link tag around the IMG inside that paragraph not around the paragraph itself (you can see it in the end result at line 8).

    I updated the article.

  12. Awesome tutorial. I was tempted to go the extension route, but glad I opted to run though this. Very well done and explained (though I’ll admit I got a little lost at the “Add a link around the <p class=”" piece as this was the only part without any related code snippet).

    Thanks for the great post, Srdjan. This is great!

    1. Thanks for your feedback MagePsycho.

      I have used your extension in the past but after a few projects that had 20+ extensions for all sorts of things I realized that having an extension for something as simple as product image lightbox is an overkill. There is no need to use extension for it because it is easy to develop and all the lightbox options can be easily changed in the JS file. Once you set them up you will not change them that often so there is really no need for the back-end configuration options.

      On the other hand, the extension is god for store owners and admins that have no development skills. They can just install and configure it without having to code anything. For developers however, I don’t recommend using extensions for simple stuff. Make your own code and you will be sure that it works and if ti doesn’t, you will know where to look for issues.

      Cheers

  13. Hi guys,

    First of all, thanks a lot for this really useful and brilliant website in general! 😉

    Just about the duplicate image issue, I was thinking same about it, like it was a real bug but you can avoid it by ticking “exclude” checkbox for the main product image.

    Hope it’s gonna be useful and working with your example.

    Cheers

    1. Hi Antoine,

      Thank you for your comment. I am aware of the “exclude” feature but I forgot to explain why I haven’t used it. Also the section title “Duplicate image issue” can be a bit misleading.
      You can still use the “exclude” option but in this example the main product image should always be excluded because we want to use the Fancybox next and previous functionality. Setting it up manually for every product is a waste of time end effort especially if you already have a substantial product catalog. Excluding the main product image by code will make things easier to manage.

      Cheers

Leave a Reply

Your email address will not be published. Required fields are marked *

You may use these HTML tags and attributes: <a href="" title=""> <blockquote cite=""> <code> <del datetime=""> <em> <s> <strike> <strong>. You may use following syntax for source code: <pre><code>$current = "Inchoo";</code></pre>.