I've got this shopping cart script that I'm trying to revise. Trouble is, whenever I try to delete more than one item from the cart, I get a negative value. The cart never goes back to zero when all items are deleted. I can add items fine.
Here is the fiddle.
Below is a code snippet of this feature. The full code is in the fiddle as it is easier to explain by showing you a demo of the problem I am having.
function addToCart(id, container_id, corTitle, corPrice, credit_hrs) {
var amount = parseFloat(corPrice);
var hours = parseFloat(credit_hrs);
var remove = "<button type=\"button\" class=\"remove\"></button>";
var selected_product = "<div class=\"item \">"
+ "<div class=\"title\">"
+"<div class=\"remove\"><button type=\"button\" title=\"remove from cart\" class=\"remove-from-cart\" alt=\"Remove Course\" ></button></div>"
+ corTitle
+ " for $" + corPrice
+ "</div>"
+ "<input name=\"containerId\" value=\"" + container_id
+ "\" type=\"hidden\">" + "</div>";
$(selected_product).insertBefore("#subtotals");
register("add", amount, hours);
$(".remove-from-cart").click(function() {
$(this).parents(".item").slideUp("slow");
console.log(this);
register("subtract", amount, hours);
$(toId(id)).removeAttr("disabled").fadeTo("slow", 1);
$(this).parents(".item").remove();
});
}
The problem appears to be that the click handler attached to the remove button is invoked multiple times when a remove button is clicked. The duplicate invocation of register("subtract", amount, hours) causes the total to go negative. How can I fix this?
The problem is that you re-run $(".remove-from-cart").click(...) each time you add an item to the cart, so all existing remove buttons get an extra handler.
Use jQuery to parse to HTML into a jQuery-wrapped DOM structure, and then use that as a context for your .remove-from-cart selector (as demonstrated in this working fiddle). That way, the .remove-from-cart selector will only apply to your newly-added item.
var selected_product = "<div class=\"item \">" + ...;
// jQuery-wrapped DOM structure
var $prod = $(selected_product)
$prod.insertBefore("#subtotals");
register("add", amount, hours);
// use $prod as jQuery context argument,
// so `.remove-from-cart` only looks in this DOM tree
$(".remove-from-cart", $prod).click(function() {
...
});
Related
I have a function within a jquery pageshow function and only should be activated when on a certain page (id). But for some reason it doesn't run that script. The pages are dynamic with id's.
Here is a sample of my code
$(document).on('pageshow', '#fragment-1', function() {
$.mobile.activePage.find("div [data-role=tabs] ul li:first-child a").click();
createCheckboxes('#fragment-2')
});
function createCheckboxes(into){
var players_names = playerCal("ars", 7, 5);
$("#createBtn").remove();
$(into).append('<fieldset class="cbFieldSet" data-role="controlgroup">');
var length = players_names[0].length;
$(".cbFieldSet").append("<ul data-role='listview' data-inset='true' data-theme='d' data-divider-theme='e' data-count-theme='b'><li data-role='list-divider'><span>Select players in the next line up2</span></li></li>");
for(var i=0;i<length;i++){
$(".cbFieldSet").append('<li><input type="checkbox" name="cb-'+i+'" id="cb-'+i+'" value="'+players_names[0][i]+'"/><label for="cb-'+i+'">'+players_names[0][i]+'</label></li>');
}
$(".cbFieldSet").append("</ul>");
$(into).trigger("create");
$("#showBtn").css("visibility","visible");
console.log(players_names);
}
Sample of my dynamic page, it works
+"<div id='fragment-1'>"
+ "<form>"
+ "<fieldset data-role='controlgroup' class = 'content'>"
+ "</fieldset>"
+ "</form>"
+ "</div>"
+ "<div id='fragment-2'>"
+ "<form>"
+ "<fieldset data-role='controlgroup' class = 'content'>"
+ "</fieldset>"
+ "</form>"
+ "</div></div></div>");
I like when someone creates clean and readable question.
You have an error in your code, div holding your page is not propperly formed jQUery Mobile page.
Change this:
<div id='fragment-1'>
To this:
<div id='fragment-1' data-role="page">
Page events works only on div containers with attribute data-role="page".
Update:
Working example made from your/my previous example: http://jsfiddle.net/Gajotres/vds2U/55/
I'm making a code of a online delivery webpage, and I having a hard time trying to figure out how to output the total of the list ordered by the user.
function ListOrder(){
document.getElementById('order').innerHTML += "<div id=\"YourOrders\">" + + document.getElementById('FoodName').value + document.getElementById('quantity').value + document.getElementById('Totality').value + "</div><br>";}
Edited: I want to know how I can get the sum of the total price. So, I placed a parseInt between the document.getElementById('Totality').value . It looks like this now,
function ListOrder(){
document.getElementById('order').innerHTML += "<div id=\"YourOrders\">" + + document.getElementById('FoodName').value + document.getElementById('quantity').value + parseInt(document.getElementById('Totality').value) + "</div><br>";}
Can someone help me make a function or something for that? Javascript only, please. I'm still kinda new at it.
function ListOrder(){
document.getElementById('order').innerHTML +=
"<div id=\"YourOrders\">" +
parseInt(document.getElementById('FoodName').value) +
parseInt(document.getElementById('quantity').value) +
parseInt(document.getElementById('Totality').value) +
"</div><br>";
}
the kernel of your code should look like the following (double + operator deleted, reformatted):
function ListOrder(){
document.getElementById('order').innerHTML +=
"<div id=\"YourOrders\">" + (
document.getElementById('FoodName').value
+ document.getElementById('quantity').value
+ document.getElementById('Totality').value
)
+ "</div><br>"
;
}
You've phrased your question in a way that suggests you wish to output an order list assembled from the content of all (html) elements with certain ids.
this won't work reliably:
Ids should be document unique.
The Js functions you use do not iterate over lists.
instead, proceed along the following lines (which assume that you import jquery, a cross-browser dom-handling and ajax library (which you should use anyway :)):
function ListOrder(){
var e_orders = $("<div id=\"YourOrders\">");
$("#order").append(e_orders);
$(".FoodName").each ( function ( idx_fn, e_fn ) {
$(e_orders).append(
$("<div/>").append(
$(e_fn).val()
+ $(e_fn).nextAll('.quantity').val()
+ $(e_fn).nextAll('.Totality').val()
);
);
$(e_orders).append("<br>");
});
return e_orders;
}
The code template assumes that the source data are elements with value attributes being marked with css classes quantity, Totality and 'FoodName``, that these elements are siblings and unique within a container element for each item incl. quantity information. It should be flexible enough to be tailored to your actual needs and html structure.
My application successfully creates elements and assigns them different (increasing) IDs.
Now my issue relies when the user deletes these elements (because they have the option to delete as well as create), the consistency of these IDs get broken therefore my application doesn't run well.
This Fiddle represents what I have so far. Just a textbox that appends its value and a few other elements inside a collapsible as many times as the user wants (For some reason my fiddle doesn't increment the alert value, but it works fine on my platform).
SCRIPT (Sorry the txt variable is too long)
$('#Add').click(function () {
if ($("#MedNameStren").val() != "") {
var value = $("#MedNameStren").val();
var noOfMeds = $('#NoOfMedicines').val();
//to check current value
alert(noOfMeds);
var text = '<div data-role="collapsible" data-collapsed="true" data-iconpos="left" data-content-theme="e">' + '<h2>' + desc + '</h2>' + '<div class="ui-grid-a">' + '<div class="ui-block-a" style="width:25%; margin-right:3%;">' + '<input id="quantity' + noOfMeds + '" class="quantity" type="text" placeholder="Quantity" />' + '</div>' + '<div class="ui-block-b" style="width:70%; margin-right:2%;"">' + '<textarea id="directions' + noOfMeds + '" class="directions" cols="40" rows="4" placeholder="Directions given by your GP." ></textarea>' + '</div>' + '</div>' + '<button key="' + vpid + '">Remove</button>' + '</div>';
$("#medListLi").append(text);
$('button').button();
$('#medListLi').find('div[data-role=collapsible]').collapsible();
$('#medListLi li').listview("refresh");
$('#medListLi').trigger("create");
document.getElementById("manuallyName").value = "";
noOfMeds++
$("#NoOfMedicines").val(noOfMeds);
}
else {
alert('Please Provide Medicine Name')
}
});
I am using a counter that neatly increments the ids of quantity and description like:
quantity0
quantity1
quantity2
..and so on, but once the following script is called...
//Deletes colapsible sets (Medicines) from the selected List
$('#medListLi').on('click', 'button', function (el) {
$(this).closest('div[data-role=collapsible]').remove();
var key = $(this).attr('key');
localStorage.removeItem(key);
var noOfMeds = $('#NoOfMedicines').val();
noOfMeds--
$("#NoOfMedicines").val(noOfMeds);
//location.reload();
});
depending on which element (collapsible) is deleted, the IDs stop being consistent. For example if the collapsible with id="quantity1" is deleted then the counter will go back to 1 (currently 2) and on the next addition the respective collapsible will get an id that's already taken, and unfortunately I don't need this to happen.
Maybe I'm making this sound more complicated that it is, but will appreciate any suggestions or ideas to solve this issue (if possible).
If more information is needed, please let me know.
Was brought to my attention that creating and deleting dynamic IDs can be done but keeping up with consistency of these IDs can be very tricky to work around it.
I've solved my own problem by simply creating a function that would keep count of the IDs from the amount of collapsibles inside my list and "renewing" the ID numbers on each Add and Delete.
I know that there's lot here on already on multiple click events being fired off, I think I've read them all but still can't see what's going wrong here.
Hope fully I'm missing something obvious that someone else can pick up easily...
Some background
My code works inside an Enterprise Social Networking platform and creates a BI dashboard for content analysis (about a 1000 lines of the stuff, mostly domain specific, so too much to post in it's entirety).
The part that is causing me grief is the function that builds the dashboard visualisation itself.
Here goes...
function makePage(){
$("#policyCount").text(policyCount);
var docTypes=getGlobalDocTypes(polOwners); //returns a constrained vocab array
var statusTypes=getGlobalStatusTypes(polOwners); //returns a constrained vocab array
$.each(polOwners,function(){ // polOwners is a global array that contains the BI data to be visualised
html=""
var ownerName = this.name.split(":")[1]; // name is a str in format "Owner:HR"
html += "<div id='" + ownerName + "' class='ownerData'>";
html += "<div class='ownerHeading'>" + ownerName + "</div>";
html += this.policies.length + " Policy documents maintained<br />"; // policies is an array of docs managed by owner
divIDReview = "dboard_" + ownerName + "reviewchart";
html += "<div id='" + divIDReview + "' class='dboardelement'></div>";
divIDType = "dboard_" + ownerName + "typechart";
html += "<div id='" + divIDType + "' class='dboardelement'></div>";
divIDStatus = "dboard_" + ownerName + "statuschart";
html += "<div id='" + divIDStatus + "' class='dboardelement'></div>";
html += "<div id='" + ownerName + "ToggleTable' class='toggletable' owner='" + ownerName + "'>";
html += "Click to display all " + ownerName + " documents<br /></div>";
html += "<div id='" + ownerName + "polTable' class='poltable'>";
html += getPolTable(this.policies); // Returns an HTML table of doc metadata
html += "</div>";
html += "</div>";
$("#owners").append(html); // When this function is called #owners is an empty div
$(".toggletable").mouseover(function(){
$(this).css({'cursor':'pointer','text-decoration':'underline'});
});
$(".toggletable").mouseout(function(){
$(this).css( {'cursor':'default','text-decoration':'none'});
});
$(".toggletable").each(function(i, elem){
$(elem).click(function(){
if ($(this).next(".poltable").css("display")=="none"){
// Currently hidden - so show
if (debug){console.log($(this).attr("id") + " was clicked")}
$(this).html("Click to hide " + $(this).attr('owner') + " documents<br/>");
$(this).next(".poltable").css("display","block");
} else {
if (debug){console.log($(this).attr("id") + " was clicked")}
$(this).html("Click to display all " + $(this).attr('owner') + " documents<br />");
$(this).next(".poltable").css("display","none");
}
});
});
// the next section calls functions that use the Google vis api to draw pie charts
drawPie(300,200, "Review Status", "Status", "Policies", getReviewStatus(this.policies), ["green","orange","red"], divIDReview);
drawPie(300,200, "Document Types", "Type", "Docs", getDocTypes(this.policies, docTypes), [], divIDType);
drawPie(300,200, "Document Status", "Status", "Docs", getStatusTypes(this.policies, statusTypes), [], divIDStatus);
});
}
Hopefully that's enough to illustrate the problem.
You'll see that the code builds a dashboard display for each polOwner consisting of three pie charts and an option to hide or display a table of underlying data.
I started by applying the click event to the .toggletable class. When that fired multiple times I used the method described on another answer here with the .each to attach a unique event to each instance of the class.
So, what happens?
There are currently 9 polOwners and at first glance, the click event only seems to be toggling the display state of every other table. The console log however shows that this is because it is firing 9 times for the first instance, 8 for the second, 7 for the third etc. with the odd numbers leaving the table in the alternate state (when this works the display will change to a .toggle animation).
For info, While I'm a text editor person, I do have a copy of MS Expression Web 4 which is a useful tool for error checking HTML. I've pasted in a copy of the entire generated markup (nearly 4000 lines) and can't see any bad nesting or structure errors.
Any ideas folks?
You've got some nested loops:
// jQuery each on polOwners
$.each(polOwners,function(){
// ... code that appends .toggletable class
// jQuery each on .toggletable class
$(".toggletable").each(function(i, elem){
// code that runs on the toggletable element
});
});
For each polOwner you are adding a div with the toggletable class. Then inside there you are looping through each div with a toggletable class and adding a click event.
This adds 1 click for the first polOwner, 2 for the second, three for the third and so on.
Move the toggletable each outside of the polOwner each and you should be good
I have seen a similar question, HERE and have tried that, but I can't seem to get it working.
Here is my code for dynamically generating table rows.
for (var contribution = 0; contribution < candidate.contributions.length - 1; contribution++) {
var id = candidate.contributions[contribution].donor_id;
var uid = candidate.contributions[contribution].user_id;
$("#history-table").append(
"<tr onclick='" + parent.viewEngine.pageChange('public-profile', 1, id, uid) + ";>" +
"<td class='img-cell'>" +
"<img class='profile-avatar-small' src='/uploads/profile-pictures/" +
candidate.contributions[contribution].image + "' alt='' /></td><td class=''>" +
"<h2>" + candidate.contributions[contribution].firstname +
" " + candidate.contributions[contribution].lastname + "</h2></a><br/><br/>" +
"<span class='contribution-description'>" + candidate.contributions[contribution].contribution_description + "</span></td>" +
"<td><h3>$" + formatCurrency(candidate.contributions[contribution].contribution_amount) + "</h3></td></tr>");
}
This still executes the click event as soon as the page loads, which is not the desired behavior. I need to be able to click the tr to execute the click event.
Pass the whole thing as a string:
"<tr onclick='parent.viewEngine.pageChange(\'public-profile\', 1, " + id + ", " + uid + ");>" // + (...)
But, as you are using jQuery, you should be attaching the click handler with .on().
(I really don't recommend using inline event handlers like that, especially when you're already using jQuery, but anyway...)
The problem is that you need the name of the function to end up in the string that you are passing to .append(), but you are simply calling the function and appending the result. Try this:
...
"<tr onclick='parent.viewEngine.pageChange(\"public-profile\", 1, " + id + "," + uid + ");'>" +
...
This creates a string that includes the name of the function and the first couple of parameters, but then adds the values of the id and uid variables from the current loop iteration such that the full string includes the appropriately formatted function name and parameters.
Note that the quotation marks around "public-profile" were single quotes but that wouldn't work because you've also used single quotes for your onclick='...', so you should use double-quotes but they need to be escaped because the entire string is in double-quotes.
I'm wondering if you might be better simplifying things a bit.
If your rows are being dynamically added, then try putting some kind of meta-data in the <tr> tag, e.g. something like this:
<tr id="id" name="uid">
Then try the following with your jQuery (v.1.7 required):
$('#history-table tr').on('click', function(){
parent.viewEngine.pageChange('public-profile', 1, this.id, this.name);
});
This will likely require modification depending on how your page rendering works but it's a lot cleaner and easier to read having been removed from your main table markup.
Well that's because you're executing the function, not concatenating it. Try:
onclick='parent.viewEngine.pageChange("public-profile", 1, id, uid);'
Take this ->
$("#contribution-" + uid).click(function(){
parent.viewEngine.pageChange('public-profile',1, id, uid);
});
And do two things:
1) Move it outside of the 'for' statement
As soon as the for statement is executed, the click function will be executed as well. The click function is not being supplied as a callback function in this for statement.
2) Change it to ->
$("tr[id^='contribution-'").on('click', function(){
var idString = $(this).attr("id").split("-"); //split the ID string on every hyphen
var uid = idString[1]; //our UID sits on the otherside of the hyphen, so we use [1] to selec it
//our UID will now be what we need. we also apply our click function to every anchor element that has an id beginning with 'contribution-'. should do the trick.
parent.viewEngine.pageChange('public-profile',1, id, uid);
});
This is my solution.