Hello all I just want to say thank you in advance. I have a few issues that I would like to address with my shopping cart page. This application is made in flask.
I have a cart that dynamically populates rows in a table with data from a Restful API created with python. At the moment it can also add the prices within the API and display it as the subtotal's html. I can also hit the delete button next to the item and it deletes that particular element out of the API. The issue is I need to be able to update the subtotal html upon deleting the item.
Yes I can hit the delete button and upon refreshing the page it will show the correct subtotal but this is unrealistic.
Upon adding and deleting items in cart I also have a badge on the shopping cart icon in the upper right hand corner that increments according to how many elements are in API. Once I figure out issue with (problem labeled 1) I can figure out how to make the badge decrease upon item deletion. My main issue here is the cart shows no badge upon moving to different tabs of the website. The JS is linked to the base html, but I guess since the java script is not running on those particular pages it's not going to show. Not to sure how to work around this.
If there are no items in cart I would like to hide the subtotal html and order button. But for some reason I can't get it to toggle and don't know where I should put the code to check if there are no items in API.
I'm probably asking too much but if possible please help if you may have any insight. I'll be attaching the code below for javascript, my flask python route, and the html for the cart page.
Pricing pricing.html
p{% extends 'base.html' %}
{% block content %}
<h1>Pricing</h1>
<div class="row">
<div class="container col-sm-6">
<div class="container border">
<table id='table'>
<thead>
<th><h5>Equipment</h5></th>
<th "><h5>Price</h5></th>
</thead>
{% for quip in pricing %}
<tr style="height:25px;" class="border">
<td id='pricewidth'>{{quip}}</td>
<td id='pricewidth' style='text-align:center;'>{{pricing[quip]}}</td>
<td ><button type="button" name="button" class="btn btn-primary">Add</button></td>
</tr>
{% endfor %}
</table>
</div>
</div>
<div class="container col-sm-6">
<table id='cart'>
</table>
<div id='pricefooter'>
<h1 style='margin-top:25px; border-top:.5px black solid;'>Subtotal: $<span id='subtotal'>0</span></h1>
<form action="{{url_for('Order')}}"><button type="submit" name="button" class='btn btn-warning'>Order</button></form>
</div>
</div>
</div>
{% endblock content %}
Cart Javascript pricecart.js
var tablerows = document.getElementById('table').rows.length;
var table = document.getElementById('table');
var cart = document.getElementById('cart');
var subtotal = document.getElementById('subtotal');
var username = document.getElementById('username').innerHTML;
var cartBadge = document.getElementById('cartbadge');
var pricesub = document.getElementById('pricefooter');
// On load cart
window.onload = function wowzers(){
var array = [];
var sum = 0;
// Get Data
var xhr = new XMLHttpRequest();
xhr.open('GET', 'pricing/orders/' + username +'/api', true);
xhr.onload = function(){
var data = JSON.parse(this.response);
cartBadge.innerHTML = data.length
if(xhr.status >= 200 && xhr.status < 400){
for(x in data){
for(key in data[x]){
array.push(Number(data[x][key]));
sum+=Number(data[x][key]);
subtotal.innerHTML = sum;
row = cart.insertRow(-1);
// Delete Data
row.addEventListener('click', function deleterow(){
index = this.rowIndex;
// subtotal.innerHTML = sum-Number(cart.rows[index].cells[1].innerHTML);
$.post('pricing/orders/delete', {
delete_item: index
});
cart.deleteRow(index);
});
cell1 = row.insertCell(0);
cell2 = row.insertCell(1);
cell3 = row.insertCell(2);
cell1.innerHTML = key;
cell2. innerHTML = data[x][key];
cell3. innerHTML = "<button class='btn btn-danger'>Delete</button>"
}
}
console.log(sum);
}else{
console.log(error)
}
}
xhr.send()
}
//Dynamic Cart
for(x = 0; x < tablerows; x++){
table.rows[x].addEventListener('click', addCartItem);
}
function addCartItem(ev){
var array = [];
var sum = 0;
index = this.rowIndex;
equipmentCell = table.rows[index].cells[0];
priceCell = table.rows[index].cells[1];
equipmentName = equipmentCell.innerHTML;
equipmentPrice = priceCell.innerHTML;
// Post Data
$.post('/pricing/orders/' + username + '/api', {
javascript_data: JSON.stringify({[equipmentName]:equipmentPrice})
});
cartrow = cart.insertRow(-1);
// Delete Data
cartrow.addEventListener('click', function deleterow(){
index = this.rowIndex;
// subtotal.innerHTML = sum-Number(cart.rows[index].cells[1].innerHTML);
$.post('pricing/orders/delete', {
delete_item: index
});
cart.deleteRow(index);
});
cell1 = cartrow.insertCell(0);
cell2 = cartrow.insertCell(1);
cell3 = cartrow.insertCell(2);
cell1.innerHTML= equipmentName;
cell2.innerHTML = equipmentPrice;
cell3.innerHTML = "<button class='btn btn-danger'>Delete</button>";
// Open Api information
var xhr = new XMLHttpRequest();
xhr.open('GET', 'pricing/orders/' + username +'/api', true);
xhr.onload = function(){
var data = JSON.parse(this.response);
cartBadge.innerHTML = data.length
if(xhr.status >= 200 && xhr.status < 400){
for(x in data){
for(y in data[x]){
array.push(Number(data[x][y]));
sum+=Number(data[x][y]);
subtotal.innerHTML = sum;
}
}
}else{
console.log(error);
}
}
xhr.send();
}
Flask Route routes.py
#app.route('/pricing/orders/<user_name>/api', methods=['POST', 'GET'])
#login_required
def api(user_name):
user_name = current_user.username
if request.method == 'POST':
cart.append(json.loads(request.form["javascript_data"]))
return jsonify(cart)
#app.route('/pricing/orders/delete', methods=['POST', 'GET'])
#login_required
def delete_item():
if request.method == 'POST':
print(cart[json.loads(request.form["delete_item"])])
cart.pop(json.loads(request.form["delete_item"]))
print(cart)
return jsonify({"whoa": "there"})
I'm a noob so this may be quite the long winded question an easy problem. Thanks guys!
You can try to bind event listener not to every single row (like you do in the loop), but for all of them in one time. After for loop add something like code below and remove event-listener in the loop, hope it will work:
document.querySelectorAll('.row-selector').on('click', function() {
... // do stuff with row
})
This problem can be solved using flask's context_processor. You can read more about it in official documentation. In a word you can put badge length in template's context and then use it anywhere in your templates, for example:
#app.context_processor
def inject_badge_length()
badge_length = ... // calculate badge length for current user
return {'BADGE_LENGTH': badge_length}
and then you can use it in template like:
<div class="badge-length">{{ BADGE_LENGTH }}</div>
Finally, if you have badge length (which can also be 0) you can hide subtotal html using css and javascript, like this:
#cart {
opacity: 0;
}
#cart.active {
opacity: 1;
}
and in js append this to the deleterow event-function (which, by the way, can be anonymous (nameless) function in this case):
if (cartBadge.innerHTML === "0") {
cart.classList.remove('active');
}
and somewhere in the end of 'addCartItem' function append:
if (!cart.classList.contains('active') && cartBadge.innerHTML !== "0") {
cart.classList.add('active');
}
Related
Ok, I am going to try to explain this as best as I can. I created a search for a database that has 3 columns: Category, OEM Number, and Price. I want to make so that when the user inputs an OEM number it will show the category and OEM Number as the result, then the results are clickable to show the entire row, Category, OEM Number, and Price. I also want it so that if they only input a partial OEM Number, that it will list all the OEM Numbers that include that partial number and they click the correct full OEM Number they want to display the Category, OEM Number, and Price for that OEM Number. Here is my code as of now, it just has them input an OEM Number and returns the entire row or if they input a partial number it returns all results including that partial number all on the page. I want the page that shows the price to only have a single entry on it.
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<script src="js/jquery-2.2.2.min.js"></script>
<script src="js/bootstrap.min.js"></script>
<link href="css/bootstrap.min.css" rel="stylesheet">
<title>AJAX Search Example</title>
<script>
function fetch() {
// (A) GET SEARCH TERM
var data = new FormData();
data.append('search', document.getElementById("search").value);
data.append('ajax', 1);
// (B) AJAX SEARCH REQUEST
var xhr = new XMLHttpRequest();
// (CHANGE1) USING ONREADYSTATECHNAGE INSTEAD OF ONLOAD
xhr.onreadystatechange = function (event) {
// (CHANGE2) we will check if ajax process has completed or not it goes from 1,2,3,4 means end.
if(this.readyState == 4){
// (CHANGE2) when ready state comes to 4 we then check what response status was it if it is 200 good else error.
if(this.status == 200){
// (CHANGE3) MOVED ALL YOUR CODE HERE
// (CHANGE4) we need to use responseText instead of response because JSON comes as string that is why we are parsing it to be converted into array
var results = JSON.parse(this.responseText);
//I have added just a measure to check what the out put is you can remove it latter. open dev console to get the result.
console.log(results);
wrapper = document.getElementById("results");
wrapper.innerHTML = "";
var rows = "";
if (results.length > 0) {
// (CHANGE5) UPDATED data ref with results
for (i = 0; i < results.length; i++) {
let line = document.createElement("div");
//it is just as simple to create id only it must start with alaphabet not number
line.id=`res${[i]}`;
//we created span tag to display price and this is what we will change. on that span we will create a data-price attribute which will hold orginial price and we will run claulcations using that number
//BIG CHANGE
//BIG CHANGE
//since after parsing invidual record will be in Js object so we dont need to access them like array results[i]['item']
//we access them with dot notation results[i].item
rows += `<tr id=res${[i]}><td>${results[i].category}</td><td>${results[i].oemnumber}</td><td>$<span data-price='${results[i].price}'>${results[i].price}</span>
select discount >>
%70
%60
%50 100%</td></tr>`;
}
wrapper.innerHTML = `<table class="table">
<thead><th>Category</th><th>OEM</th><th>Price</th></thead><tbody>${rows}</tbody></table>`;
// (CHANGE6) We moved event listeners here so any newly added elements will be updated.
//get all the links and apply event listener through loop
var links = document.querySelectorAll('a');
for ( ii = 0; ii < links.length; ii++) {
links[ii].addEventListener("click", function(event) {
//capture link value and get number to be converted to percentage
var percentage = event.target.innerText.match(/\d+/)[0]/100;
//capture the data-price which is within same div as anchor link
var pricetarget = event.target.parentElement.querySelector('[data-price]');
//get value of data-price
var actualprice= pricetarget.dataset.price;
//run math and chnage the value on display
pricetarget.innerHTML=(actualprice*percentage).toFixed(2);
});
}
} else { wrapper.innerHTML = "No results found"; }
} else {
//if reponse code is other ethan 200
alert('INTERNET DEAD OR AJAX FAILED ');
}
}
};
// (CHANGE7) We moved open event to end so everything is ready before it fires.
xhr.open('POST', "2-search.php");
xhr.send(data);
return false;
};
</script>
</head>
<body>
<!-- (A) SEARCH FORM -->
<form ID='myForm' onsubmit="return fetch();">
<h1>SEARCH FOR CATALYTIC CONVERTER</h1>
<input type="text" id="search" required/>
<input type="submit" value="Search"/>
</form>
<!-- (B) SEARCH RESULTS -->
<div id="results"></div>
</body>
</html>
To sum it up simply, I want to have the first results return just 2 columns in the results, the category and the OEM Number. Then I want those results to be clickable and return the entire single row, all 3 columns. Thank you for any help you can offer.
Avoid calling a global function fetch, and there is a built in browser function also called fetch that's like a much much better XMLHttpRequest.
Let's rename that fetch function to getData, to save confusion.
Next up it's getting data and trying to update your DOM, which makes it much harder to debug and find out what it's doing wrong.
You're setting document.getElementById("results").innerHTML, which will work, but will also cause fairly slow reflows. You may want to set DOM directly or use a library like Lit or React (or any of the many others) that will handle that for you.
By replacing the AJAX fetch with dummy data I can test this, and it works in a snippet...
async function getData() {
// (A) GET SEARCH TERM
const data = new FormData();
data.append('search', document.getElementById("search").value);
data.append('ajax', 1);
const wrapper = document.getElementById("results");
wrapper.innerHTML = "Loading...";
// This is the better API to use
//const response = await fetch(...);
//if(!response.ok) return;
//const results = await response.json();
// But let's use a dummy
const results = [{
category: 'foo',
oemnumber: 1,
price: 1
},
{
category: 'bar',
oemnumber: 2,
price: 2
}
];
let rows = "";
if (results.length > 0) {
for (i = 0; i < results.length; i++) {
let line = document.createElement("div");
line.id = `res${[i]}`;
rows += `<tr id=res${[i]}><td>${results[i].category}</td><td>${results[i].oemnumber}</td><td>$<span data-price='${results[i].price}'>${results[i].price}</span>
select discount >>
%70
%60
%50 100%</td></tr>`;
}
wrapper.innerHTML = `<table class="table">
<thead><th>Category</th><th>OEM</th><th>Price</th></thead><tbody>${rows}</tbody></table>`;
const links = document.querySelectorAll('a');
for (ii = 0; ii < links.length; ii++)
links[ii].addEventListener("click", function(event) {
const percentage = event.target.innerText.match(/\d+/)[0] / 100;
const pricetarget = event.target.parentElement.querySelector('[data-price]');
const actualprice = pricetarget.dataset.price;
pricetarget.innerHTML = (actualprice * percentage).toFixed(2);
});
} else
wrapper.innerHTML = "No results found";
};
document.getElementsByTagName('form')[0].addEventListener('submit', e => {
e.preventDefault();
getData();
return false;
});
<!-- (A) SEARCH FORM -->
<form ID='myForm'>
<h1>SEARCH FOR CATALYTIC CONVERTER</h1>
<input type="text" id="search" required/>
<input type="submit" value="Search" />
</form>
<!-- (B) SEARCH RESULTS -->
<div id="results"></div>
It sounds like you're describing a master-detail-list, where selecting a row shows a detail panel with more information. Generally you want to split this out so that the detail is rendered outside the result list, as you reset the result list all the time.
Looking for a little guidance. I know its something small and dumb but I'm completely drawing a blank at this point and could use some help. I'm trying to create a mobile app for my class that needs a dynamic table for my results. I'm attempting to create a user input to select a number of "Random powerball tickets" and the table would give "Ticket 1 / Random Numbers." I have managed to create the random number generator onclick but cant for the life of me figure out the rest.
HTML- I dont remember how to connect the user input to the button and repeat x amount of times to match.
<div data-role="content">
<p>This will be a simple application that provide generated powerball numbers between 1-69.</p>
</div>
<div>
<button id="button" onClick="winningNumbers()" >Powerball Numbers</button>
</div>
<p id="outcome"></p>
<table id="data">
</table>
Current Javascript
var powerball;
function powerballNumbers(max) {
var ranNum = Math.floor((Math.random() * max) + 1);
return ranNum;
}
function main() {
powerball = [];
for (i = 0; i < 5; i++) {
powerball.push(powerballNumbers(69));
}
powerball.push(powerballNumbers(26));
}
function winningNumbers() {
main();
var totalTickets = document.getElementById("outcome");
totalTickets.innerText = powerball;
}
Thinking of something like this for the table but know it's not correct
function updateTable(ticketNumber, powerballNumber) {
var dataTable = document.getElementById("data");
dataTable.innerHTML = "";
// create rows of data based on given arrays
(Not sure what to put here)
// create header row
var thead = dataTable.createTHead();
var row = thead.insertRow(0);
var tableHeaders = ["Ticket", "Numbers"];
for (var i = 0; i < tableHeaders.length; i++) {
var headerCell = document.createElement("th");
headerCell.innerHTML = tableHeaders[i];
row.appendChild(headerCell);
}
}
I'm not entirely sure of what your end goal is, but the best I understand is you want to generate some tickets with an ID, and each ticket has 5 numbers? If so, I simply generated a ticket ID, and 5 numbers to go with that ticket. Then in the update table function, I've simplified it so it can focus on just appending new rows. If I've missed the mark please comment below and/or update your question.
Just some side comments.
Avoid using attributes for click events, it's unreliable at best.
Don't hestiate to use HTML when HTML is the answer. Your original update table method was going to build out a table? It only adds a headache, not ease.
Good job on leveraging the tools <table> gives us!
var powerball;
function powerballNumbers(max) {
var ranNum = Math.floor((Math.random() * max) + 1);
return ranNum;
}
function main() {
let i = 0
interval = setInterval(function() {
updateTable(powerballNumbers(9999), [powerballNumbers(69),
powerballNumbers(69),
powerballNumbers(69),
powerballNumbers(69),
powerballNumbers(69)
]);
i++;
if (i > 5) {
clearInterval(interval);
}
}, 500)
}
function winningNumbers() {
main();
var totalTickets = document.getElementById("outcome");
totalTickets.innerText = powerball;
}
function updateTable(ticket, powerballNumber) {
var dataTable = document.getElementById("data");
let newRow = dataTable.insertRow();
let ticketCell = newRow.insertCell();
ticketCell.textContent = ticket;
let numbers = newRow.insertCell();
numbers.textContent = powerballNumber.join(", ");
}
<div data-role="content">
<p>This will be a simple application that provide generated powerball numbers between 1-69.</p>
</div>
<div>
<button id="button" onClick="winningNumbers()">Powerball Numbers</button>
</div>
<p id="outcome"></p>
<table id="data" border=1>
<thead>
<tr>Ticket Number</tr>
<tr>Numbers</tr>
</thead>
</table>
I am making a table in JavaScript using template literals, so I don't have access to each row of my table. I have a form in which my table is set and I have a number input at the end of each row. Right now, my program is only sending the quantity of the fist object to the console, but I need all of the quantities according to the id so that i can make a total at the end of my shopping cart.
I don't know if I could make a loop that goes through each row and tell me the id, price and quantity but that would be my first instinct. I am still new to JavaScript so I don't really know where to go from here.
Here is my JavaScript code:
//load JSON file
var articles = ""
var txt = ""
var xmlhttp = new XMLHttpRequest();
xmlhttp.onreadystatechange = function(){
if(xmlhttp.status == 200 && xmlhttp.readyState == 4){
articles = xmlhttp.responseText;
processArticles(articles);
var form = document.getElementById('formtable');
var quantity = document.getElementById('quantity');
form.onsubmit = function(e) {
e.preventDefault();
console.log("HI");
console.log(quantity.value);
};
}
};
xmlhttp.open("GET","../articles.json",true);
xmlhttp.send();
function processArticles(articles) {
txt = JSON.parse(articles);
var tableStart = `
<h2>Liste des articles</h2>
<form id="formtable">
<table>
<tr>
<th>ID</th>
<th>Article</th>
<th>Prix</th>
<th>Prix-Retour</th>
<th>Quantitée maximale</th>
<th>Projet</th>
<th>Quantitée</th>
</tr>`;
var tableEnd = `
</table>
<input type="submit">
</form>`;
function articlesTemplate(txt) {
return `
<tr>
<td>${txt.ID}</td>
<td>${txt.Article }</td>
<td>${txt.Prix}</td>
<td>${txt.PrixRetour}</td>
<td>${txt.QuantiteeMaximale}</td>
<td>${txt.Projet}</td>
<td><input type="number" id="quantity" min="1" max="5"></td>
</tr>
`;
}
let mctxt=txt.filter(value=>
value.Projet=="mc");
document.getElementById("tablemc").innerHTML = `
${tableStart}
${mctxt.map(articlesTemplate).join("")}
${tableEnd}
`;
;
}
In my HTML, I just have a div with the id of tablemc.
I want to be able to see the quantity of each item with their id, so that I can make a total amount at the end of my table. Right now, it only sends the quantity of the first item and it doesn't tell me which id it is or what the price of the item is.
Every time a selection is made from a dropdown menu, specific data is pulled from facebook and added to different divs. I am trying to update the contents of the div every time a different selection is made, however at the minute, the contents are just appended on after the initial contents.
This is the code that gets data based on a selection and creates the list from the returned data
<script>
city = document.getElementById("citySelection")
city.addEventListener("change", function() {
var selected = this.value;
var eventsList = document.getElementById("events");
if (selected == "None") {
eventsList.style.display = "none";
} else {
eventsList.style.display = "block";
};
if (selected == 'Bristol') {
getBristolEvents();
};
if (selected == 'Leeds') {
getLeedsEvents();
};
if (selected == 'Manchester') {
getManchesterEvents();
};
if (selected == 'Newcastle') {
getNewcastleEvents();
};
});
function createList(response, listId) {
var list = document.createElement('UL')
for (var i = 0; i < 10; i++) {
var events = response.data[i].name
var node = document.createElement('LI');
var textNode = document.createTextNode(events);
node.appendChild(textNode);
list.appendChild(node)
listId.appendChild(list);
}};
</script
This is the div being targeted:
<html>
<div id="events" style="display: none">
<div id="eventsDiv" style="display: block">
<div id="eventsListOne">
<h3 id='headerOne'></h3>
</div>
<div id="eventsListTwo">
<h3 id='headerTwo'></h3>
</div>
<div id="eventsListThree">
<h3 id='headerThree'></h3>
</div>
</div>
</div>
</div>
</html>
I have tried resetting the innerHtml of the div every time the function to get the data from facebook is called:
<script>
function getEventsThree(fbUrl, title) {
var listId = document.getElementById('eventsListThree');
var headerThree = document.getElementById('headerThree');
listId.innerHtml = "";
headerThree.append(title)
FB.api(
fbUrl,
'GET', {
access_token
},
function(response) {
listId.innerHtml = createList(response, listId)
}
)};
</script>
However, that still doesn't reset the contents of the div.
I've looked at other response but they all use jquery which I am not using.
Can anyone advise on the best way to fix this? Thanks.
I think your Hennessy approach is fine. Generate the inner content, then set .innerHTML.
At least one of your problems, maybe the only one, appears to be that you set .innerHTML to the return value of createList, but that function does not return anything.
Im starting to do some small functions and tweaks on websites with javascript, but whats really bothers me is that I dont know how to run the javascript again after a function has run?
For instance if I call a function onclick which adds a user to an array that is shown in my website, the new user wont be displayed until the page is refreshed?
How do I work around this?
EXAMPLE:
if (!localStorage.myStorage) {
// CREATE LOCALSTORAGE
}else{
myArray = JSON.parse(localStorage.myStorage);
for (var i = 0; i < myArray.length; i++) {
if(myArray[i].id === 1){
$(".firstIdContainer").append("<p>" + myArray[i].userName + "</p>");
}
if(aUserLogin[i].id === 2) {
$(".secondIdContainer").append("<p>" + myArray[i].userName + "</p>");
}
}
}
$(document).on("click", ".btnRegisterUser", function() {
// ADD NEW USER TO LOCALSTORAGE
}
How do i make sure my new user i register will be shown immediately through my for loop displaying users.
Like:
if(!localStorage.myStorage){
// CREATE LOCALSTORAGE
}
function doIt(){
var myArray = JSON.parse(localStorage.myStorage);
for(var i in myArray){
var apd = '<p>' + myArray[i].userName + '</p>';
if(myArray[i].id === 1){
$(".firstIdContainer").append(apd);
}
else if(aUserLogin[i].id === 2) {
$(".secondIdContainer").append(apd);
}
}
}
}
doIt();
$('.btnRegisterUser').click(doIt);
Try creating a contentUpdate function that resets whatever is getting displayed and creates it again based on new variables (this would go at the bottom of a function to add the user, for example). The reason that variable changes aren't reflected in the DOM is that the DOM has no abstraction for how it was made; it's output, and it won't change itself based on what its input has done after it was put in.
If you just want to insert a new row into a table you don't need to refresh the page.
jsfiddle
html:
<table id="usertable">
<tr><td>user 1</td></tr>
</table>
<input id="newuser"></input>
<input id="adduser" type="submit"></input>
js:
var button = document.getElementById('adduser');
button.onclick = function(event) {
var user = document.getElementById('newuser').value
//add the user to your array here
//add a table row
var table = document.getElementById('usertable');
var row = table.insertRow(0);
var cell1 = row.insertCell(0);
cell1.innerHTML = user;
event.preventDefault();
}