An AJAX Based Shopping Cart with Drap Drop Item Effect

An AJAX Based Shopping Cart
Step 1 – the MySQL Database
If you want to set up a working demo, you’ll need to execute the following SQL code in your database manager (e.g. phpMyAdmin). It will set up the table and insert a few products. The code is also available in table.sql in the demo files.
table.sql
table.sql
CREATE TABLE IF NOT EXISTS `internet_shop` (
`id` int(6) NOT NULL auto_increment,
`img` varchar(32) collate utf8_unicode_ci NOT NULL default '',
`name` varchar(64) collate utf8_unicode_ci NOT NULL default '',
`description` text collate utf8_unicode_ci NOT NULL,
`price` double NOT NULL default '0',
PRIMARY KEY (`id`),
UNIQUE KEY `img` (`img`)
) ENGINE=MyISAM DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci AUTO_INCREMENT=7 ;
INSERT INTO `internet_shop` VALUES(1, 'iPod.png', 'iPod', 'The original and popular iPod.', 200);
INSERT INTO `internet_shop` VALUES(2, 'iMac.png', 'iMac', 'The iMac computer.', 1200);
INSERT INTO `internet_shop` VALUES(3, 'iPhone.png', 'iPhone', 'This is the new iPhone.', 400);
INSERT INTO `internet_shop` VALUES(4, 'iPod-Shuffle.png', 'iPod Shuffle', 'The new iPod shuffle.', 49);
INSERT INTO `internet_shop` VALUES(5, 'iPod-Nano.png', 'iPod Nano', 'The new iPod Nano.', 99);
INSERT INTO `internet_shop` VALUES(6, 'Apple-TV.png', 'Apple TV', 'The new Apple TV. Buy it now!', 300);
After this, you should fill in your MySQL credentials in connect.inc.php.
Step 2 – the XHTML
First we start off with our main markup.
demo.php
demo.php
<div id="main-container"> <!-- the main container element -->
<div class="tutorialzine"> <!-- some headings -->
<h1>Shopping cart</h1>
<h3>The best products at the best prices</h3>
</div>
<div class="container"> <!-- the first section - products -->
<span class="top-label">
<span class="label-txt">Products</span> <!-- section label -->
</span>
<div class="content-area">
<div class="content drag-desired"> <!-- assigning a common content class and an individually targeted drag-desired class -->
<?php
// php code that generates the products
?>
<div class="clear"></div> <!-- clearing the floats -->
</div>
</div>
<div class="bottom-container-border"> <!-- bottom part of the section -->
</div>
</div> <!-- closing the products section -->
<div class="container"> <!-- the second section - shopping cart -->
<span class="top-label">
<span class="label-txt">Shopping Cart</span> <!-- label for the section -->
</span>
<div class="content-area">
<div class="content drop-here"> <!-- content class, shared with the product section above, and the targeted drop-here class -->
<div id="cart-icon">
<img src="img/Shoppingcart_128x128.png" alt="shopping cart" class="pngfix" width="128" height="128" /> <!-- using the pngfix class -->
<img src="img/ajax_load_2.gif" alt="loading.." id="ajax-loader" width="16" height="16" /> <!-- the rotating gif - hidden by default and shown during ajax interactions -->
</div>
<form name="checkoutForm" method="post" action="order.php"> <!-- the form -->
<div id="item-list"> <!-- in this div we insert all the products in the shopping cart -->
</div>
</form> <!-- closing the form -->
<div class="clear"></div> <!-- clearing -->
<div id="total"></div> <!-- the total price goes here -->
<div class="clear"></div> <!-- clearing the floats -->
<a href="" onclick="document.forms.checkoutForm.submit(); return false;" class="button">Checkout</a> <!-- the submit button, hidden by default, notice the onclick attribute -->
</div>
</div>
<div class="bottom-container-border"> <!-- bottom part of the section -->
</div>
</div><!-- closing the main container -->
As you can see, we’ve organized our content into two main sections, which are almost identical in the XHTML markup they use – the first one, where all the products are displayed, and second one, acting as a shopping card.
Below you can see a detailed explanation of the structure of the product section.
Below you can see a detailed explanation of the structure of the product section.

