I have list of products for users to select. When user selects a product, the price is displayed and the user enters the quantity to calculate the total price for all selected products.
Now with my code, the total price for the first selected product appears the same for all other products selected afterwards. i.e
If user selects Fifa and PRO the total price for FIFA appears for PRO as below
Item : Fifa
Price : 100eu
Quantity : 4
Total price : 400eu
Item : PRO
Price: 50eu
Quantity: 1
Total Price: 400eu
But this is what i am looking to have
Item : Fifa
Price : 100eu
Quantity : 4
Total price : 400eu
Item : PRO
Price: 50eu
Quantity: 1
Total Price: 50eu
Total Order Price = 400eu + 50eu = 450eu
JS
public selectGame(game){
$(".gameshop").append(
'<p id="name">Item: ' + game.name + '</p>'+
'<p name="price" id="price" data-price="'+game.price+'"> Price : ' + game.price + '</p>'+
'<input type="text" onKeyUp="final()" id="quantity" name="quantity" />' +
'<p id="final_price">Total Price $:<span></span></p>').appendTo('form')
}
function final()
{
a = Number($('#quantity').val());
b = Number($('#price').data("price"));
c = a * b;
$("#final_price span").text(c);
}
HTML
<form class="form-horizontal" method="post">
#foreach ($errors->all() as $error)
<p class="alert alert-danger">{{ $error }}</p>
#endforeach
#if (session('status'))
<div class="alert alert-success">
{{ session('status') }}
</div>
#endif
{!! csrf_field() !!}
<fieldset>
<div class="panel label_container">
<input onclick="return selectGame(this)" type="checkbox" id="{!! $game->id !!}" " />
<label class="mylabel" > </label>
</div>
<legend>Games</legend>
<div class="gameshop" id="gameshop">
</div>
</div>
</fieldset>
</form>
Update
function selectGame(game)
{
$('.gameshop').append(
'<div class="item">'+
'<p class="name"> Item: ' + game.name + '</p>'+
'<p class="price" data-price="'+game.price+'"> Price : ' + game.price + '</p>'+
'<input type="text" class="quantity" name="quantity" />'+
'<p class="total">Total Price $:<span></span></p>'+
'</div>'
)
$('.gameshop').on('keyup','.quantity',function()
{
var a = Number($(this).val());
var b = Number($(this).closest('div').find('.price').data('price'));
alert(b);
});
}
There are a few steps to correct this, since there are a couple of simultaneous problems. Namely (1) you're re-using id values in your HTML and (2) you're trying to get and update values globally on the page when you want to get do so relatively to the element invoking the event.
First, replace all of your ids in your dynamic HTML string with class values instead. This will prevent the HTML from being invalid by re-using id values. Something like:
'<p class="name">Item: ' + game.name + '</p>'+
//... and so on
Also, remove your onKeyUp inline handler from the <input> element:
'<input type="text" class="quantity" name="quantity" />'+
Next, bind your keyup event using jQuery and event delegation. Have a single declared event handler on a parent element to handle the event:
$('.gameshop').on('keyup', '.quantity', function () {
//...
});
This event handler replaces your final() function. This should take care of the multiple-id problem, but now inside this event handler we need to navigate the DOM to find the elements you're looking for.
First, wrap your dynamic elements in a container of some sort. Any <div> will do. Something like:
'<div class="product">'+ // <--- here
'<p class="name">Item: ' + game.name + '</p>'+
//...
'<p class="final_price">Total Price $:<span></span></p>'+
'</div>' // <--- and here
Then we can use that container as a stopping point to navigate up and down the DOM to find the target elements. First, we can find the "quantity" from the event-throwing element itself. So instead of this:
a = Number($('#quantity').val());
We can just use the element itself:
var a = Number($(this).val());
For other values, it's a neighboring element. This would involve navigating up and down the DOM to find that element. So instead of this:
b = Number($('#price').data("price"));
You can do something like this:
var b = Number($(this).closest('div').find('.price').data('price'));
Notice how it starts from the element throwing the event (this), navigates up to the container (<div>), then back down to the target element (.price). This should identify the specific element you want to find, relative to the one throwing the event.
Repeat these same corrections for your other elements, selectors, etc.
There could very well be other issues here, but these appear to be the main structural/logical problems you're encountering. (Another potential problem could be that strange combined use of .append() and .appendTo(), I'm not really sure what that's accomplishing. You should only need one of those.)
You are appending dynamically tags with same ids, consider adding something to the id such
id="xxx"+"_"+i
being i an index
Related
I am creating an invoice system using Laravel, JQuery and Bootstrap-3-typeahead and have created a button that dynamically adds new table row with text inputs in each table data on selection of a list item using JQuery. The first text input in each row has bootstrap-3-typeahead functionality: After selecting an item like this
creating invoice tutorial.
After selecting from the autocomplete text-input dropdown list, it automatically populates the price, quantity and assign a default value of "1" to the quantity input using the below Typeahead(afterSelect method) implementation using JQuery and Ajax GET method.
This is my
html markup screenshot.
My JQuery implementation of boostrap-3-typeahead and generation of auto price, quantity, and total values base on selected item from typeahead dropdown list.
var typeaheadSettings = {
source: function (query, process) {
return $.get(url, { query: query }, function (data) {
console.log(data)
return process(data);
});
},
afterSelect: function (data) {
console.log(data.sellPrice);
$('#itemPrice').val(data.sellPrice);
$('#itemQuantity').val('1');
var price = $('#itemPrice').val();
var quantity = $('#itemQuantity').val();
var subtotal = price * quantity;
$('#itemSubtotal').val(subtotal);
}
};
The JS code that adds new row on button click:
// Add Row
$(document).on('click', '#btnAddRow', function(event) {
var i=$('#invoiceTable tbody tr').size();
html = '<tr>';
html += '<td><p class="text-center pt-xs"><input type="radio" name="sn" id="sn" value=""></p></td>';
html += '<td><input type="text" name="productName[]" id="itemName'+ i +'" class="form-control typeahead twitter-typeahead" placeholder="Item name" autocomplete="off" data-fit-to-element="true" data-provide="typeahead"></td>';
html += '<td><input type="text" name="price[]" id="itemPrice'+i+'" class="form-control text-right" placeholder="Item price" autocomplete="off" readonly></td>';
html += '<td><input type="number" name="quantity[]" id="itemQuantity'+ i +'" class="form-control text-right" placeholder="Quantity"></td>';
html += '<td><input type="text" name="subtotal[]" id="itemSubtotal'+ i +'" class="form-control text-right" placeholder="Subtotal" readonly></td>';
html += '<td class="actions-hover actions-fade text-center"><p><a id="btnDelRow"><i class="fa fa-minus-circle fa-lg text-warning" id="iconDelRow"></i></a></p></td>';
html += '</tr>';
$('#invoiceTable tbody').append(html);
i++;
$('.typeahead').trigger('added');
});
This is what the markup of the dynamically added row looks like, with integer "1,2" suffixed to the ID.The integer increments if another row is added dynamically, giving all duplicate controls a unique ID.
dynamically added tr screenshot
The problem is that when i add a second control dynamically by clicking the "Add New Row" button shown on the image above, the dynamically added price and quantity input controls don't get the newly generated values. Instead, the values appear on the first already defined row. The same thing happens when i add the 3rd row and more.
I'd be glad if someone can help me out with a way i can pass the values generated after dropdown list item is selected to the right or appropriate inputs.
The HTML selector you are using seems to be an ID selector which should be unique per document. This may explain why this is happening.
It seems the values appear on the first defined row because you have coded only their IDs into the typeaheadSettings.afterSelect(...) function:
$('#itemPrice').val(data.sellPrice);
$('#itemQuantity').val('1');
var price = $('#itemPrice').val();
var quantity = $('#itemQuantity').val();
var subtotal = price * quantity;
$('#itemSubtotal').val(subtotal);
You should probably refactor the function so it can use the other auto-generated IDs like #itemPrice1, #itemSubtotal2, ...
I'm learning and trying to put together a little bit of jquery. Admittedly I'm finding it difficult to find a good basics guide, particularly, when adding multiple actions to one page.
I read somewhere that the document listener should only be used once. I believe I'm using it twice here, but not 100% sure how to bring it into one listener.
Also because I've been hacking bits of script together, I think I'm using parts of javascript and parts of jQuery. Is this correct?
A critique of the code below [which does work] and any advice on how best to approach learning jQuery would be most helpful. Thanks.
Script 1 styles a group of 3 radio buttons depending on which one is clicked.
Script 2 appends new inputs to the bottom of a form.
var stateNo = <?php echo $HighestPlayerID; ?> + 1;
$(document).on('click', 'input', function () {
var name = $(this).attr("name");
if ($('input[name="' + name + '"]:eq(1)')[0].checked) {
$('label[name="' + name + '"]:eq(1)').addClass('nostate');
$('label[name="' + name + '"]').removeClass('selected');
}
else {
$('label[name="' + name + '"]').removeClass('nostate selected');
if ($('input[name="' + name + '"]:eq(0)')[0].checked) {
$('label[name="' + name + '"]:eq(0)').addClass('selected');
}
else {
$('label[name="' + name + '"]:eq(2)').addClass('selected');
}
}
});
$(document).on('click', 'button[name=btnbtn]', function () {
var stateName = 'state[' + stateNo + ']';
var newPlayerAppend = `` +
`<tr><td>` +
`<input type="hidden" name="state['` + stateNo + `'][PlayerID]" value="` + stateNo + `" /></td>` +
`<td><input name="` + stateName + `[Name]" value="Name"></td>` +
`<td><input name="` + stateName + `[Team]" type="radio" value="A">` +
`<td><input name="` + stateName + `[Team]" type="radio" value="">` +
`<td><input name="` + stateName + `[Team]" type="radio" value="B">` +
`</td></tr>`;
$("tbody").append(newPlayerAppend);
stateNo++;
});
HTML for the 3 radio button inputs
<td class="Choice">
<label name="state[1][Team]" class="teampick Astate ">A
<input name="state[1][Team]" type="radio" value="A" />
</label>
<label name="state[1][Team]" class="smallx nostate ">X
<input name="state[1][Team]" type="radio" value="" checked />
</label>
<label name="state[1][Team]" class="teampick Bstate">B
<input name="state[1][Team]" type="radio" value="B" />
</label>
</td>
Some of the code can be written more concisely, or more the jQuery way, but first I want to highlight an issue with your current solution:
The following would generate invalid HTML, if it were not that browsers try to solve the inconsistency:
$("tbody").append(newPlayerAppend);
A tbody element cannot have input elements as direct children. If you really want the added content to be part of the table, you need to add a row and a cell, and put the new input elements in there.
Here is the code I would suggest, that does approximately the same as your code:
$(document).on('click', 'input', function () {
$('label[name="' + $(this).attr('name') + '"]')
.removeClass('nostate selected')
.has(':checked')
.addClass(function () {
return $(this).is('.smallx') ? 'nostate' : 'selected';
});
});
$(document).on('click', 'button[name=btnbtn]', function () {
$('tbody').append($('<tr>').append($('<td>').append(
$('<input>').attr({name: `state[${stateNo}][PlayerID]`, value: stateNo, type: 'hidden'}),
$('<input>').attr({name: `state[${stateNo}][Name]`, value: 'Name'}),
$('<input>').attr({name: `state[${stateNo}][Team]`, value: 'A', type: 'radio'})
)));
stateNo++;
});
There is no issue in having two handlers. They deal with different target elements, and even if they would deal with the same elements, it would still not be a real problem: the DOM is designed to deal with multiple event handlers.
There are 2 places you are using anonymous functions. If the code block moves to a named function, the entire code becomes more maintainable. It also helps better in debugging by telling you upfront which function name the error may lie in.
Once you have named functions you will realise that you really do have 2 event listeners for click. So there isn't much benefit of moving them in one listener (or one function you may be referring to). These both event listeners attach on document object and listen to a click event.
Class names are always better when hyphenated. a-state over Astate.
If it works it is correct code, for once you asked about correctness.
It is absolutely fine to have multiple listeners but I usually prefer making everything under one roof. Consider making code as simple as possible which saves lot of time during maintenance.
you can use $(function() {}) or document.ready().
$(document).ready(function() {
$('input[type="radio"]').click(function() {
var thisa = $(this).parent();
var name = $(this).attr("name");
// Remove :selected class from the previous selected labels.
$('label[name="' + name + '"]').removeClass('selected');
// Add conditional class with tenary operator.
thisa.parent().hasClass("smallx") ? thisa.addClass('nostate') : thisa.addClass('selected');
});
$('button[name=btnbtn]').click(function() {
var stateName = 'state[' + stateNo + ']';
// Add TR and TD before appending the row to tbody
var newPlayerAppend = `<tr><td>` +
`<input type="hidden" name="state['` + stateNo + `'][PlayerID]" value="` + stateNo + `" />` +
`<input name="` + stateName + `[Name]" value="Name">` +
`<input name="` + stateName + `[Team]" type="radio" value="A"></td></tr>`;
$("tbody").append(newPlayerAppend);
stateNo++;
});
});
Hope this helps.
My current page looks like this WITHOUT the input box. The problem I'm having is with the input. I'm going to have multiple controls with the same id name, and I want the value to be different for each row. What should I do to automate this? If I hardcoded it, I would still get the same ID tag on each loop iteration. My goal is to be able to add and delete entire target groups at the end
Target Name: text <--- what it looks like now
Command Line: [ input box ] <----- desired output
Build Env: [ input box ]
Rel Out: [ input box ]
Rel Log: [ input box ]
Dep: [ input box ]
my JS looks like:
for (var i = 0; i < records.length; i++) {
var row = $('<tr></tr>');
row.append($('<tr></tr>').text("Target Name: " + records[i].TargetName));
row.append($('<tr></tr>').text("Command Line: " + records[i].CommandLine));
row.append($('<tr></tr>').text("Build Environment: " + records[i].BuildEnvTypeName));
row.append($('<tr></tr>').text("Relative Output Path: " + records[i].RelativeOutputPath));
row.append($('<tr></tr>').text("Relative Log Path: " + records[i].RelativeLogPath));
row.append($('<tr></tr>').text("Dependencies: " + records[i].Dependencies));
$('#AddTargetTable').append(row);
}
the input box part I want to append for each part (this is specific for the target name one):
<div class="control-group">
<div class="controls">
<input type="text" id="target-name" value="<% =TargetName %>" />
</div>
</div>
if you pass a unique identifier along with your record data you can add that identifier to each row. You don't need element ID's for tasks like this.
/* add identifier to start of row */
var row = $('<tr data-id="'+records[i].id +'"></tr>');
Then when you interact with a row you can pull the data-id using jQuery.data() method.
As for the input's you need to use html() instead of text() for cell data.
A simple template function will help keep the clutter down
function inputTemplate( value, type){
return ' <div class="control-group">'+
'<div class="controls">'+
'<input type="text" value="'+value+'" data-type="'+type+'" />'+
'</div>'+
'</div>';
}
Adding cells:
row.append($('<td>').html("Command Line: " + inputTemplate( records[i].CommandLine, 'Command')));
Now add a change handler for the input's. Will use similar row traversal approach for delete row.
$('.controls input').change(function(){
var rowId = $(this).closest('tr').data('id');
var newValue=$(this).val();
var fieldType= $(this).data('type')
alert('new value for '+ fieldType +' in row ' + rowId +' is ' + newValue );
updateServer( rowId, fieldType, newValue);
})
I'm pretty new to JQuery, as you can tell by my question...
The user can append many new input fields to the form. This works great, but how can they delete a specific field? If they append 5 input fields, how do they delete lets say the third field?
Below is my following code. What is currently does is always delete the first item when clicked.
$("#addNewItem").click(function(){
$("#invoice_items").append('<input type="text" name="name[]" value="name" id="item_name" class="item_name" /><img src="images/delete.png" />');
});
$("#delete_input").live("click", function(){
$("#item_name").remove();
$(this).remove();
});
How about using additional container for inputs?
http://jsfiddle.net/dFpMV/
$("#addNewItem").click(function(){
$("#invoice_items").append('<div class="input-container"><input type="text" name="name[]" value="name" id="item_name" class="item_name" />X<img src="images/delete.png" /></div>');
});
$("#delete_input").live("click", function(){
$(this).parent().remove();
});
First, count the number of inputs you've added and store it in a variable.
Then, when you add the element, make a unique identifier based on that number.
$("#invoice_items").append('<input type="text" name="name[]" value="name" id="item'+count'" class="item_name" /><img src="images/delete.png" />');
I would avoid using the specific item name as the id in this case, use something generic like item0, item1 etc.
Then, to remove
$("#item" + desiredNumber).remove();
$(this).remove();
all links need to have unique id. Allowing to append element with specified id twice is an error. What you could do is to add an artificial number at the end of id to make them unique. I would wrap both input and link into a div, i would assign an unique id to it, assign a class to delete link instead of id and remove div like ($this).parent().remove()
If you are using jQuery 1.7+: Also note that .live() is deprecated and you should use .on() instead (note that syntax is however a little bit different).
I made 2 examples for you and adding a dummy variable so you can see whats happend:
1 If you know how to DOM will look like and the relationship between the delete link and the input you can simply traversing to the previous item.
$("#delete_input").live("click", function(){
$(this).prev().remove();
$(this).remove();
});
http://jsfiddle.net/JgKRw/ Example nr 1 in action
2 You give each item a unique number when you add them to the DOM.
var dummyId = 0;
$("#addNewItem").click(function(){
dummyId++;
$("#invoice_items").append('<input type="text" name="name[]" value="name ' + dummyId + '" id="item_name" class="item_name" data-id="' + dummyId + '" /><a data-id="' + dummyId + '" href="#" id="delete_input">' + dummyId + '<img src="images/delete.png" /></a>');
});
$("#delete_input").live("click", function(){
var deletedId = $(this).data("id"); // Get the ID of the clicked link
$("input[data-id='" + deletedId + "']").remove(); // Seach for an input which has the ID
$(this).remove();
});
http://jsfiddle.net/JgKRw/1/ Example nr 2 in Action
I would implemented number 2, couse else you have to take care of the script if you want to change the UI.
Btw you should only have one element assigned to an ID, so change your ID and use classes insteed.
http://api.jquery.com/class-selector/
Given the markup you appending it should be simply $(this).prev().remove(); and ignore the IDs.
Here's my fiddle:
http://jsfiddle.net/JfUAa/
(function () {
var count = 0,
items = document.getElementById("input_items"),
$items = $(items),
tpl = '<div><input type="text" id="{{id}}" />delete</div>';
function addItem(){
$items.append(tpl.replace("{{id}}", count++));
}
function remove(){
items.removeChild(this.parentNode);
}
$("#addNewItem").click(addItem);
$items.on("click", "a", remove);
}());
I have a simple ui which has a link that says "add item". When this is clicked, a pair of input boxes appears below it. If it is clicked again, yet another pair appears. I'm trying to think of the best way to generate these elements and turn it into some sort of json array of key value pairs (the first input element in each pair being the key and the second input element being the value).
Right now I just have a counter and I generate the ids using it, such as (in the click event of the "add item" link):
$('#features').append('<input id="feature-name-' + self.featureCount + '" type="text" name="asdf" /><a class="delete-feature" data-id="' + self.featureCount + '">Delete</a><input id="feature-description-' + self.featureCount + '" type="text" name="asdf" />');
I don't know what to use as the "name" attributes in order to make it easy to create a json array from them.
you can do something like this without using id attributes.
$('#features').append('<div><input type="text" />
<a class="delete-feature" data-id="' + self.featureCount + '">Delete</a><input type="text" /></div>');
And your javascript,
var yourArray=[];
$('#yourButton').click(function(){
$('#features div').each(function(){
var div=$(this);
var k=$('input:first',div).val();
var v=$('input:first',div).next().val();
yourArray.push({key:k, value: v});
});
});
It doesn't matter what you use for a name attribute, so long as there name and description names are different. Let's say that these elements are all appended to a form with the id myform. Give each pair its own wrapper object. Here, I've used a div, but a fieldset is equally appropriate.
$('#features').append(
'<div class="feature-div">
'<input id="feature-name-' + self.featureCount + '" type="text" name="asdf" />' +
'<a class="delete-feature" data-id="' + self.featureCount + '">Delete</a>' +
'<input id="featurena-description-' + self.featureCount + '" type="text" name="asdf" />' +
'</div>');
Now, it's possible to extract each pair sensibly:
var myarray = [];
$('#myform .feature-div').each(function(i, v) {
myarray.push([
$('input[name=name]', v).val(), $('input[name=description]', v).val()]);
});
Or however you want the data to be presented.