Ask the CSS Guy

Pushing a shopping cart full of tea with CSS and JavaScript

While brainstorming for a recent project, I toyed with the idea of having a shopping cart that follows the user on a scrolling page. The idea was partly inspired by Derek Allard's entry, "Conditionally Sticky Sidebar".

The idea would be that the cart would only start following the scrolling page if it had a product in it; an empty cart would not. I ended up not using the feature for a variety fo reasons, but I thought I could still share the logic behind the effect.

For those who like to skip ahead: view the final example or download a zip with all the html, CSS, JavaScript, and supporting images.

First, a storefront

To start off, here's example 1 - a very basic storefront. In this example, our storefront sells tea. The shopping cart, pictured with some items inside, is on the right. The shopping cart is 200 pixels wide, and I've reserved the right-side 200 pixels all the way down the page to serve as the shopping cart 'aisle'.

HTML

<div id="shoppingCartAisle">
  <div id="shoppingCart">
    <h3>Shopping Cart</h3>
    <ul>
      <li>product 1</>
      <li>product 2</>
      <li>product 3</>
    </ul>
  </div>
</div>

CSS

...
#shoppingCartAisle {
  width:200px;
  float:left;
}
#shoppingCartAisle #shoppingCart {
  width:200px;
  position:absolute;
  top:0;
  background:transparent url(images/bottom.gif) no-repeat bottom left;
}
#shoppingCartAisle #shoppingCart h3 	{
  background:transparent url(images/top.gif) no-repeat top left;
  margin-top:0;
  padding:4px;
 }
...

Notice that I have a 'shoppingCartAisle', which is the container for the 'shoppingCart' div. #shoppingCartAisle is set to position relative, and #shoppingCart is absolutely positioned to stick to the top of #shoppingCartAisle. (I'm using absolute positioning because I'm setting up what is to come.)

Using position:fixed

To illustrate the cart running up and down the page, I've created example 2, which has #shoppingCart using position:fixed; instead of position:absolute;. As the user scrolls, the shoppingCart follows the browser. Unfortunately, it overlaps the header area as well. I'd rather my cart stay in the aisle.

Deciding how to do this

This is where Derek Allard's article proved to be of great reference. I varied from his approach in two ways:

  1. My cart does not hug the side of the browser, but instead stays within the right column of a fixed-width layout.
  2. I planned for the distance from the top of the browser to the cart to vary based on promotional messages or whatnot that may/may not line the top of the page, so it was important for the cart to be positioned absolute to the aisle, not the browser window.

It's helpful for me to state what I want to happen in English before writing the JavaScript. When the browser scrolls, determine if it has scrolled down past where the 'shoppingCartAisle' begins. If so, change the 'shoppingCart' to position:fixed so that it follows the user down the page. If the browser scrolls back up to where 'shoppingCartAisle' starts, then switch 'shoppingCart' back to position:absolute so that it doesn't go into the header space of the page.

The fun part - JavaScript

To determine where the shoppingCartAisle begins, I have a function called establishTopPosition(), which is the first of two functions you see below. The second function, pushMyCart(), does what the above paragraph describes in English. For the parts that look weird, just know that Derek Allard figured that stuff out for me.

<script type="text/javascript">
function establishTopPosition() {
  var shoppingCartAisle = document.getElementById('shoppingCartAisle');
  var y = 0;
  while (shoppingCartAisle!=null) {
    y += shoppingCartAisle.offsetTop
    shoppingCartAisle = shoppingCartAisle.offsetParent;
  }
    return y;
}
function pushMyCart() {
  var shoppingCart = document.getElementById('shoppingCart');
  var topPos = establishTopPosition();
  if( window.XMLHttpRequest ) { // IE 6 hates position fixed
    if (document.documentElement.scrollTop > topPos  || self.pageYOffset > topPos) {
      shoppingCart.style.position = 'fixed';
    } else {
      shoppingCart.style.position = 'absolute';
    }
  }
}
</script>

And I want JavaScript to pushMyCart() when the page is scrolling, so add this to the onscroll event of the body:

<body onscroll="pushMyCart();">

See example 3 where JavaScript is pushing my cart.

Let's enhance it some more

I only want a rolling shopping cart if there are products in the cart. If it's empty, I just want it to stay put.

To do this, I'm going to add a class to the shopping cart called 'rollingCart' when a product is added. Then I will tweak my JavaScript to only roll the cart if it has a class of 'rollingCart'.

I also need a way to clear out the items from my cart, so I've provided a link, 'Empty Cart', than when clicked, removes the class of 'rollingCart' from the shopping cart.

Once again, I'm relying on the great work of others to accomplish this, namely Robert Nyman, who wrote the very helpful functions getElementsByClassName, addClassName, and removeClassName, and also one Simon Willison, who wrote the addLoadEvent function. (Yes, I know there are libraries that can do this, but this assumes there are none being used.)