The products are generated by our PHP code, as you can see on line 18. We are digging deeper into this in a few minutes. Now lets take a look at how we turned the XHTML layout into a finished design.
Step 3 – the CSS
This time the CSS code is quite long, so I’m going to split it into parts.
demo.css
demo.css
body,h1,h2,h3,p,td,quote,small,form,input,ul,li,ol,label{
/* resetting some of the styles for browser compatibility */
margin:0px;
padding:0px;
font-family:Arial, Helvetica, sans-serif;
}
body{
color:#555555;
font-size:13px;
background-color:#282828;
}
.clear{ /* implementing the clear-fix hack for fixing floated layouts */
clear:both;
}
#main-container{ /* this is the main container, holding the two sections */
width:700px;
margin:20px auto;
}
.container{ /* the main container of our content sections - the products and the shopping cart */
margin-bottom:40px;
}
.top-label{ /* the outer span comprising the label */
background:url(img/label_bg.png) no-repeat; /* showing the left part of label_bg.png - a wide round-cornered image */
display:inline-block;
margin-left:20px;
position:relative;
margin-bottom:-15px; /* the whole label is lowered on the product section */
}
.label-txt{ /* the inner span - red border in the illustration above */
background:url(img/label_bg.png) no-repeat top right; /* shows the right part of the wider label_bg.png */
display:inline-block;
font-size:10px;
height:36px;
margin-left:10px; /* leaves space on the left, so that the outer span can show its background */
padding:12px 15px 0 5px;
text-transform:uppercase;
}
.content-area{ /* The top part of the rounded container image, see the illustration above */
background:url(img/container_top.png) no-repeat #fcfcfc;
padding:15px 20px 0 20px;
}
.content{ /* shared by the two sections */
padding:10px;
}
.drag-desired{ /* individually targeted properties */
background:url(img/drag_desired_label.png) no-repeat top right;
padding:30px;
}
.drop-here{ /* not shared with the other sections */
background:url(img/drop_here_label.png) no-repeat top right;
}
.bottom-container-border{ /* the bottom rounded graphic, that completes the section */
background:url(img/container_bottom.png) no-repeat;
height:14px;
}
.product{ /* styling the products */
border:2px solid #F5F5F5;
float:left;
margin:15px;
padding:10px;
}
.product img{
cursor:move;
}
p.descr{
padding:5px 0;
}
small{
display:block;
margin-top:4px;
}
.tooltip{ /* the tooltips, this div is created by the simpletip plugin */
position: absolute;
top: 0;
left: 0;
z-index: 3;
display: none;
background-color:#666666;
border:1px solid #666666;
color:#fcfcfc;
padding:10px;
-moz-border-radius:12px; /* rounded corners */
-khtml-border-radius: 12px;
-webkit-border-radius: 12px;
border-radius:12px;
}
Now lets take a look at the styles that target the shopping cart section.
#cart-icon{ /* the div that contains the shopping cart icon */
width:128px;
float:left;
position:relative; /* changing the position to relative so that ajax-loader is positioned relative to it */
}
#ajax-loader{
position:absolute; /* absolute positioning makes an element relative to its parent if the latter has positioning different from the default */
top:0px;
left:0px;
visibility:hidden;
}
#item-list{ /* the contents of the shopping cart goes into this div */
float:left;
width:490px;
margin-left:20px;
padding-top:15px;
}
a.remove,a.remove:visited{ /* the REMOVE link */
color:red;
font-size:10px;
text-transform:uppercase;
}
#total{ /* the total cost div */
clear:both;
float:right;
font-size:10px;
font-weight:bold;
padding:10px 12px;
text-transform:uppercase;
}
#item-list table{ /* every product in the shopping cart is positioned inside the item-list div */
background-color:#F7F7F7;
border:1px solid #EFEFEF;
margin-top:5px;
padding:4px;
}
a.button,a.button:visited{ /* the CHECKOUT button */
display:none;
height:29px;
width:136px;
padding-top:15px;
margin:0 auto;
overflow:hidden;
color:white;
font-size:12px;
font-weight:bold;
text-align:center;
text-transform:uppercase;
background:url(img/button.png) no-repeat center top; /* showing only the top part of the background image */
}
a.button:hover{
background-position:bottom; /* on hover we show the bottom part of the image */
text-decoration:none;
}
/* Some less interesting classes */
a, a:visited {
color:#00BBFF;
text-decoration:none;
outline:none;
}
a:hover{
text-decoration:underline;
}
h1{
font-size:28px;
font-weight:bold;
font-family:"Trebuchet MS",Arial, Helvetica, sans-serif;
}
h2{
font-weight:normal;
font-size:20px;
color:#666666;
text-indent:30px;
margin:20px 0;
}
.tutorialzine h1{
color:white;
margin-bottom:10px;
font-size:48px;
}
.tutorialzine h3{
color:#F5F5F5;
font-size:10px;
font-weight:bold;
margin-bottom:30px;
text-transform:uppercase;
}
.tutorial-info{
color:white;
text-align:center;
padding:10px;
margin-top:-20px;
}
As any front-end developer will tell you, we have something missing here. You guessed it – special treatment for IE6.
I personally plan to stop supporting IE6 soon in my projects altogether – if it wasn’t for it, the above code would have been at least a quarter shorter, and it would have taken a lot less time to debug.
But anyway, here is how we target IE6 specifically:
demo.php
I personally plan to stop supporting IE6 soon in my projects altogether – if it wasn’t for it, the above code would have been at least a quarter shorter, and it would have taken a lot less time to debug.
But anyway, here is how we target IE6 specifically:
demo.php
There. Now lets take a look at the PHP backend.

