In class, our assignment was to create a grocery list of fruit and their prices by placing objects into an array. Now that I did that part, i'm supposed to Extend the shopping cart program from the last lab. Set up a basic HTML page
Append the items and their prices from the shopping list to the page."
This is where I got lost. We can only use vanilla JavaScript. ** I was able to get the webpage to display the total button, and it works(it calculated the items), but where is my list of groceries and their prices?!
my code:
var fruita = {
name: 'apple',
price: 5
};
var fruitb = {
name: 'pear',
price:3
};
var fruitc = {
name: 'orange',
price: 4
};
var grocery = [];
grocery.push(fruita, fruitb, fruitc);
var total = (fruita.price + fruitb.price +fruitc.price);
for (i=0; i<grocery.length; i++){
console.log(grocery[i].name);
console.log(grocery[i].price);
}
var total = (fruita.price + fruitb.price +fruitc.price);
console.log("total price= " + total);
function calcTotal() {
document.getElementById("total").innerHTML = total;
}
function displayList() {
document.write(grocery).innerHTML = grocery;
In my html:
<head>
<script src="monday2assn2.js"></script>
</head>
<body>
<h1> Shopping List </h1>
<p>Click "Total" to add up your shopping list.</p>
<button onclick="calcTotal()">Total</button>
<p id="total"> </p>
One good way to start is to create a global function that standardizes your method for adding elements to the page. Here are functions that can be used to add an element to a parent element:
function addElement(type,content,parent) {
var newElement = document.createElement(type);
var newContent = document.createTextNode(content);
newElement.appendChild(newContent);
get(parent,'id');
current.insertBefore(newElement,current.childNodes[0]);
}
function get(reference, type) {
switch(type) {
case 'id':
current = document.getElementById(reference);
break;
case 'class':
current = document.getElementsByClassName(reference);
break;
case 'tag':
current = document.getElementsByTagName(reference);
break;
}
}
I copied and pasted this from my own files, as I use these frequently. The get function is used to select elements. The addElement function is used to create a new element. The "type" parameter specifies the tag - so p, div, span, etc. "Content" and "parent" are pretty straightforward. The "parent" parameter is represented by the id of the parent. Once done, you can do something as so:
for(var i=0;i<grocery.length;i++) {
var concat = 'Item: ' + grocery[i].name + ' Price: ' + grocery[i].price;
addElement('p',concat,'body'); //assign the body tag an id of "body"
}
An alternate method is to simply set the innerHTML of the body to add everything to it.
get('body','id');
var joined = current.innerHTML; //all elements in a string
for(var i=0;i<grocery.length;i++) {
var concat = 'Item: ' + grocery[i].name + ' Price: ' + grocery[i].price;
joined += concat;
}
current.innerHTML = joined;
Of course, it would help if you'd specify more specifically how exactly you want your data to be formatted in the HTML. But don't fear Vanilla Javascript - I find it to be much easier than using libraries for things like this, and people often become too reliant on libraries that they forget the roots of JS.
Related
I'm getting JSON data from the API, like this
data = {
q: 'sugar',
from: 0,
to: 10,
params: {
sane: [],
q: [ 'sugar' ]
},
more: true,
count: 1000,
hits: [{
recipe: {
uri: 'http://www.edamam.com/ontologies/edamam.owl#recipe_a46ae0ad4f8320fa6285b25fc7b36432',
label: 'Bread Pudding with Apple and Brown Sugared Bacon',
image: 'https://www.edamam.com/web-img/1ae/1ae111af3737d42e9d743445f5605373.JPG '
},
recipe: {
uri: 'http://www.edamam.com/ontologies/edamam.owl#recipe_a36b51f50f102bf5da228af55d2ce040',
label: 'Vanilla sugar',
image: 'https://www.edamam.com/web-img/4fd/4fdd2336043aa81dd05a4b6a08934402.jpg',
}
}]
}
And I try to bind the recipe to divs. For example, there is a div with the id columns,
Here is that piece of codes.
var list = data.hits;
function Builddivs(list) {
var array = new Array(0);
var count = list.length;
for (var i = 0; i < 10; i++) {
var p = list[i];
title = p.recipe.label;
imgurl = p.recipe.image;
href = p.recipe.url;
array.push("<div class='pin'><a href='" + href + "'><img src='" + imgurl + "'/></a><p>" + title + "</p> </div>");
}
var html = array.join("");
$("#columns").html(html);
}
The problem is to generate that html takes like several seconds, so is there a better way to do that? like bind the data directly to existing dynamic number of divs? Thanks!
Instead of generating a lot of HTML at once, it would be more efficient to edit existing HTML.
Example jsfiddle solution to this question
Editing HTML with jQuery
You can replace or add text or HTML to a div:
$(selector).html('Try <span class="red">this!</span>');
$(selector).text('Just add some text');
$(selector).append('Some HTML');
Adding src to an image or adding href to a link:
$(selector).attr('src','http://example.com/someimage.jpg');
$(selector).attr('href','http://example.com');
Instead of using a for loop, you could use javascript Array.forEach or jquery $.each
Generating HTML for the first time
On every run (every time the list changes) you can check if the HTML element to be edited exists. If not, then you can generate the appropriate HTML. On the first run of the code, the HTML would then be generated for every element from the list.
See this question to see how to check if elements exist:
How do you check if a selector matches something in jQuery?
Example
Here is a working jsfiddle with an example of how HTML can be edited using $().attr and $().html: https://jsfiddle.net/fyux250p/1/
var hitsContent =""
(list || []).forEach(function(data,index){
hitsContent += "<div class='pin'><a href='" + data.url + "'><img src='" + data.url + "'/></a><p>" + data.label + "</p> </div>";
})
$("#columns").html(hitsContent);
I am trying to solve a problem in javascript and I want to do it in javascript not in jQuery.
I create objects in javascript, display them as elements and I bind an eventListener. For simplicity I create objects with a name, description and a price. What I basically want to do is when people click the name the corresponding price is alerted. Is there any way to do this with javascript? In my actual project I want a hover event so that people get a tooltip-like description of the product, but the idea is the same.
Thanks in advance.
Here a fiddle:
fiddle
And some code:
function makeProduct (name, description, price) {
this.name = "<p class='name'>" + name + "</p>";
this.description = "<p class='description'>" + description + "<p>";
this.price = "<p class='price'>" + price + "</p>";
document.getElementById('productlist').innerHTML+=this.name;
document.getElementById('productlist').innerHTML+=this.description;
document.getElementById('productlist').innerHTML+=this.price;
}
var product1=new makeProduct("Pizza", "very Tasty", 5.00);
var product2=new makeProduct("Choclate milk", "warm for the cold winter", 3.00);
var productnames = document.getElementsByClassName('name');
for (i=0; i<productnames.length; i++){
(function(){
productnames[i].addEventListener('click', showPrice, false);
})();
}
function showPrice () {
alert("product price");
}
The quick fix is to write a function that returns the next matching p elem:
function getNextElement(elem, className) {
var next = elem.nextElementSibling; // set next element to "nextElementSibling" relative to passed element.
while (next && !next.classList.contains(className)) { // check for existence and class
next = next.nextElementSibling; // if it exists, but the class does not, move to the next element and repeat.
}
return next; // return whatever was found, or null
}
function showPrice () {
var elem = getNextElement(this, 'price'),
price = elem ? elem.innerHTML : 'Price not found.';
alert(price);
}
See updated fiddle: http://jsfiddle.net/zkbdy0rt/5/
A better way to do this would be to change how you are building your product list. For instance, I would put each product in a containing div (with class="product") for easier styling or finding.
In JavaScript, you can also add arbitrary properties to DOM elements which can be useful. For instance, you could store the price on the DOM element and then retrieve it in your click event handler.
div.price = product.price;
and then later...
showPrice = function (e) {
var price = this.parentElement.price;
alert(price.toFixed(2));
}
See this fiddle for a working example: http://jsfiddle.net/zkbdy0rt/6/
First I fix your current code:
function makeProduct (name, description, price) {
this.name = "<p class='name'>" + name + "</p>";
this.description = "<p class='description'>" + description + "<p>"; //this line
...
}
//this line shoud be:
this.description = "<p class='description'>" + description + "</p>"; //</p> should be closing tag
Secondly:
function makeProduct (name, description, price) {
//...
document.getElementById('productlist').innerHTML+=this.price;
}
as makeProduct(..) returns nothing, there shoulding be assigment to new object to product1
var product1=new makeProduct("Pizza", "very Tasty", 5.00);
Finally your function that returs relevant price should look line:
function showPrice(evt)
{
var p1 = evt.target.nextSibling.nextSibling;
alert(p1.innerHTML);
}
DEMO
I'm developing a food order system. I want show my order list in a textarea. Right now I can make list in a div's content. I also want my result price to be rounded at two decimal place like if you enter 2, it'll be formatted to 2.00.
My full code is a bit long.
This is a sample of the JavaScript I use :
function addNewItem() {
price = 4.50;
result += price;
appendElement("container", "element" + price, "Cappucino Ice Blended " + price + "[Remove]");
document.getElementById('sumOrder').value = result;
}
The full code in a fiddle
To create new lines in the textarea, so that it looks like a list, you can delimit your orders with "\n".
Example: http://jsfiddle.net/U58gT/
<textarea id="orders" rows="10"></textarea>
<br /><br /><br />
<input id="new-order" type="text"><button>add order</button>
$('button').click(function() {
var new_order = $('#new-order').val();
$('#orders').append(new_order + "\n");
});
Thanks for reply but I using other method. I already can make list in text area. Now i got the problem to remove it when i click remove link. In DIV's i can remove properly but the textarea not remove. i using this code for make list in textarea.
This is a sample of the JavaScript I use :
var result =0.00;
function addtxt(input) {
price = 4.50;
result += price;
var obj=document.getElementById(input)
var txt=document.createTextNode("Cappucino Ice Blended "+ price+"\n")
obj.appendChild(txt)
addNewItem();}
function addNewItem()
{
appendElement("container", "element" + price, "Cappucino Ice Blended " + price + "[Remove]");
document.getElementById('sumOrder').value = result;
}
function removeItem(price)
{
result -= price;
removeElement("container", "element" + price);
document.getElementById('sumOrder').value = result;
}
function removeElement(parentId, elementId)
{
var parentElement = document.getElementById(parentId);
var childElement = document.getElementById(elementId);
parentElement.removeChild(childElement);
}
function appendElement(containerId, newElementId, newElementContent)
{
var newElement=document.createElement("div");
newElement.setAttribute("id", newElementId);
newElement.innerHTML=newElementContent;
var container = document.getElementById(containerId);
container.appendChild(newElement, container);
}
The full code in a fiddle
I have 4 <div> tag and <a> tag for each <div> tags.
In each and every div tag i have inserted 2 span tag and a a tag.
When the a tag is clicked i need to get the product name and the price of that div
Here is the demo http://jsfiddle.net/8VCWU/
I get the below warning message when i use the codes in the answer ...
Try this:
$(".get").click(function(e) {
e.preventDefault();
var $parent = $(this).closest(".item");
var itemName = $(".postname", $parent).text();
var itemPrice = $(".price", $parent).text();
alert(itemName + " / " + itemPrice);
});
Example fiddle
Note that you had a lot of repeated id attributes which is invalid code and will cause you problems. I've converted the #item elements and their children to use classes instead.
jQuery
$(".get").click(function(event){
event.preventDefault(); /*To Prevent the anchors to take the browser to a new URL */
var item = $(this).parent().find('#postname').text();
var price = $(this).parent().find('#price').text();
var result = item + " " + price;
alert(result)
});
DEMO
A Quick Note about id:
The id attribute specifies a unique id for an HTML element (the value must be unique within the HTML document).
A unique identifier so that you can identify the element with. You can use this as a parameter to getElementById() and other DOM functions and to reference the element in style sheets.
solution is below
use the blow code and try it
<a data-role="link" href="javascript:linkHandler('<%= obj.productname %>', '<%= obj.price %>')" class="get" >Add <a>
function linkHandler(name, price)
{
alert(name);
alert(price);
var name = name;
var price = price;
var cartItem = new item(name, parseFloat(price));
// check duplicate
var match = ko.utils.arrayFirst(viewModel.cartItems(), function(item){ return item.name == name; });
if(match){
match.qty(match.qty() + 1);
} else {
viewModel.cartItems.push(cartItem);
var rowCount = document.getElementById("cartcontent1").getElementsByTagName("TR").length;
document.getElementById("Totala").innerHTML = rowCount;
}
}
with jQuery
$('a.get').on('click',function(){
var parent = $(this).parent();
var name = $(parent+' #postname').text();
var price = $(parent+' #price').text();
});
Or again:
$('a').click(function(e){
e.preventDefault();
var $price = $(this).siblings('#price').text();
var $postname = $(this).siblings('#postname').text();
alert($price);
alert($postname);
});
Try
function getPrice(currentClickObject)
{
var priceSpan = $(currentClickObject).parent("div:first").children("#price");
alert($(priceSpan).html());
}
and add to your a tag:
...
I'd suggest to use classed instead of id if you have more than one in your code.
The function you're looking for is siblings() http://api.jquery.com/siblings/
Here's your updated fiddle:
http://jsfiddle.net/8VCWU/14/
Hi I cleaned up the HTML as mentioned using the same Id more than once is a problem.
Using jQuery and the markup I provided the solution is trivial.
Make a note of the CSS on the below fiddle
http://jsfiddle.net/8VCWU/27/
$(document).ready(function(){
$("#itmLst a.get").click(function(){
var $lstItm = $(this).parents("li:first");
var pName = $lstItm.find("span.postname").html();
var price = $lstItm.find("span.price").html();
alert("Product Name: " + pName + " ; Price: " + price);
});
});
I have made some changes in your html tags and replace all repeated Ids with class, because you have repeated many ids in your html and it causes trouble so it is wrong structure. In HTML, you have to give unique id to each and every tag. it will not be conflicted with any other tag.
Here i have done complete bins demo. i have also specified all alternative ways to find tag content using proper jQuery selector. the demo link is as below:
Demo: http://codebins.com/bin/4ldqp8v
jQuery
$(function() {
$("a.get").click(function() {
var itemName = $(this).parent().find(".postname").text().trim();
var itemPrice = $(this).parent().find(".price").text().trim();
//OR another Alternate
// var itemName=$(this).parents(".item").find(".postname").text().trim();
// var itemPrice=$(this).parents(".item").find(".price").text().trim();
//OR another Alternate
//var itemName=$(this).closest(".item").find(".postname").text().trim();
// var itemPrice=$(this).closest(".item").find(".price").text().trim();
//OR another Alternate
//var itemName=$(this).siblings(".postname").text().trim();
//var itemPrice=$(this).siblings(".price").text().trim();
alert(itemName + " / " + itemPrice);
});
});
Demo: http://codebins.com/bin/4ldqp8v
You can check above all alternatives by un-commenting one by one. all are working fine.
I have asked a question about how to avoiding to write the html in the js,then some people tell me using the javascript template,for example,the jquery/template pugin and ect.
It is a good idea when generate static html,for example:
<ul id="productList"></ul>
<script id="productTemplate" type="text/x-jquery-tmpl">
<li><a>${Name}</a> (${Price})</li>
</script>
<script type="text/javascript">
var products = [
{ Name: "xxx", Price: "xxx" },
{ Name: "yyy", Price: "xxx" },
{ Name: "zzz", Price: "xxx" }
];
// Render the template with the products data and insert
// the rendered HTML under the "productList" element
$( "#productTemplate" ).tmpl( products )
.appendTo( "#productList" );
</script>
However when I try to bind some event to the generated html,I meet some problem.
For example,I have a page which user can search some products by the price/name/location.
So I have three function:
searchByPrice(lowPrice,highPrice,productType,currentPage)
searchByName(name,productType,currentPage);
searchByLocation(location,currentpage);
ALl the above function have a realated method in the server side and they will retrun the products usint the xml format.
Since they will retrun so many items,so I have to paging them,the "currengPage" is used to tell the server side which part of results should be returned.
When the client get the result from the server side,now it is the js for display them int he div and create a Paging Bar if possible.
Before I know the template,I use this manner(which I hate most,try my best to avoid):
function searchByPrice(lowPrice,highPrice,productType,currentPage){
var url="WebService.asmx/searchByPrice?low="+lowPrice="&high="+highPrice+"&curPage="+currentPage;
//code to create the xmlHttp object
xmlhttp.open("GET",url,true);
xmlhttp.onreadystatechange=function(){
if (xmlhttp.readyState==4 && xmlhttp.status==200){
var i=0;
var Prohtml="";
var proList=parseProductList(xmlhttp.responseText);
for(i=0;i<prolist.length;i++){
Prohtml+="<li><a href='#'>"+prolist[i].name+"</a> ("+prolist[i].price"+)</li>";
}
//generate the paging bar:
var totleResult=getTotleResultNumber(xmlhttp.responseText);
if(totleResult>10){
var paghtml="<span>";
//need the paging
var pagNum=totleResult/10+1;
for(i=1;i<=pagenum;i++){
paghtml+="<a onclick='searchByPrice(lowPrice,highPrice,productType,currentPage+1)'>i</a>";
//here the synax is not right,since I am really not good at handle the single or doule '"' in this manner.
//also if in the searchByName function,the click function here should be replaced using the searchByName(...)
}
}
}
}
}
In the example,it is easy to use the template to generate the "Prohtml" since there is no event handling with them,but how about the "paghtml",the click function is different in differnt search type.
So,any good idea to hanld this?
Either:
Create DOM Elements instead of building HTML strings, using document.createElement or a small library if you're doing lots of this, which will allow you to attach events immediately in the usual fashion.
or
Give each element which needs to make use of event handlers a unique ID and build up a list of events to be attached once the HTML has been inserted into the document.
E.g.:
var eventHandlers = []
, eventCount = 0;
for (i = 1; i <= pagenum; i++) {
var id = "search" + eventCount++;
html += "<a id='" + id + "'>" + i + "</a>";
eventHandlers.push([id, 'click',
handler(searchByPrice, lowPrice, highPrice, productType, currentPage + i)])
}
// Later...
someElement.innerHTML = html;
registerEvents(eventHandlers);
Where registerEvents is:
function registerEvents(eventHandlers) {
for (var i = 0, l = eventHandlers.length; i < l; i++) {
var eventHandler = eventHandlers[i],
id = eventHandler[0],
eventName = eventHandler[1],
func = eventHandler[2];
// Where addEvent is your cross-browser event registration function
// of choice...
addEvent(document.getElementById(id), eventName, func);
}
}
And handler is just a quick way to close over all the arguments passed in:
/**
* Creates a fnction which calls the given function with any additional
* arguments passed in.
*/
function handler(func) {
var args = Array.prototype.slice.call(arguments, 1);
return function() {
func.apply(this, args);
}
}
I use something like this approach (but automatically adding unique ids when necessary) in the HTML generation portion of my DOMBuilder library, which offers a convenience method for generating HTML from content you've defined, inserting it into a given element with innerHTML and registering any event handlers which were present. Its syntax for defining content is independent of output mode, which allows you to switch between DOM and HTML output seamlessly in most cases.
First of all, you can simply use the $.get() or $.ajax() for your AJAX call.
Secondly, you can use .live() or .delegate() to bind events to elements that do not exist.
Thirdly, you can use the data attributes in the anchor elements as a way to pass in the arguments for the event handler, see .data().
So, to rewrite your function, you have may something like the following:
function searchByPrice(event) {
$this = $(this);
var lowPrice = $this.data('lowPrice'),
highPrice = $this.data('lowPrice'),
productType = $this.data('productType'),
currentPage = $this.data('currentPage');
var url = "WebService.asmx/searchByPrice?low=" + lowPrice = "&high=" + highPrice + "&curPage=" + currentPage;
$.get(url, function(data, textStatus, jqXHR) {
var i = 0;
var Prohtml = "";
var proList = parseProductList(data);
for (i = 0; i < prolist.length; i++) {
Prohtml += "<li><a href='#'>" + prolist[i].name + "</a> (" + prolist[i].price "+)</li>";
}
//generate the paging bar:
var totleResult = getTotleResultNumber(data);
if (totleResult > 10) {
var paghtml = "<span>";
//need the paging
var pagNum = totleResult / 10 + 1;
for (i = 1; i <= pagenum; i++) {
paghtml += '<a class="pagelink" ' +
'data-lowPrice="' + lowPrice + '" ' +
'data-highPrice="' + highPrice + '" ' +
'data-productType="' + productType + '" ' +
'data-currentPage="' + (currentpage + 1) + '">' + i + '</a>';
//here the synax is not right,since I am really not good at handle the single or doule '"' in this manner.
//also if in the searchByName function,the click function here should be replaced using the searchByName(...)
}
}
});
}
$(document).ready(function(){
$("a.pagelink").live('click', searchByPrice);
});