One of our eCommerce clients at work had a number of products they wanted to put on special to promote on the web. Each product has a different "reason" why it's on special. Perhaps it's discontinued, perhaps its special pricing, perhaps it's free shipping. Originally they wanted a special page built for a new type of discount: "rollback pricing". We argued that in general, a visitor on the web doesn't really care why a product is on sale, just that it is, and that it may be of disservice to split up the specials page into different pages.

Instead, we compromised on leaving the specials page as a single page, but allowing the products within to be viewed all at once, or filtered by the type of special that it is. A little jQuery is always up for the job!


View Demo   Download Files


The Markup

Each block has a wrapping div. It has one class that it shares with all other blocks for common styling and a class unique to its "type". Div's don't have href elements, but they can have rel attributes, which I used to store a URL. In the demo, div clickablility isn't implemented, but the code is just commented out in the JavaScript, so you can see how to make that happen there.

<div class="discounted-item reduced" rel="/store/catalog/Dryline_Markers-47-1.html">

    <img src="images/discountthumb-streamliner.jpg" alt="streamliner" />
    <div class="reasonbar">
        <div class="prod-title">Streamliner Field Chalkers</div>
        <div class="reason">Rollback Pricing</div>

    <div class="discount-bar">
        Price lower than catalog. Save $30!


The "filtering" menu is just a series of links. Each link has an ID that matches the class of the type it intends to filter by:

<p id="catpicker">
<a href="#" id="allcat" class="current">VIEW ALL</a> | 
<a href="#" id="reduced" class="filter">Rollback Pricing</a> | 
<a href="#" id="freeshipping" class="filter">Free Shipping</a> | 
<a href="#" id="discontinued" class="filter">Closeout</a> | 
<a href="#" id="webonly" class="filter">Web Only</a>


There is nothing too tricky going on in the CSS, but I'll drop it in here just for good measure.

*                           { margin: 0; padding: 0; }
html                        { overflow-y: scroll; }
body                        { font: 12px Georgia; }

#page-wrap                  { width: 690px; margin: 20px auto; }

h1                          { font: 30px Georgia; margin: 0 0 10px 0;  }

.discounted-item            { width: 100%; margin: 0 0 10px 0; position: relative; cursor: pointer;
                              height: 79px; }

.discount-bar               { padding: 10px 0 10px 150px; font: italic 18px Georgia, Serif; }

.reasonbar                  { padding: 4px 0 4px 150px; overflow: hidden;
                              width: 540px; color: white; }
.prod-title                 { width: 49%; float: left; font: bold 17px Helvetica, Sans-Serif; }
.reason                     { width: 49%; float: right; text-align: right; text-transform: uppercase; 
                              letter-spacing: 2px; padding: 0 5px 0 0; }
.discounted-item img        { position: absolute; top: 0; left: 0; }

.reduced                    { border: 2px solid #A34427; }
.reduced .reasonbar         { background: #A34427; }
.reduced .discount-bar      { color: #A34427; background: white; }

Random things of note:

  • Because the height of the page grows and shrinks, it's best to force a vertical scrollbar to avoid jumps in horizontal centering.
  • Image was placed with absolute positioning. Easy and sturdy layout technique for this, with no floats.
  • Div's have a pointer cursor applied, since in a live scenario they would be clickable.

The jQuery JavaScript

Added a little opacity-rollover action. (Also, this is the area you could make the div's clickable, see the actual JS in the example).

       }, function() {

And to make the filtering work:

       $("#catpicker a").removeClass("current");
       return false;
        var thisFilter = $(this).attr("id");
        $("."+ thisFilter).slideDown();
        $("#catpicker a").removeClass("current");
        return false;

Slightly different code for the "View All" button, since all it has to do is reveal everything.

In the live demo, there is also some "shuffling" going on, so that the order of the specials is randomized on every page load thanks to James Padolsey's DOM shuffler.

Similar Tutorial

Trevor Davis did a very similar demo on NETTUTS a while back.