Related
I'm having a bit of a weird problem and there are now 2 people in my office completely stumped by this (so I hope it's not something embarrassingly obvious!).
As an overview, we have a SQLite database that could contain any number of tables, each with any number of rows or columns. We want to display this data in a page in an Electron app.
We have an Angular page in our Electron app (with Express/Node as a backend) which makes some calls to an Express endpoint (which gets data from the SQLite db) and we end up with two 2D arrays of data - one for the table headers ($scope.tableHeaders) and one for the table content ($scope.documentsList).
The code for the calls is below:
$http.get('http://localhost:3000/tableCount').then(function (res) {
if (res.data.numberoftables) {
$scope.count = res.data.numberoftables;
var i;
$scope.documentsList = new Array();
$scope.tableHeaders = new Array();
for (i = 1; i <= $scope.count; i++) {
var tableNo = i;
//loop through tables
***$http.get('http://localhost:3000/doclist/' + tableNo).then(function (resDocs) {
//get table content
if (resDocs.data) {
***$scope.documentsList.push(resDocs.data);
}
});
$http.get('http://localhost:3000/tableColumns/' + tableNo).then(function (resHeads) {
//get table headers
if (resHeads.data) {
$scope.tableHeaders.push(resHeads.data);
}
});
}
}
});
Just for fun, here's our HTML:
<div id="documentTables">
<div ng-repeat="tableID in getNumberArray(count)">
<table st-table="documentsList[tableID]" class="table table-condensed table-hover">
<thead>
<tr st-safe-src="tableHeaders[tableID]">
<th ng-repeat="col in tableHeaders[tableID]">
{{col}}
</th>
</tr>
<tr st-safe-src="tableHeaders[tableID]" colspan="{{tableHeaders[tableID].length}}">
<th colspan="1" ng-repeat="col in tableHeaders[tableID]">
<input id="{{col + 'searchbox' + tableID}}" st-search="col" placeholder="{{col}}" class="input-sm form-control" type="search" />
</th>
</tr>
</thead>
<tbody>
<tr st-safe-src="documentsList[tableID]" ng-repeat="document in documentsList[tableID]">
<td ng-repeat="col in tableHeaders[tableID]">
{{document[col].value}}
</td>
</tr>
</tbody>
<tfoot>
<tr>
<td colspan="{{tableHeaders[tableID].length}}" class="text-center">
<div st-pagination="" st-items-by-page="10" st-displayed-pages="7"></div>
</td>
</tr>
</tfoot>
</table>
</div>
</div>
The problem we're coming across is that no actual data is displayed in the tables. Somewhere between the starred lines (***) in the JS above, an empty array for each table is added. So if we have two tables in our database, we end up with documentsList = Array[4] with documentsList[0] and documentsList[1] being empty arrays. Why/where would this be happening, and how can I fix it?
Note - if you'd like to test this out without our endpoints, try out these variables:
$scope.count = 2;
$scope.tableHeaders = [["TabNo", "Document", "PageNumber", "Date"],["TabNo", "Document", "PageNumber"]];
$scope.documentsList = [];
$scope.documentsList.push(JSON.parse('[{"idANX":1,"pathANX":"Product Overview.pdf","isHeaderANX":1,"isExcludedANX":0,"isNotHyperlinkedANX":0,"isSubHeaderANX":0,"HyperlinkedColumnANX":1,"TabNo":{"colspan":0,"rowspan":0,"hyperlinkedDoc":"","isLastCell":false,"value":"1"},"Document":{"colspan":0,"rowspan":0,"hyperlinkedDoc":"Product Overview.pdf","isLastCell":false,"value":"Product Overview"},"PageNumber":{"colspan":0,"rowspan":0,"hyperlinkedDoc":"","isLastCell":false,"value":"1 - 2"},"Date":{"colspan":0,"rowspan":0,"hyperlinkedDoc":"","isLastCell":true,"value":"18 August 2015"}},{"idANX":2,"pathANX":"Spec.pdf","isHeaderANX":0,"isExcludedANX":0,"isNotHyperlinkedANX":0,"isSubHeaderANX":0,"HyperlinkedColumnANX":1,"TabNo":{"colspan":0,"rowspan":0,"hyperlinkedDoc":"","isLastCell":false,"value":"2"},"Document":{"colspan":0,"rowspan":0,"hyperlinkedDoc":"Spec.pdf","isLastCell":false,"value":"Spec"},"PageNumber":{"colspan":0,"rowspan":0,"hyperlinkedDoc":"","isLastCell":false,"value":"3 - 4"},"Date":{"colspan":0,"rowspan":0,"hyperlinkedDoc":"","isLastCell":true,"value":"1 April 2015"}}]');
$scope.documentsList.push(JSON.parse('[{"idANX":1,"pathANX":"Product Overview.pdf","isHeaderANX":0,"isExcludedANX":0,"isNotHyperlinkedANX":0,"isSubHeaderANX":0,"HyperlinkedColumnANX":1,"TabNo":{"colspan":0,"rowspan":0,"hyperlinkedDoc":"","isLastCell":false,"value":"1"},"Document":{"colspan":0,"rowspan":0,"hyperlinkedDoc":"Product Overview.pdf","isLastCell":false,"value":"Product Overview"},"PageNumber":{"colspan":0,"rowspan":0,"hyperlinkedDoc":"","isLastCell":false,"value":"1 - 2"}},{"idANX":2,"pathANX":"Spec.pdf","isHeaderANX":0,"isExcludedANX":0,"isNotHyperlinkedANX":0,"isSubHeaderANX":0,"HyperlinkedColumnANX":1,"TabNo":{"colspan":0,"rowspan":0,"hyperlinkedDoc":"","isLastCell":false,"value":"2"},"Document":{"colspan":0,"rowspan":0,"hyperlinkedDoc":"Spec.pdf","isLastCell":false,"value":"Spec"},"PageNumber":{"colspan":0,"rowspan":0,"hyperlinkedDoc":"","isLastCell":false,"value":"3 - 4"}}]');
$scope.getNumberArray = function (num) {
var n = new Array(num);
var i;
for (i = 0; i < num; i++) {
n[i] = i;
}
return n;
}
$scope.rowClass = function (row) {
if (row.isSubHeaderANX == 1) {
return 'subheader';
} else if (row.isHeaderANX == 1) {
return 'header';
}
else { return 'doctablerow'; }
}
Also pagination and filtering don't work but that's an issue for when our table actually contains data.
Edit: Probably should have mentioned that this is using angular-smart-table
You should take a look at how asynchronous requests and promises works. To make your code run you could do this: var promises = []; // Creates an array to store the promises
$http.get('http://localhost:3000/tableCount').then(function (res) {
if (res.data.numberoftables) {
$scope.count = res.data.numberoftables;
var i;
$scope.documentsList = new Array();
$scope.tableHeaders = new Array();
var promises = [];
for (i = 1; i <= $scope.count; i++) {
var tableNo = i;
$scope.getHttp(i).then(function(resp){
$scope.documentsList.push(resolutions[0]);
$scope.tableHeaders.push(resolutions[1]);
});
}
}
});
$scope.getHttp = function(tableNo){
var promise;
promise = $http({
url:'http://localhost:3000/doclist/' + tableNo,
method:'get'
});
promises.push(promise);
promise = $http({
url:'http://localhost:3000/tableColumns/' + tableNo,
method:'get'
});
promises.push(promise);
return $q.all(promises);
}
Make the changes accordingly. I did not test it because dont have complete controller.But for sure, if you are looping http, make sure promises are stored.
I have the following HTML table:
<table id="review-total">
<tbody><tr class="wlp">
<td class="left-cell">WLP Total</td>
<td>199.00</td>
</tr>
<tr class="tax">
<td class="left-cell">GST</td>
<td>19.90</td>
</tr>
<tr class="net">
<td class="left-cell">Order Total</td>
<td class="net-price">$218.90</td>
</tr>
</tbody>
</table>
I'm trying to loop through this table and retrieve the values i.e
199.00, 19.90 and $218.90 I have the following code:
var reviewTotal = document.getElementById('review-total');
for (var i = 1; i < reviewTotal.rows.length; i++) {
if (reviewTotal.rows[i].cells.length) {
wlpTotal = (reviewTotal.rows[i].cells[1].textContent.trim());
gstAmount = (reviewTotal.rows[i].cells[3].textContent.trim());
totalOrderAmount = (reviewTotal.rows[i].cells[5].textContent.trim());
}
}
I'm having a small issue trying to retrieve those values specified above, at present the error I get is textContent is undefined.
Can someone show me how I should go about retrieving those values, unfortunately I'm not strong in Javascript.
You have 3 rows and each row has only 2 cells. The 3 and 5 indices are undefined and undefined doesn't have .textContent property.
If you want to store the values by using specific variable names, you remove the loop and select the target elements manually:
var wlpTotal = reviewTotal.rows[0].cells[1].textContent.trim();
var gstAmount = reviewTotal.rows[1].cells[1].textContent.trim();
var totalOrderAmount = reviewTotal.rows[2].cells[1].textContent.trim();
If you want to store the values in an array, you can code:
var values = [].map.call(reviewTotal.rows, function(row) {
return row.cells[1].textContent.trim();
});
By using ES2015's Destructuring Assignment you can also extract the array's elements:
var [wlpTotal, gstAmount, totalOrderAmount] = values;
First:the index start the 0 either row or cell.
Secend:get value in the tag to use innerText or innerHTML ,The code following:
var reviewTotal = document.getElementById('review-total');
for (var i = 0; i < reviewTotal.rows.length; i++)
{
if (reviewTotal.rows[i].cells.length>1)
{
wlpTotal = (reviewTotal.rows[i].cells[1].innerText);
}
}
I'm trying to generate a table using properties of two viewmodels, which are sub viewmodels of the main viewmodel, which ko.applyBindings() is called using.
The idea is to generate a row for each element in SubVM1 where the first cell is the element's name. Then for every element in SubVM2, an additional cell is added to the row.
The rows generate correctly and the first cell shows the SubVM1 name, but it is only followed by one cell, instead of however many elements are in SubVM2.
Also, the function in the data-bind doesn't work either. I've tried declaring the Value function as a prototype of SubV2 but it errors out as undefined.
Regardless, something I'm not sure about is clearly happening with the binding context, and help would be appreciated.
<tbody data-bind="foreach: {data: SubViewModel1, as: 'SubVM1'}">
<tr>
<td data-bind="text: SubVM1.Name" />
<!-- ko foreach: {data: $root.SubViewModel2, as: 'SubVM2'} -->
<td data-bind="text: Value(SubVM1.Type)"></td>
<!-- /ko -->
</tr>
</tbody>
Edit: Partially done jsfiddle: http://jsfiddle.net/jgr71o8t/1
There are a couple of things that I could see.
The first one is that the <td data-bind='' />.
Knockout does not generally like self closed tags. Always use the closing tag, in this case <td data-bind=''></td>
The second is that anything you want updated on the screen should be an ko.observable or ko.observableArray. any changes to properties after ko.applyBindings will not be reflected on the screen
HTML
<table border="1">
<tbody data-bind="foreach: {data: subViewModel1, as: 'SubVM1'}">
<tr>
<td data-bind="text: name"></td>
<!-- ko foreach: {data: $root.subViewModel2, as: 'SubVM2'} -->
<td data-bind="text: SubVM2.Value(SubVM1.Type)"></td>
<!-- /ko -->
</tr>
</tbody>
</table>
JS Fiddle Demo with knockout bindings on all properties
function MasterViewModel() {
var self = this;
self.subViewModel1 = ko.observableArray([]);
self.subViewModel2 = ko.observableArray([]);
}
function SubVM1ViewModel() {
var self = this;
self.name = ko.observable("Sub View Model 1");
self.otherProperty = ko.observable(43);
}
function SubVM2ViewModel() {
var self = this;
self.title = ko.observable('Sub View Model 2');
self.subVM1List = ko.observableArray([]);
}
SubVM2ViewModel.prototype.Value = function (type) {
for (var i = 0; i < this.subVM1List().length; i++) {
if (this.subVM1List()[i].Type === type) {
return this.subVM1List()[i].name();
}
}
};
var masterVM = new MasterViewModel();
var subVM2 = new SubVM2ViewModel();
subVM2.subVM1List.push(new SubVM1ViewModel());
masterVM.subViewModel1.push(new SubVM1ViewModel());
masterVM.subViewModel2.push(subVM2);
ko.applyBindings(masterVM);
JS Fiddle Demo with straight javascript properties
function MasterViewModel() {
var self = this;
self.subViewModel1 = [];
self.subViewModel2 = [];
}
function SubVM1ViewModel() {
var self = this;
self.name = "Sub View Model 1";
self.otherProperty =43;
}
function SubVM2ViewModel() {
var self = this;
self.title = 'Sub View Model 2';
self.subVM1List = [];
}
SubVM2ViewModel.prototype.Value = function (type) {
for (var i = 0; i < this.subVM1List.length; i++) {
if (this.subVM1List[i].Type === type) {
return this.subVM1List[i].name;
}
}
};
var masterVM = new MasterViewModel();
var subVM2 = new SubVM2ViewModel();
subVM2.subVM1List.push(new SubVM1ViewModel());
masterVM.subViewModel1.push(new SubVM1ViewModel());
masterVM.subViewModel2.push(subVM2);
ko.applyBindings(masterVM);
I'm using Josh Fraser's Backwards compatible window.postMessage() (http://www.onlineaspect.com/2010/01/15/backwards-compatible-postmessage) and I'm having trouble.
I have 2 iframes on a page and both iframes send different data to the same parent page. In other words, there are 2 XD.receivemessage functions getting 2 different messages from 2 different iframes. Here's a shortened version of my code:
<iframe id="IFRAME1" src="https://www.DOMAIN.com/PAGENAMEFIRST.php"></iframe>
<iframe id="IFRAME2" src="https://www.DOMAIN.com/PAGENAMESECOND.php"></iframe>
<div id="firstTable">
<table>
<tr>
<th>Name</th>
<th>Address</th>
<th>ID</th>
</tr>
<tr>
<td><!--this will be filled in by the data--></td>
<td><!--this will be filled in by the data--></td>
<td><!--this will be filled in by the data--></td>
</tr>
</table>
</div>
<div id="secondTable">
<table>
<tr>
<th>Email</th>
<th>Twitter Handle</th>
<th>ID</th>
</tr>
<tr>
<td><!--this will be filled in by the data--></td>
<td><!--this will be filled in by the data--></td>
<td><!--this will be filled in by the data--></td>
</tr>
</table>
</div>
<script type="text/javascript">
$(document).ready(function() {
//FIRST DATA PULL
var encodeUriFirst = encodeURIComponent(document.location.href);
var getMessageFirst = 'https://www.DOMAIN.com/PAGENAMEFIRST.php#' + encodeUriFirst;
$("#IFRAME1").attr('src',getMessageFirst);
XD.receiveMessage(function(getMessageFirst){
var getDataFirst = getMessageFirst.data;
var getDataFirstEach = new Array();
for(var a=0; a<getDataFirst.length; a++){
getDataFirstEach = getDataFirst.split("~,~");
$('div#firstTable table td:nth-child(0)').text(getDataFirstEach[0].replace(/[~]/g,""));
}
}, 'https://www.DOMAIN.com');
//SECOND DATA PULL
var encodeUriSecond = encodeURIComponent(document.location.href);
var getMessageSecond = '//www.DOMAIN.com/PAGENAMESECOND.php#' + encodeUriSecond;
$("#IFRAME2").attr('src',getMessageSecond);
XD.receiveMessage(function(getMessageSecond){
var getDataSecond = getMessageSecond.data;
var getDataSecondEach = new Array();
for(var a=0; a<getDataFirst.length; a++){
getDataSecondEach = getDataSecond.split("~,~");
$('div#secondTable table td:nth-child(0)').text(getDataSecondEach[0].replace(/[~]/g,""));
}
}, 'https://www.DOMAIN.com');
});
</script>
Please keep in mind that I've scaled back the code significantly. Each getDataFirst or getDataSecond
It actually works as far as getting the data from both iframes (send/receive both work). My data comes through in the format ~name~,~address~,~ID~ for the first set of data and ~email~,~twitter~,~ID~ for the second set.
I'm trying to get the data to populate the 2 tables. The For loops look for the ID in the table and if the ID matches, it fills in the other fields associated with that ID. In some cases, the ID from the first data set will be the same as the ID from the second data set. When that happens, the for loops place the data in both tables instead of the appropriate one. I'm wondering why this would happen since I'm specifically targeting the table inside either $('div#firstTable') or $('div#secondTable') for the output. For example:
$('div#firstTable table td:nth-child(0)').text(getDataFirstEach[0]);
inside the For loop is placing getDataFirstEach[0] into both the firstTable and secondTable tables?
Can anyone tell me why?
AS REQUESTED, HERE IS THE FULL JS:
var encodeUriGifts = encodeURIComponent(document.location.href);
var giftsMessage = '//www.DOMAIN.org/firstpage.php#' + encodeUriGifts;
$("#IFRAME1").attr('src',giftsMessage);
XD.receiveMessage(function(giftsMessage){
var getDataGifts = giftsMessage.data;
var currentRecordGifts = new Array();
var getGiftsLines = getDataGifts.split("|"); //LINES OF DATA ARE PIPE SEPARATED
for(var bCnt=0;bCnt < getGiftsLines.length;bCnt++){
var getGiftsEach = getGiftsLines[bCnt].split("`,`"); //EACH ITEM IS SEPARATED BY `,`
for(var cCnt=0;cCnt < getGiftsEach.length;cCnt++){
if (getGiftsEach[cCnt]!=="" && getGiftsEach[cCnt]!=="undefined"){
currentRecordGifts[bCnt] = " <td class='giftamount'>"+getGiftsEach[1]+"</td><td class='giftdate'>"+getGiftsEach[2].replace(/[`]/g,"")+"</td>";
}
if (cCnt==2) {
var thisGiftsID = getGiftsEach[0].replace(/[`]/g,"");
$('#firstTable table td:contains("'+thisGiftsID+'")').closest('tr').find('td:nth-child(3)').after(currentRecordGifts[bCnt]);
}
}
if (bCnt==0){
$('#firstTable table th:nth-child(3)').after('<th class="BBListingHeading DirectoryListingHeading" scope="col" style="white-space: nowrap;">Last Gift Amount</th><th class="BBListingHeading DirectoryListingHeading" scope="col" style="white-space: nowrap;">Last Gift Date</th>');
}
}
}, 'https://www.DOMAIN.org');
var encodeUriChanges = encodeURIComponent(document.location.href);
var changesMessage = '//www.DOMAIN.org/secondpage.php#' + encodeUriChanges;
$("#IFRAME2").attr('src',changesMessage);
XD.receiveMessage(function(changesMessage){
var getDataChanges = changesMessage.data;
var currentRecordChanges = new Array();
var getChangesLines = getDataChanges.split("|");
$('#secondTable table tr td:nth-child(3)').each( function() {
$('#secondTable table tr td:nth-child(3)').after('<td class="BBListingItem DirectoryListingItem" style="white-space: nowrap;"><ul class="changes"></ul></td>');
});
for(var dCnt=0;dCnt < getChangesLines.length;dCnt++){
var getChangesEach = getChangesLines[dCnt].split("`,`");
for(var eCnt=0;eCnt < getChangesEach.length;eCnt++){
if (getChangesEach[eCnt]!=="" && getChangesEach[eCnt]!=="undefined"){
currentRecordChanges[dCnt] = "<li class='change'>"+getChangesEach[2]+" ("+getChangesEach[1].replace(/[`]/g,"")+")</li>";
}
if (eCnt==2) {
var thisChangesID = getChangesEach[0].replace(/[`]/g,"");
$('#secondTable table td:contains("'+thisChangesID+'")').closest('tr').find('td ul.changes').append(currentRecordChanges[dCnt]);
}
if (dCnt==0){
$('#changesdiv .DirectoryListingTable th:nth-child(3)').after('<th class="BBListingHeading DirectoryListingHeading" scope="col" style="white-space: nowrap;">Change Details</th>');
}
}
}
}, 'https://www.DOMAIN.org');
I'm not sure what to do in order to complete this project. I need to create a shopping cart that uses only one HTML page. I have the table set up showing what is being sold but where I am lost is the JavaScript.
I don't know how to link the "Add to Cart" button with all the necessary data( The name, description, and price) to be able to add it to the cart. I don't need to be able to remove it from the cart, but it does need to show the total. After searching online for a few answers, I've tried some things but just cannot figure it out.
Any help is definitely appreciated because I am completely lost at this point and am new to JavaScript in general.
This is the jsFiddle but that was a little confusing to me, because it's working differently on there than if I just went to Run in Notepad++
jsFiddle: http://jsfiddle.net/renavi/ATjvt/5/
function AddtoCart() {
console.log('hi');
var x = document.getElementById('Items');
var new_row = x.rows[1].cloneNode(true);
var len = x.rows.length;
new_row.cells[0].innerHTML = len;
var inp1 = new_row.cells[1].getElementsByTagName('input')[0];
inp1.id += len;
inp1.value = '';
var inp2 = new_row.cells[2].getElementsByTagName('input')[0];
inp2.id += len;
inp2.value = '';
x.appendChild(new_row);
}
The direct file is here
Pastebin: http://pastebin.com/sutGWjSY
You simply need to use simpleCart
It is a free and open-source javascript shopping cart that easily integrates with your current website.
You will get the full source code at github
For a project this size, you should stop writing pure JavaScript and turn to some of the libraries available. I'd recommend jQuery (http://jquery.com/), which allows you to select elements by css-selectors, which I recon should speed up your development quite a bit.
Example of your code then becomes;
function AddtoCart() {
var len = $("#Items tr").length, $row, $inp1, $inp2, $cells;
$row = $("#Items td:first").clone(true);
$cells = $row.find("td");
$cells.get(0).html( len );
$inp1 = $cells.get(1).find("input:first");
$inp1.attr("id", $inp1.attr("id") + len).val("");
$inp2 = $cells.get(2).find("input:first");
$inp2.attr("id", $inp2.attr("id") + len).val("");
$("#Items").append($row);
}
I can see that you might not understand that code yet, but take a look at jQuery, it's easy to learn and will make this development way faster.
I would use the libraries already created specifically for js shopping carts if I were you though.
To your problem; If i look at your jsFiddle, it doesn't even seem like you have defined a table with the id Items? Maybe that's why it doesn't work?
I think it is a better idea to start working with a raw data and then translate it to DOM (document object model)
I would suggest you to work with array of objects and then output it to the DOM in order to accomplish your task.
You can see working example of following code at http://www.softxml.com/stackoverflow/shoppingCart.htm
You can try following approach:
//create array that will hold all ordered products
var shoppingCart = [];
//this function manipulates DOM and displays content of our shopping cart
function displayShoppingCart(){
var orderedProductsTblBody=document.getElementById("orderedProductsTblBody");
//ensure we delete all previously added rows from ordered products table
while(orderedProductsTblBody.rows.length>0) {
orderedProductsTblBody.deleteRow(0);
}
//variable to hold total price of shopping cart
var cart_total_price=0;
//iterate over array of objects
for(var product in shoppingCart){
//add new row
var row=orderedProductsTblBody.insertRow();
//create three cells for product properties
var cellName = row.insertCell(0);
var cellDescription = row.insertCell(1);
var cellPrice = row.insertCell(2);
cellPrice.align="right";
//fill cells with values from current product object of our array
cellName.innerHTML = shoppingCart[product].Name;
cellDescription.innerHTML = shoppingCart[product].Description;
cellPrice.innerHTML = shoppingCart[product].Price;
cart_total_price+=shoppingCart[product].Price;
}
//fill total cost of our shopping cart
document.getElementById("cart_total").innerHTML=cart_total_price;
}
function AddtoCart(name,description,price){
//Below we create JavaScript Object that will hold three properties you have mentioned: Name,Description and Price
var singleProduct = {};
//Fill the product object with data
singleProduct.Name=name;
singleProduct.Description=description;
singleProduct.Price=price;
//Add newly created product to our shopping cart
shoppingCart.push(singleProduct);
//call display function to show on screen
displayShoppingCart();
}
//Add some products to our shopping cart via code or you can create a button with onclick event
//AddtoCart("Table","Big red table",50);
//AddtoCart("Door","Big yellow door",150);
//AddtoCart("Car","Ferrari S23",150000);
<table cellpadding="4" cellspacing="4" border="1">
<tr>
<td valign="top">
<table cellpadding="4" cellspacing="4" border="0">
<thead>
<tr>
<td colspan="2">
Products for sale
</td>
</tr>
</thead>
<tbody>
<tr>
<td>
Table
</td>
<td>
<input type="button" value="Add to cart" onclick="AddtoCart('Table','Big red table',50)"/>
</td>
</tr>
<tr>
<td>
Door
</td>
<td>
<input type="button" value="Add to cart" onclick="AddtoCart('Door','Yellow Door',150)"/>
</td>
</tr>
<tr>
<td>
Car
</td>
<td>
<input type="button" value="Add to cart" onclick="AddtoCart('Ferrari','Ferrari S234',150000)"/>
</td>
</tr>
</tbody>
</table>
</td>
<td valign="top">
<table cellpadding="4" cellspacing="4" border="1" id="orderedProductsTbl">
<thead>
<tr>
<td>
Name
</td>
<td>
Description
</td>
<td>
Price
</td>
</tr>
</thead>
<tbody id="orderedProductsTblBody">
</tbody>
<tfoot>
<tr>
<td colspan="3" align="right" id="cart_total">
</td>
</tr>
</tfoot>
</table>
</td>
</tr>
</table>
Please have a look at following free client-side shopping cart:
SoftEcart(js) is a Responsive, Handlebars & JSON based, E-Commerce shopping cart written in JavaScript with built-in PayPal integration.
Documentation
http://www.softxml.com/softecartjs-demo/documentation/SoftecartJS_free.html
Hope you will find it useful.
Here's a one page cart written in Javascript with localStorage. Here's a full working pen. Previously found on Codebox
cart.js
var cart = {
// (A) PROPERTIES
hPdt : null, // HTML products list
hItems : null, // HTML current cart
items : {}, // Current items in cart
// (B) LOCALSTORAGE CART
// (B1) SAVE CURRENT CART INTO LOCALSTORAGE
save : function () {
localStorage.setItem("cart", JSON.stringify(cart.items));
},
// (B2) LOAD CART FROM LOCALSTORAGE
load : function () {
cart.items = localStorage.getItem("cart");
if (cart.items == null) { cart.items = {}; }
else { cart.items = JSON.parse(cart.items); }
},
// (B3) EMPTY ENTIRE CART
nuke : function () {
if (confirm("Empty cart?")) {
cart.items = {};
localStorage.removeItem("cart");
cart.list();
}
},
// (C) INITIALIZE
init : function () {
// (C1) GET HTML ELEMENTS
cart.hPdt = document.getElementById("cart-products");
cart.hItems = document.getElementById("cart-items");
// (C2) DRAW PRODUCTS LIST
cart.hPdt.innerHTML = "";
let p, item, part;
for (let id in products) {
// WRAPPER
p = products[id];
item = document.createElement("div");
item.className = "p-item";
cart.hPdt.appendChild(item);
// PRODUCT IMAGE
part = document.createElement("img");
part.src = "images/" +p.img;
part.className = "p-img";
item.appendChild(part);
// PRODUCT NAME
part = document.createElement("div");
part.innerHTML = p.name;
part.className = "p-name";
item.appendChild(part);
// PRODUCT DESCRIPTION
part = document.createElement("div");
part.innerHTML = p.desc;
part.className = "p-desc";
item.appendChild(part);
// PRODUCT PRICE
part = document.createElement("div");
part.innerHTML = "$" + p.price;
part.className = "p-price";
item.appendChild(part);
// ADD TO CART
part = document.createElement("input");
part.type = "button";
part.value = "Add to Cart";
part.className = "cart p-add";
part.onclick = cart.add;
part.dataset.id = id;
item.appendChild(part);
}
// (C3) LOAD CART FROM PREVIOUS SESSION
cart.load();
// (C4) LIST CURRENT CART ITEMS
cart.list();
},
// (D) LIST CURRENT CART ITEMS (IN HTML)
list : function () {
// (D1) RESET
cart.hItems.innerHTML = "";
let item, part, pdt;
let empty = true;
for (let key in cart.items) {
if(cart.items.hasOwnProperty(key)) { empty = false; break; }
}
// (D2) CART IS EMPTY
if (empty) {
item = document.createElement("div");
item.innerHTML = "Cart is empty";
cart.hItems.appendChild(item);
}
// (D3) CART IS NOT EMPTY - LIST ITEMS
else {
let p, total = 0, subtotal = 0;
for (let id in cart.items) {
// ITEM
p = products[id];
item = document.createElement("div");
item.className = "c-item";
cart.hItems.appendChild(item);
// NAME
part = document.createElement("div");
part.innerHTML = p.name;
part.className = "c-name";
item.appendChild(part);
// REMOVE
part = document.createElement("input");
part.type = "button";
part.value = "X";
part.dataset.id = id;
part.className = "c-del cart";
part.addEventListener("click", cart.remove);
item.appendChild(part);
// QUANTITY
part = document.createElement("input");
part.type = "number";
part.value = cart.items[id];
part.dataset.id = id;
part.className = "c-qty";
part.addEventListener("change", cart.change);
item.appendChild(part);
// SUBTOTAL
subtotal = cart.items[id] * p.price;
total += subtotal;
}
// EMPTY BUTTONS
item = document.createElement("input");
item.type = "button";
item.value = "Empty";
item.addEventListener("click", cart.nuke);
item.className = "c-empty cart";
cart.hItems.appendChild(item);
// CHECKOUT BUTTONS
item = document.createElement("input");
item.type = "button";
item.value = "Checkout - " + "$" + total;
item.addEventListener("click", cart.checkout);
item.className = "c-checkout cart";
cart.hItems.appendChild(item);
}
},
// (E) ADD ITEM INTO CART
add : function () {
if (cart.items[this.dataset.id] == undefined) {
cart.items[this.dataset.id] = 1;
} else {
cart.items[this.dataset.id]++;
}
cart.save();
cart.list();
},
// (F) CHANGE QUANTITY
change : function () {
if (this.value == 0) {
delete cart.items[this.dataset.id];
} else {
cart.items[this.dataset.id] = this.value;
}
cart.save();
cart.list();
},
// (G) REMOVE ITEM FROM CART
remove : function () {
delete cart.items[this.dataset.id];
cart.save();
cart.list();
},
// (H) CHECKOUT
checkout : function () {
// SEND DATA TO SERVER
// CHECKS
// SEND AN EMAIL
// RECORD TO DATABASE
// PAYMENT
// WHATEVER IS REQUIRED
alert("TO DO");
/*
var data = new FormData();
data.append('cart', JSON.stringify(cart.items));
data.append('products', JSON.stringify(products));
var xhr = new XMLHttpRequest();
xhr.open("POST", "SERVER-SCRIPT");
xhr.onload = function(){ ... };
xhr.send(data);
*/
}
};
window.addEventListener("DOMContentLoaded", cart.init);