JSCart
No Javascript tutorial would be complete without some kind of shopping cart. JSCart to the rescue! In this project we’ll build a one page store/cart where customers can add items to the cart and watch the MAGIC as it updates on the fly.
1. HTML and Styles
In this project we’re going to insert significant content dynamically into the HTML. Didn’t I previously say that was a bad idea? Kinda. Sometimes it makes sense. Maybe you’re loading the content from another website, maybe you want to keep most of the HTML static for caching and just update some parts with Javascript. There are times it makes sense, so let’s try it.
That’ll leave us with a very barebones HTML file:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 |
|
Note that our head now includes a data.js
file. For our example we’re going to type our inventory data right into data.js
, but when doing it "for real" this file could retrieve the data from anywhere (a file, database, the web, etc).
Create an application.js
file and just start it with the document ready wrapper:
1 2 3 |
|
The create a styles.css
and paste in this style code:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 |
|
Load the page in your browser and you won’t see much! It’ll just be a header and an empty cart.
2. Loading External Data
We’re going to store the inventory information in data.js
and keep it really simple. We’ll just store them in a Javascript array where each element of the array is one product. The products themselves are stored using the "Object Literal Notation" which we saw in JSTasker. Feel free to makeup your own products and values, but match the names of the attributes:
1 2 3 4 5 6 7 |
|
Javascript knows it’s an array because of the [
and ]
. Each object in the array is marked with {
and }
. Then inside the objects there are key/value pairs for the keys price, name, stock, and product_id.
Loading the Data
Go over to your application.js
. Let’s prove that inventory
exists by putting this inside your document ready block:
1 2 |
|
We’re taking the rawInventory
data and turning it into a jQuery object so it’s easier to work with, then storing that into inventory
. Then we alert the size of that object which should display 5. If that works then we know the data is loaded and accessible!
Building a Prototype
Now it’s going to get interesting. We’re creating content on the fly, but it’s still wrong to have a lot of HTML in your Javascript. What can we do?
Let’s try putting a template into our HTML. It’ll have the "shape" of an inventory item, it just won’t have any data. Then from Javascript we can find that prototype, make copies of it, fill in the data, and inject those copies into the actual HTML. Go over to your index.html
and add this prototype inside the inventory DIV:
1 2 3 4 5 6 7 8 |
|
Refresh your browser and you’ll see a blank item show up. Next let’s find this prototype from the Javascript. Inside your document ready block of application.js
, try this:
1 2 |
|
Refresh your browser and the blank inventory item is gone. We’ve got it loaded into prototypeItem
, so now we can create our actual product listings.
Building Products from the Prototype
We’ve got the prototypeItem
, we’ve got the inventory
, now we need to iterate through the inventory and inject a copy of the prototype into our listing for each one. That’s complex, so let’s do it one step at a time.
Add this each
method call inside your document ready:
1 2 3 |
|
Inside the each
block, this
refers to the OLN object inside of inventory
. We can access the attributes using a dot notation like this.name
. Try this in your browser and ensure each of your items is alerted.
The first step is to create a copy or a clone
of the prototypeItem
. Add this inside your each
block:
1
|
|
That creates a "deep clone" of prototypeItem
. Let’s set the first attribute:
1
|
|
Within the object item
, find any H3s and replace their text
with the value of this.name
. Then insert it into the inventory DIV:
1
|
|
Refresh your browser and your items should all appear. They’ll have names, but the other attributes are blank.
On your own, add more Javascript instructions to set the price and quantity. We still have to figure out the add to cart link, but that’s the next iteration.
One thing you’ll want to change is the item’s DIV id. Use the attr
method to set it to a unique id like "product_6" for each item. The attr
method takes two parameters: the name of the attribute to change and the value to change it to. The attribute you want to change is just "id"
.
Once you’re setting the name, price, quantity, id and displaying them in the inventory, then this iteration is done.
3. Adding Items to the Cart
Those "Add to Cart" links are mocking us. They don’t do anything!
We need to add a click
listener. Each link will need it’s own listener, so it’s easiest to add it within your inventory.each
loop. Start with this:
1 2 3 |
|
Try it in your browser. What’s up with that "undefined"? this
isn’t what we think it is. When the function actually gets executed, this
refers to the item’s DIV, not the data we pulled out of the inventory
array. Hmmm. What can we do with that? Try changing you alert
to this:
1
|
|
Try it in the browser. Looks promising, right? That’s pulling the id that we set which holds the product id from the data array. Our cart could use that information to find the item in the inventory, get its price, etc.
Building Up the Cart
It looks like we can get enough information when the "Add to Cart" link is clicked, but what are we going to do with it? The cart is basically a blank DIV right now. Let’s set it up like a spreadsheet where each inventory item has a row and a quantity ordered.
We can use the same prototype style we did for the inventory listings. Add this plain HTML into your cart DIV:
1 2 3 4 5 |
|
Go back to your application.js
and look at how we worked with prototypeItem
. Add a similar set of instructions to build up cartItem
and append
it to the cart DIV. Refresh your browser and make sure a line shows up for each of your products.
Connecting the Click to the Cart
Now we need to connect the click action to the cart. When a user clicks on an add to cart link we need to…
- Find the link’s DIV’s ID
- Find the DIV in the cart with the same ID
- Increment the quantity in the cart item
Currently our alert
line finds the proper id
value. Let’s store that into a variable named targetId
. Then find the actual target DIV in the cart like this:
1
|
|
Then the only thing left is to properly update the quantity. That math is a little tricky, so let’s take a smaller step:
1
|
|
Refresh your browser and click the Add to Cart links. You should see the associated cart quantity change from 0
to ME!
.
Dealing with Strings and Numbers
Our cart quantity is just a string. We want to increase it by one when the user clicks, but if we add a 1 to the contents of quantity it’ll just add strings resulting in "111" instead of "3". We need to…
- Find the current value in QTY
- Convert it to an integer
- Add one to it
- Store it back into QTY
Let’s first find the SPAN itself and store that into a variable:
1
|
|
We can pull out the .text()
to get the string number then use Javascript’s parseInt
method to convert it to an integer:
1
|
|
From there it’s just one more line to add 1
to current
and stick it back into quantity
– you figure it out.
Then refresh your browser, click some links, and your cart quantities should increase numerically.
4. Cart Math
We’re plunking items into the cart. Let’s add a little more intelligence there to update the total items count and give the user a total price.
Prefactoring
I thought I just made up the word prefactoring, but Wikipedia says that it exists.
Let’s apply the same pattern we used in JSTasker to group methods into a name space. I want to collect a few methods that will be responsible for tending the cart, just like we did with the To-Do list. Up at the top of your application.js
, let’s start with this:
1 2 3 4 5 6 7 8 |
|
Again we’re using the "Object Literal Notation" here to namespace three methods. We’ll call updateCart
from our click instruction, and it’ll call updateCartItemCount
and updateCartTotal
for us.
Calling the Updaters
Let’s start by inserting an alert
line within updateCart
that says "Updating the cart", one in updateCartItemCount
that says "Updating the item count", and one in the updateCartTotal
that says "Updating the total".
Refresh your browser and nothing should happen. Why do I bother with putting in these alerts
and checking things? I believe in "Fail Early, Fail Often". I’m not very smart, I make mistakes (especially in Javascript), and throwing in a few alerts lets me know that things are or aren’t on the right track. If, for instance, you forgot to declare the anonymous function for updateCart
and instead just put instructions, your script would either fail to load OR it’d execute the alert
as soon as it was read (when the page was loaded). This would be quick clue that something was wrong.
But our methods aren’t getting called yet which is correct. Go down you application.js
and find the item.on('click'...)
listener. As the last instruction there, add a call to JSCart.updateCart()
.
Refresh your browser, click an add to cart link, and the three alerts should pop up. If you’re satisfied that things are wired together properly, remove the three alert lines.
Writing the updateCartItemCount
Function
Our updateCartItemCount
function should now be blank. Here’s what we need it to do:
- Find all the items in the cart
- Gather the quantities specified for each one
- Add them together
- Save the result into the text span "X items in cart"
Finding the Items
Each item type has it’s own DIV with the class name "cart_item". Write a selector that finds them all and stores them into a variable named items
.
Try popping an alert that tells you how many items there are. It should be the same as your number of products in the inventory.
Gathering the Quantities
We’ll need to iterate through the list of items, detect their "qty"
SPAN, get it’s contents, convert it to an integer, then add that to the total. Here’s the basic structure:
1 2 3 4 5 6 |
|
Storing the Value
Then you just need to find the SPAN with ID 'cartQuantity'
and store total
into it’s text
.
Refresh your browser and confirm that things are working. Make sure your count is accurate!
Writing the updateCartTotal
Function
Having completed the updateCartItemCount
, it becomes apparent that we’re currently setup to do things twice. The updateCartTotal
function needs to do almost the same thing. The only difference is that instead of adding the quantity into total we want to multiply quantity times price and add that.
Refactoring necessary? Probably. But let’s do it the simple way first. Copy & Paste! Grab the body of updateCartItemCount
and paste it into updateCartTotal
count. Then we need to make a few changes:
- Now
value
feels like the wrong name, rename it toquantity
- After
quantity
is found, findprice
- Multiply
quantity
timesprice
to makesubtotal
- Add
subtotal
tototal
- Change the text inserter to put
total
into the SPAN with ID'cartPrice'
I think you can do this on your own, go to it! One little note: when you pull out the price string and want to convert it to a number, use parseFloat
so the cents don’t get truncated.
I Hate Floats
Dealing with floating point numbers is always a pain. At least in my experimenting, every once in awhile my total would jump to having really small fractions after the decimal. Ugh! In general, when working with prices, I recommend you store prices in cents so you can use integers. But let’s just leave these as floats and fix this presentation issue.
Find the line where you’re inserting total
into the cartQuantity
SPAN. Instead of just inserting total
, use the Javascript native method toFixed
like this: total.toFixed(2)
.
Extensions
This project, like most shopping carts, could go a hundred directions. Here are some ideas to try adding on:
- Add a clickable link that clears the cart
- Add a keyboard shortcut that clears the cart
- Alphabetize the items in the inventory
- Decrease the number of items in inventory when they’re added to the cart
- When stock is low, change the listing to say "Only X Left!"
- When an item is out of stock, grey out or remove it from the inventory
- Prevent the cart from having more of an item than are in stock
- Add a link to remove individual items from the cart
- Change the cart to use a text field so the quantity could be easily manipulated and the totals updated
- Implement Drag & Drop so the items can be dragged and dropped in the cart