Compare before and after photos using CSS Clip and jQuery

UPDATE: Turns out this has already been done! Oh well, I still had fun with it.

Last night I followed a link from Reddit to The Berlin Wall: 20 Years Later on the New York Times site. It was an interesting feature, but I thought that the use of Flash to display the before and after photos was a little bit gratuitous, especially considering that they could make the content accessible to a wider range of user agents (mobile browsers and Google image search come to mind) by using CSS and Javascript.

Here's a demo I put together:

If you'd like to experiment with this yourself, here's how:

  1. Include jQuery and jQuery UI. I like to use the libraries hosted by Google.
  2. Here is the markup - beats the hell out of Flash embed code!
    
    <div class="imagecompare">
        <img src="clip_img_1.jpg" alt="Before photo shows model with frizzy hair" />
        <img src="clip_img_2.jpg" alt="After photo shows manageable (but greasy-looking) hair" />
        <a class="resizer">If your browser supports it, drag the handle in the middle to compare the two photos</a>
    </div>
    
  3. Here is the CSS. In a nutshell, we position the images on top of one another and then set the CSS clip property of the 'after' image. However, we apply most of the styles by adding classes using jQuery, so that if javascript is disabled the images can be seen in all their unclipped, static-positioned glory. Some CSS is also applied to the a.resizer to position it in the middle of our container, set the cursor style and set an extreme negative text-indent to hide the content of the link.
    
    div.imagecompare { width:500px; height:640px; position:relative; }
    div.imagecompare img.after { 
       clip: rect(0px 500px 640px 250px); /* use spaces instead of commas so it works in IE */
    }
    div.imagecompare .resizer {
       display:none; /* our JS will set this to block */
       height:100%;
       width:2px;
       position:absolute;
       top:0;
       left: 250px;
       cursor:col-resize;
       background:#888;
       text-indent:-999em;
    }
    
  4. The jQuery is pretty straightforward, because Draggable does most of the work. We set a callback on the 'drag' event, to adjust the clip of the after image when the resizer is dragged. There is also a function that sets the clip and repositions the resizer on click. Finally, we set some styles which are being set by JS because they shouldn't be present if JS is disabled.
    
    jQuery(document).ready( function ()
     {
         /* Init jQuery UI Draggable on the resizer element - see http://docs.jquery.com/UI/API/1.7.2/Draggable */
         jQuery('.resizer').draggable({ 
             axis: 'x',
             containment:'parent',
             drag: function(event,ui) 
             {
                 // Set the clip property of the after image on drag
                 jQuery(this).prev('img.after').css('clip','rect(0px 500px 640px '+ui.position.left+'px)');
             }
         }).css('display','block');
         jQuery('.imagecompare img').click(function (event) 
         {
             // Get offset of imgcompare div
             var imgcompare_offset = jQuery(this).parent().offset();
             // The position/clip-left value we should use is the X coordinate of the click, minus the offset of the imgcompare container.
             var pos = event.pageX - imgcompare_offset.left;
             // Set the resizer position
             jQuery(this).parent().find('.resizer').css('left',pos+'px');
             // Set the clip
             jQuery(this).parent().find('img.after').css('clip','rect(0px 500px 640px '+pos+'px)');
         });
    
        /* Set the position of the img elements inside .imgcompare so they will overlap */
        jQuery('.imagecompare img').css({
             position: 'absolute',
             top: 0,
             left: 0 
        });
        
        jQuery('.imagecompare img:last').addClass('after'); // Add after class to the 'after' image
    });
    

This was my first time working with the CSS clip property. While I was pleasantly surprised that it works in IE6+ and all modern browsers, I was disappointed to learn that it's impossible to set the top, right, bottom and left clip values separately as you can for margin and padding. Instead, you have to reset the whole statement including all four values.

Be sure to leave a comment if you find a use for this code!

Comments

  1. Rick says:

    Your second image hides the subsequent text when javascript is turned off. Use overflow:

    div.imagecompare {
    height:640px;
    overflow:scroll;
    position:relative;
    width:500px;
    }

  2. You might want to widen the draggable handle a bit and add a hover action to the images so that the draggable handle gets a bit more attention and lets the user know that it is interactive.

    Sample CSS additions (transparency could be done via jQuery):

    div.imagecompare .resizer {
    opacity: .5;
    width: 5px;
    }

    div.imagecompare:hover .resizer {
    opacity: 1;
    }

    Great job. :)

  3. Danny says:

    Thank you! I learned a lot from reading that.

  4. [...] visually powerful, split-screen effect was created using Flash, but some developers suggested a more elegant way to do the same thing using CSS Clip and jQuery. It’s certainly not as dramatic nor profound as the fall of the Berlin Wall, but Flash’s [...]

Submit a Comment