Click Next to Readmore.....
Step 4 – PHP
We use PHP in a number of ways and places. First lets look at how the product list is generated on the main page.
demo.php
demo.php
$result = mysql_query("SELECT * FROM internet_shop"); // selecting all the products
while($row=mysql_fetch_assoc($result))
{
echo '<div class="product"><img src="img/products/'.$row['img'].'" alt="'.htmlspecialchars($row['name']).'" width="128" height="128" class="pngfix" /></div>';
}
Another place where we use PHP is in tips.php, which takes an image file name as a parameter, checks which product is associated with that image, and outputs the tool tip data as html. This is later used by the simpletip plugin.
ajax/tips.php
ajax/tips.php
define('INCLUDE_CHECK',1);require "../connect.php";
if(!$_POST['img']) die("There is no such product!");
$img=mysql_real_escape_string(end(explode('/',$_POST['img'])));
$row=mysql_fetch_assoc(mysql_query("SELECT * FROM internet_shop WHERE img='".$img."'"));
if(!$row) die("There is no such product!");
echo '<strong>'.$row['name'].'</strong>
<p class="descr">'.$row['description'].'</p>
<strong>price: $'.$row['price'].'</strong>
<small>Drag it to your shopping cart to purchase it</small>';
We also use PHP to return the necessary data for the addition of products in the shopping cart. The difference is that this time we return the data as JSON (a javascript object).
ajax/addtocart.php
ajax/addtocart.php
define('INCLUDE_CHECK',1);require "../connect.php";
if(!$_POST['img']) die("There is no such product!");
$img=mysql_real_escape_string(end(explode('/',$_POST['img'])));$row=mysql_fetch_assoc(mysql_query("SELECT * FROM internet_shop WHERE img='".$img."'"));
echo '{status:1,id:'.$row['id'].',price:'.$row['price'].',txt:\'\
\
<table width="100%" id="table_'.$row['id'].'">\
<tr>\
<td width="60%">'.$row['name'].'</td>\
<td width="10%">$'.$row['price'].'</td>\
<td width="15%"><select name="'.$row['id'].'_cnt" id="'.$row['id'].'_cnt" onchange="change('.$row['id'].');">\
<option value="1">1</option>\
<option value="2">2</option>\
<option value="3">3</option></slect>\
\
</td>\
<td width="15%"><a href="#" onclick="remove('.$row['id'].');return false;" class="remove">remove</a></td>\
</tr>\
</table>\'}';
The outputted object has status, id, price and txt properties. These are used by our AJAX functions as you’ll see in a moment.
Notice how I’ve escaped each line of the string with a backslash. This is done because javascript does not support multiline strings.
The last place where we use PHP is in order.php, which is used to process the orders. Currently, it just outputs your order. You could modify it to include an email form, paypal functionality, or anything that will make it into a functional online shop.
order.php
Notice how I’ve escaped each line of the string with a backslash. This is done because javascript does not support multiline strings.
The last place where we use PHP is in order.php, which is used to process the orders. Currently, it just outputs your order. You could modify it to include an email form, paypal functionality, or anything that will make it into a functional online shop.
order.php
<?php
define('INCLUDE_CHECK',1);require "connect.php";
if(!$_POST) // if there is no data submitted to the form
{
if($_SERVER['HTTP_REFERER']) // redirect
header('Location : '.$_SERVER['HTTP_REFERER']);
exit; // and exit
}
?>
<!-- XHTML code.. -->
<?php
$cnt = array();$products = array();
foreach($_POST as $key=>$value)
{
$key=(int)str_replace('_cnt','',$key);
$products[]=$key; // store the product ids in an array
$cnt[$key]=$value; // create a key / value pair where for each product id there is a corresponding value being the number of products
purchased}
$result = mysql_query("SELECT * FROM internet_shop WHERE id IN(".join($products,',').")"); // selecting all the products with the IN() function
if(!mysql_num_rows($result)) // no products found
{
echo '<h1>There was an error with your order!</h1>';
}else
{
echo '<h1>You ordered:</h1>';
while($row=mysql_fetch_assoc($result))
{
echo '<h2>'.$cnt[$row['id']].' x '.$row['name'].'</h2>';
$total+=$cnt[$row['id']]*$row['price'];
}
echo '<h1>Total: $'.$total.'</h1>';
}
?>
This concludes the PHP section. The only thing that is left is some jQuery magic.