Here are the functions I wrote to accomplish my new task. The first one sets up the 'add to cart' links so that once clicked, they give a 'rollingCart' class to the cart, then tell the browser to push my cart. The second function sets up the 'clear cart' link, so that once clicked, it removes the 'rollingCart' class, then tells the browser to parkMyCart(). parkMyCart() attaches the cart to the top of the shoppingCartAisle element again.

function addToCartLinks() {
  var links = getElementsByClassName('addToCart','a',document);
  var cart = document.getElementById('shoppingCart');
  for (var i=0; i<links.length; i++) {
    links[i].onclick = function() {
      addClassName(cart,'rollingCart');
      pushMyCart();
      return false;
    }
  }
}

function clearCart() {
  var dumpit = document.getElementById('clearCart');
  var cart = document.getElementById('shoppingCart');
  dumpit.onclick = function() {
    removeClassName(cart,'rollingCart');
    parkMyCart();
    return false;
  }
}

function parkMyCart() {
  var cart = document.getElementById('shoppingCart');
  cart.style.position = 'absolute';
}

See it in action with example 4 - remember to 'add to cart' and 'clear cart' to see the effect. Feel free to download the zip as well.

Note that I kept these functions simple for the sake of demonstration - no products are added or removed from the cart when links are clicked; just the class name changes. Also, this doesn't work in IE6, and the JavaScript allows IE6 to 'gracefully degrade' by always having a positioned absolute cart.

I hope you find the post helpful. I am no JavaScript genius, so if you see anything that could make this better, please leave a comment.

Why tea?

This post is so themed to raise awareness for Ron Paul in honor of this weekend's fundraiser event, which purposefully coincides with the anniversary of the Boston Tea Party. If you don't know who Ron Paul is, I encourage you to google him or search YouTube for supporter-made videos. Or start with these: one, two, three. Thanks.

Comments (16)

Salt said:

Nice article, the 4:th example page doesn't work tho, "Add to cart" and "Clear cart" does nothing.

Coblo said:

Another good article...however...I've never liked the effect of anything scrolling with the user. It just feels tacky tbh.

CSS Guy said:

@Salt:

What browser and platform are you using? it works for me in ff2/mac, safari/mac, and ie7/win. (It's not supposed to work for IE6/win.

@Coblo:
Agreed about the tackiness - which is one of the reasons I abandoned the idea. Still - it was a fun exercise.

Dylan B. said:

There are work arounds for fixing an element in ie6. They might be quite a bit more javascript though.

TomW said:

Example 4 isn't working for me either. I'm on PC (Windows XP Pro) using Firefox 2.0.0.11.

Jennifer C. said:

At first I thought it wasn't working, but then I actually read the article and noted that it works only after adding an item to the cart, clicking the add links. I too am using Firefox 2.0.0.11 on XP Pro.

Try adding an item, then scroll down. Then, clear cart - poof, the cart will revert to the top of the screen.

Now, you could get really wild and crazy and add some mootools transforms for a visual frenzy!

TomW said:

Ah, thanks Jennifer. Short attention span was the problem on this end.

Clong said:

I implemented a very similar feature on the Ecommerce site I work for at http://well.ca you can see it in action on http://well.ca/products/loreal-men-expert-anti-fatigue_2722.html

Try clicking on add to cart and see the product "fly" to the cart. Something really basic, but it's amazing how many people love it. We're really trying to make shopping fun.

ctraos said:

gracias, exelente post!!

Ivan said:

Hi!
My tip.....
When you have to do any web page for a client you get a psd mock up. You have to stick to that mock-up up to a pixel and ALL clients demands that their page is a bug free at least in IE6, IE7, Opera, FF and MAC.If you don't do that you'll be fired and replaced!!!!
I like you page and all the examples however some of them are buggy.
It's not what you can do in IE7 waiting for IE8 and css3. Sometimes i get upset at my work when things which shouldn't been so complecated don't match mock-up in Mac which have 2yrs old safari and your clients ask you to fix that!
So....don't go so crazy with your scripts for just IE7 & Firefox.
ivan

asfsfasf said:

The example failed on Opera 9.x

Charles said:

Nice article, and very useful. Up until that Ron Paul part. Seriously? Ron Paul? What a cook.

Konyakov Artem said:

This example does not work in the browser of NN 9.0.0.6. Works more faithful, but a bit not correctly!

Adrian said:

I'm using FF3 for Mac and examples 2 and 3 are the only ones work (scroll). The 'clear cart' and 'add to cart' do not work on any examples.

CSS Guy said:

@Adrian:

Are you sure you clicked an 'Add to cart' link first? That must be done before the cart will 'follow' you. I've heard others not notice that bit in the article, so I'm sure I could be more clear about what that example is supposed to do.

 

Post a comment