Step 5 – jQuery
We will utilize jQuery to the fullest, so we will need to include the additional jQuery UI library as well as the basic library.
<script type="text/javascript" src="http://ajax.googleapis.com/ajax/libs/jquery/1.3.2/jquery.min.js"></script>
<script type="text/javascript" src="http://ajax.googleapis.com/ajax/libs/jqueryui/1.7.2/jquery-ui.min.js"></script>
<script type="text/javascript" src="simpletip/jquery.simpletip-1.3.1.pack.js"></script> <!-- the jQuery simpletip plugin -->
<script type="text/javascript" src="script.js"></script> <!-- our script.js file -->
Now we can continue with our script.
script.js
script.js
var purchased=new Array(); //an array containing all the products we've purchased so far
var totalprice=0; //the total price
$(document).ready(function(){
$('.product').simpletip({ //using the simpletip plugin
offset:[40,0],
content:'<img style="margin:10px;" src="img/ajax_load.gif" alt="loading" />', // default content
onShow: function(){
var param = this.getParent().find('img').attr('src');
// fix for IE6
if($.browser.msie && $.browser.version=='6.0')
{
param = this.getParent().find('img').attr('style').match(/src=\"([^\"]+)\"/);
param = param[1];
}
// after the tooltip is shown, load the tips.php file and pass the image name as a parameter
this.load('ajax/tips.php',{img:param});
}
});
$(".product img").draggable({ // enable all product images to be dragged
containment: 'document',
opacity: 0.6,
revert: 'invalid',
helper: 'clone',
zIndex: 100
});
$("div.content.drop-here").droppable({ // convert the shopping cart to a droppable
drop:
function(e, ui)
{
var param = $(ui.draggable).attr('src');
// IE6 fix
if($.browser.msie && $.browser.version=='6.0')
{
param = $(ui.draggable).attr('style').match(/src=\"([^\"]+)\"/);
param = param[1];
}
addlist(param); // the special addlist function - see below
}
});
});
Below is the second part of script.js.
function addlist(param){
// the addlist function ads a product to the shopping cart
$.ajax({ // sending an ajax request to addtocart.php
type: "POST",
url: "ajax/addtocart.php",
data: 'img='+encodeURIComponent(param), // the product image as a parameter
dataType: 'json', // expecting json
beforeSend: function(x){$('#ajax-loader').css('visibility','visible');}, // showing the loading gif
success: function(msg){
$('#ajax-loader').css('visibility','hidden'); // hiding the loading gif animation
if(parseInt(msg.status)!=1)
{
return false; // if there has been an error, return false
}
else
{
var check=false;
var cnt = false;
for(var i=0; i<purchased.length;i++)
{
if(purchased[i].id==msg.id) // find if we have already bought this prduct
{
check=true;
cnt=purchased[i].cnt;
break;
}
}
if(!cnt) // if we haven't bought it yet, or we have removed it from the purchases, we insert it in the shopping cart
$('#item-list').append(msg.txt);
if(!check) // if we haven't bought it yet, insert it in the purchased array
{
purchased.push({id:msg.id,cnt:1,price:msg.price});
}
else // else if we've bought it
{
if(cnt>=3) return false; // 3 products of type max
purchased[i].cnt++;
$('#'+msg.id+'_cnt').val(purchased[i].cnt); // update the select box
}
totalprice+=msg.price; // recalculate the price
update_total(); // update the total div
}
$('.tooltip').hide(); // hiding the tooltip (sometimes it stays on screen after the drag)
}
});
}
function findpos(id) // a helper function that finds the position at which the product is inserted in the array, returns the position{
for(var i=0; i<purchased.length;i++)
{
if(purchased[i].id==id)
return i;
}
return false;
}
function remove(id) // remove a product from the shopping cart{
var i=findpos(id); // find its position in the array
totalprice-=purchased[i].price*purchased[i].cnt; // recalculate the price
purchased[i].cnt = 0; // reset the counter
$('#table_'+id).remove(); // remove it from the cart
update_total(); // update the total price counter on the page
}
function change(id) // evoked when we change the number of products via the select area{
var i=findpos(id);
totalprice+=(parseInt($('#'+id+'_cnt').val())-purchased[i].cnt)*purchased[i].price;
purchased[i].cnt=parseInt($('#'+id+'_cnt').val());
update_total();
}
function update_total() // function that updates the total price div on the page{
if(totalprice)
{
$('#total').html('total: $'+totalprice); // if we've bought somehitng, show the total price div and the purchase button
$('a.button').css('display','block');
}
else // hide them
{
$('#total').html('');
$('a.button').hide();
}
}
There are a few places in this code, where we use an id for pointing to a product. The id is a unique identifier, that is assigned by the MySQL database once we insert a new item.
It is passed by the AJAX requests and we need to translate it to the index position of our products array in order to use it, which is the purpose of the findpos() function.
With this our shopping cart is complete!
It is passed by the AJAX requests and we need to translate it to the index position of our products array in order to use it, which is the purpose of the findpos() function.
With this our shopping cart is complete!
Conclusion
Today we made a functional AJAX driven shopping cart. You are free to download the code and modify it any way you wish. You can build upon it and even turn it into a full fledged online shop.
An AJAX Based Shopping Cart with Drap Drop Item Effect
Reviewed by Sanaulllah Rais
on
13:55
Rating:

No comments: