I have dynamic object (button and textbox) create by jquery where each time I press Add Transport. I took this code from this forum and modify little bit to suit my situtions (thanks to whom create this code).
Let me detail first regarding my dynamic items:
My set of group data are Trip[], Bus No[] and Amount[] .
This set can be multiple but consistent with 3 items only each group.
trip[] = button object
busno[] = text object
amount = text object
Below are my HTML script:
<div class="purchase-items-fieldset" style="clear:both;">
<div class="purchase-items-wrapper">
<div class="purchase-items">
<ul>
<li>
<input type="button" name="trip[]" value="PB" class="field btn-field">
</li>
<li>
<input type="text" name="busno[]" class="field txt-field">
</li>
<li>
<input type="text" name="amount[]" class="field txt-field">
</li>
</ul>
<input type="button" class="remove-line btn-remove" style="border:solid">
</div>
</div>
<button type="button" id="btnAddTrans" class="add-field" style="display: none">Add field</button>
</div>
Let says I have 2 set group data like below***(mydata="PB,WBX001,1000,P,WBK001,500")***
Then, my plan is:-
Group 1
trip[]=PB
busno[]=WBX001
amount[]=1000
Group 2
trip[]=P
busno[]=WBK001
amount[]=500
where I want Jquery/Javascript create 2 group dynamic object and assign value each object base on plan above :-
function assigndatatrip (mydata) {
//mydata="PB,WBX001,1000,P,WBK001,500"
//each 3 item are 1 set group data etc: PB,WBX001,500
//do split function and count how many set group
// create dynamic object
// assign data for dynamic object
//loop
//else no more data to assign then exit-return
}
I no idea where to start because I not so good on javascript/jquery function.
I hope anybody who face this problem can share and help me how to solve this problem. Thanks on advance who's reading and reply this question.
Thanks you.
If you will have that structure for your data : "each three items a group".
Then you can apply this logic:
$(document).ready(function() {
//Initialize vars
var start,
mydata = ["PB", "WBX001", "1000", "P", "WBK001", "500"]
//Loop to create items based on the array amount/3
for (start = 0; start < (mydata.length / 3); start++) {
//Target the elements of the array you need for each group
var trip = start * 3,
busno = trip + 1,
amount = trip + 2;
//Create the element to append
var item = "<div class='item'><ul><li>"
+ mydata[trip] + "</li><li>"
+ mydata[busno] + "</li><li>"
+ mydata[amount] + "</li></ul></div>";
//Append the group element
$('body').append(item);
}
})
.item {
color: white;
background: purple;
margin: 20px auto;
padding: 25px;
}
.item li {
list-style: none
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
I must thanks to DaniP who have spirit and kindness, helping without asking. Thanks again.
I think and think many time regarding problem above, I can't use DaniP entire solution because I already have function AddTrans, SaveTrans and many more. My critical part only how to display back all a data I already save. Since object are dynamic my head getting sick to think and honestly I really bad on javascript/jquery coding.
Ok. If anybody using Dynamic Object inside Div and already run perfectly but do not know how to display back a data while create dynamic object then please consider my code below are one of solution on your reference.
function assigndatatrans(mydata) {
//var mydata = "PB,WBX001,500,P,WBK001,300"
var gdata = mydata.split(',').length / 3;
for (start = 0; start < gdata - 1; start++) {
$('#btnremove').click();
clickaddtrans();
}
var substr = mydata.split(',');
$.each($('.field'), function (index,value) {
$(this).val(substr[index]);
});
}
Let me explain my code for myself (I still on learning process JQuery function):-
First thing first is data string get from database
//var mydata = "PB,WBX001,500,P,WBK001,300"
then I need to count how many group data I have, each 3 item are 1 group then I need do small calculation
var gdata = mydata.split(',').length / 3;
After that, I know I already have function AddTrans and RemoveTrans then I need to clear first my dynamic object and show it again based how many group should be appear:-
for (start = 0; start < gdata - 1; start++) {
$('#btnremove').click();
clickaddtrans();
}
Finally, I learn some function $.each and Index and Value on JQuery recently and I try apply to make sure my data string can be perfectly insert into each field on my dynamic object. I think this part are really tricky for me and I try many time to make it working.
var substr = mydata.split(',');
$.each($('.field'), function (index,value) {
$(this).val(substr[index]);
});
That's all.... Now I can get my output run smoothly and workable. I can't say perfect! because I believe yours all who read this question can do better than this and far far more superior.
Anyway thanks so much and sleep well. :)
Related
I have a list of products, each individual product has a checkbox value with the products id e.g. "321". When the products checkbox is checked (can be more than 1 selected) i require the value to be collected. Each product will also have a input text field for defining the Qty e.g "23" and i also require this Qty value to be collected. The Qty text input should only be collected if the checkbox is checked and the qty text value is greater than 1. The plan is to collect all these objects, put them in to a loop and finally turn them in to a string where i can then display the results.
So far i have managed to collect the checkbox values and put these into a string but i'm not sure how to collect the additional text Qty input values without breaking it. My understanding is that document.getElementsByTagName('input') is capable of collecting both input types as its basically looking for input tags, so i just need to work out how to collect and loop through both the checkboxes and the text inputs.
It was suggested that i use 2 if statements to accomplish this but i'm new to learning javascript so i'm not entirely sure how to go about it. I did try adding the if statement directly below the first (like you would in php) but this just seemed to break it completely so i assume that is wrong.
Here is my working code so far that collects the checkbox values and puts them in a string. If you select the checkbox and press the button the values are returned as a string. Please note nothing is currently appended to qty= because i dont know how to collect and loop the text input (this is what i need help with).
How can i collect the additional qty input value and append this number to qty=
// function will loop through all input tags and create
// url string from checked checkboxes
function checkbox_test() {
var counter = 0, // counter for checked checkboxes
i = 0, // loop variable
url = '/urlcheckout/add?product=', // final url string
// get a collection of objects with the specified 'input' TAGNAME
input_obj = document.getElementsByTagName('input');
// loop through all collected objects
for (i = 0; i < input_obj.length; i++) {
// if input object is checkbox and checkbox is checked then ...
if (input_obj[i].type === 'checkbox' && input_obj[i].checked) {
// ... increase counter and concatenate checkbox value to the url string
counter++;
url = url + input_obj[i].value + '&qty=' + '|';
}
}
// display url string or message if there is no checked checkboxes
if (counter > 0) {
// remove first "&" from the generated url string
url = url.substr(1);
// display final url string
alert(url);
}
else {
alert('There is no checked checkbox');
}
}
<ul>
<li>
<form>
<input type="checkbox" id="checked-product" name="checked-product" value="311">Add To Cart
<div class="quantity">
<input type="text" name="qty" id="qty" maxlength="12" value="1" class="input-text qty"/>
</div>
</form>
</li>
<li>
<form>
<input type="checkbox" id="checked-product" name="checked-product" value="321">Add To Cart
<div class="quantity">
<input type="text" name="qty" id="qty" maxlength="12" value="10" class="input-text qty"/>
</div>
</form>
</li>
<li>
<form>
<input type="checkbox" id="checked-product" name="checked-product" value="98">Add To Cart
<div class="quantity">
<input type="text" name="qty" id="qty" maxlength="12" value="5" class="input-text qty"/>
</div>
</form>
</li>
</ul>
<button type="button" onclick="javascript:checkbox_test()">Add selected to cart</button>
My answer has two parts: Part 1 is a fairly direct answer to your question, and Part 2 is a recommendation for a better way to do this that's maybe more robust and reliable.
Part 1 - Fairly Direct Answer
Instead of a second if to check for the text inputs, you can use a switch, like so:
var boxWasChecked = false;
// loop through all collected objects
for (i = 0; i < input_obj.length; i++) {
// if input object is checkbox and checkbox is checked then ...
switch(input_obj[i].type) {
case 'checkbox':
if (input_obj[i].checked) {
// ... increase counter and concatenate checkbox value to the url string
counter++;
boxWasChecked = true;
url = url + input_obj[i].value + ',qty=';
} else {
boxWasChecked = false;
}
break;
case 'text':
if (boxWasChecked) {
url = url + input_obj[i].value + '|';
boxWasChecked = false;
}
break;
}
}
Here's a fiddle showing it working that way.
Note that I added variable boxWasChecked so you know whether a Qty textbox's corresponding checkbox has been checked.
Also, I wasn't sure exactly how you wanted the final query string formatted, so I set it up as one parameter named product whose value is a pipe- and comma-separated string that you can parse to extract the values. So the url will look like this:
urlcheckout/add?product=321,qty=10|98,qty=5
That seemed better than having a bunch of parameters with the same names, although you can tweak the string building code as you see fit, obviously.
Part 2 - Recommendation for Better Way
All of that isn't a great way to do this, though, as it's highly dependent on the element positions in the DOM, so adding elements or moving them around could break things. A more robust way would be to establish a definitive link between each checkbox and its corresponding Qty textbox--for example, adding an attribute like data-product-id to each Qty textbox and setting its value to the corresponding checkbox's value.
Here's a fiddle showing that more robust way.
You'll see in there that I used getElementsByName() rather than getElementsByTagName(), using the name attributes that you had already included on the inputs:
checkboxes = document.getElementsByName('checked-product'),
qtyBoxes = document.getElementsByName('qty'),
First, I gather the checkboxes and use an object to keep track of which ones have been checked:
var checkedBoxes = {};
// loop through the checkboxes and find the checked ones
for (i = 0; i < checkboxes.length; i++) {
if (checkboxes[i].checked) {
counter++;
checkedBoxes[checkboxes[i].value] = 1; // update later w/ real qty
}
}
Then I gather the Qty textboxes and, using the value of each one's data-product-id attribute (which I had to add to the markup), determine if its checkbox is checked:
// now get the entered Qtys for each checked box
for (i = 0; i < qtyBoxes.length; i++) {
pid = qtyBoxes[i].getAttribute('data-product-id');
if (checkedBoxes.hasOwnProperty(pid)) {
checkedBoxes[pid] = qtyBoxes[i].value;
}
}
Finally, I build the url using the checkedBoxes object:
// now build our url
Object.keys(checkedBoxes).forEach(function(k) {
url += [
k,
',qty=',
checkedBoxes[k],
'|'
].join('');
});
(Note that this way does not preserve the order of the items, though, so if your query string needs to list the items in the order in which they're displayed on the page, you'll need to use an array rather than an object.)
There are lots of ways to achieve what you're trying to do. Your original way will work, but hopefully this alternative way gives you an idea of how you might be able to achieve it more cleanly and reliably.
Check the below simplified version.
document.querySelector("#submitOrder").addEventListener('click', function(){
var checkStatus = document.querySelectorAll('#basket li'),
urls = [];
Array.prototype.forEach.call(checkStatus, function(item){
var details = item.childNodes,
urlTemplate = '/urlcheckout/add?product=',
url = urlTemplate += details[0].value + '&qty=' + details[1].value;
urls.push(url)
});
console.log(urls);
})
ul{ margin:0; padding:0}
<ul id="basket">
<li class="products"><input type="checkbox" value = "311" name="item"><input type="text"></li>
<li><input type="checkbox" value = "312" name="item"><input type="text"></li>
<li><input type="checkbox" value = "313" name="item"><input type="text"></li>
</ul>
<button id="submitOrder">Submit</button>
I'm making a meal planning / grocery list application with JavaScript and jQuery. Basically, this is how it works:
The user adds recipes through a form. The user enters the name of the recipe as well as the ingredients associated with that recipe.
When submitted, each recipe is stored in a <dl id="recipeList"> element. The name of the recipe is stored as a <dt class="recipe"> and each ingredient is stored as a <dd class="ingredient">.
For each day of the week, the user may click on a "Plan a Meal" anchor. This brings up a copy of the #recipeList. When the user clicks on a <dt>, a class="meal" is applied to it and the rest of the list is removed.
The next step is for the user to click on the "Generate Grocery List" anchor. When the user does this, JavaScript should loop through each .meal and create an array, #mealsArray. JavaScript should then loop through each class="recipe" and check to see if the .innerHTML of it matches an item in the #mealsArray. It does this just fine, but the problem is after a match is found, it should get the children of the class="recipe" (i.e., the <dt class="ingredient">) and push them into #groceriesArray.
JavaScript will not find the children of the <dt class="recipe">. I have tried numerous ways of coding this, such as:
this.children
this.childNodes
this.children()
this.children("dt")
this.children(".ingredient")
this.contents()
this.find(".ingredient")
It usually finds something strange like [Object HTMLElement] or returns an error message like Type Error: this.children() is not a function.
It seems like this so be so simple, but I have no idea what to do. I will provide my code below — apologies for how sloppy it is.
Here is the HTML:
<form id="addRecipeForm">
<label>Name</label><input type="text" id="recipeName">
<label>Ingredients</label><input type="text" class="recipeIngredients">
<label>Ingredients</label><input type="text" class="recipeIngredients">
<label>Ingredients</label><input type="text" class="recipeIngredients">
<button id="recipeButton">Add Recipe</button>
</form>
<dl id="recipeList"></dl>
<div>
<h3>Sunday</h3>
Plan a Meal
</div>
<div>
<h3>Monday</h3>
Plan a Meal
</div>
<!-- And so on, until Saturday -->
Generate Grocery List
<ul id="groceryList"></ul>
Here is the JavaScript:
var recipeList = $("#recipeList");
var recipeIngredients = $(".recipeIngredients");
var planAnchor = $(".planAnchor");
var groceryListAnchor = $("#groceryListAnchor");
var groceryList = $("#groceryList");
////////// ADD A RECIPE //////////
$("#recipeButton").click(function(e) {
e.preventDefault();
var recipeName = $("#recipeName").val();
var recipeIngredients = $(".recipeIngredients");
recipeList.append("<dt class='recipe'></dt>");
recipeList.children("dt").last().text(recipeName);
for (i = 0; i < recipeIngredients.length ; i++) {
$("<dd class='ingredient'></dd>").text(recipeIngredients[i].value).appendTo(recipeList);
};
});
////////// PLAN A MEAL //////////
planAnchor.click(function(e) {
e.preventDefault();
var dayInPlanning = $(this).parent("div");
var availableRecipes = recipeList.clone();
availableRecipes.children("dd").remove();
availableRecipes.attr("id", "availableRecipes");
$(this).parent("div").append(availableRecipes);
$(this).remove();
availableRecipes.children("dt").click(function(e) {
e.preventDefault();
var selectedRecipe = $(this);
var para = $("<p class='meal'></p>");
para.appendTo(dayInPlanning);
para.text(selectedRecipe.text());
availableRecipes.remove();
});
////////// GENERATE GROCERY LIST //////////
///////// THIS IS WHERE THE PROBLEM LIES //////////
groceryListAnchor.click(function(e) {
e.preventDefault();
var mealsArray = [];
var groceriesArray = [];
// Create an array of .meal elements
$(".meal").each(function() {
mealsArray.push(this.innerHTML);
});
console.log("mealsArray is " + mealsArray);
$(".recipe").each(function() {
console.log("Checking " + this.innerHTML);
// Match the innerHTML of each .recipe to the items in the mealsArray
if ($.inArray(this.innerHTML, mealsArray) > -1) {
console.log("We found " + this.innerHTML + " in the array!");
// Get the children of that recipe, and place them in groceriesArray
// *** Not Working ***
groceriesArray.push(this.children.innerHTML)
} else {};
});
console.log("The grocery list is " + groceriesArray);
});
To explain this simply. They're two types of elements jQuery Elements and JavaScript Elements. this is a JavaScript element. jQuery functions only work with jQuery Elements. So to make it work, use:
$(this).myjQueryFunction();
so for you:
$(this).children();
In depth
When creating jQuery elements using $(), it does a few things. jQuery uses Sizzle for selecting elements. If what's passed into $() is already an element. It doesn't do anything. Of it is, it will turn it into an element. Depending on which of the two jQuery uses. It will return an element. This is a regular JavaScript element but what makes it so special? The jQuery functions can only be run after $. The reason is how you create chained-JavaScript functions using prototype:
//Kind of how it's created
$.prototype.children = function () {
//jQuery's code to get children
}
This is making it so children() can only be run off of $.
Can you try cast this Dom Element as a jQuery Element:
//so instead of this.children() use
$(this).children()
and in this case if you want HTML it will be
$(this).children().html()
but it will get you first child HTML only, you can try the followng to get for all:
html_contents = ""
$.each($(this).children(), function(){
html_contents+=$(this).html();
});
This post was on the right track, but I need one step further; my input names potentially have non-sequential index values assigned to them.
Background: Instead of a long series of checkboxes, I have a routine that builds a list from selecting values in a dropbox. This helps reduce real estate on the screen. So as the user selects values from the drop down, the script tries to make sure the selection does not already exist, and if it does, don't add it a second time.
The list generated looks like this:
<div id="Access:search_div">
<ul class="options">
<li onclick="removeRoleOptions(this,'Access:search_div');"><input type="hidden" name="Access:search[7]" value="1" />Standard</li>
<li onclick="removeRoleOptions(this,'Access:search_div');"><input type="hidden" name="Access:search[8]" value="1" />Premium</li>
<li onclick="removeRoleOptions(this,'Access:search_div');"><input type="hidden" name="Access:search[10]" value="1" />Lifetime</li>
<li onclick="removeRoleOptions(this,'Access:search_div');"><input type="hidden" name="Access:search[14]" value="1" />SysOp</li>
</ul>
</div>
I see plenty of examples of how to find the value, but it's just a Boolean, so if it's in the list, it will always be 1. What I need is to search the name and see what index value is attached. So for the above code, I want to compare the value of the item selected from the drop down against 7, 8, 10, and 14.
Here is the important part of the code I have so far:
function addRoleOptions(select, variable, output) {
var display=document.getElementById(output);
var option = select.options[select.selectedIndex];
var ul = display.getElementsByTagName('ul')[0];
if (!ul) {
ul = document.createElement("ul");
ul.setAttribute('class', 'options');
display.innerHTML = '';
display.appendChild(ul);
}
var choices = ul.getElementsByTagName('input');
for (var i = 0; i < choices.length; i++) {
if (choices[i].value == option.value) {
return;
}
}
option.value holds the number I'm comparing against.
So my question is, how do I parse out the value within the [] of the name to compare against the value being sent?
I am not so sure about the requirement, but then you can try using regex to extract the index from the name.
(choices[i].name).match(/[0-9]+/g) will give you the value between the []
and then you can use that value to the operation you need to do
var s = "Access:search[6543]";
var num = s.match(/\d+/)
I'd like to sort multi-column list without using a table. I know of solutions for single column lists but I can't seem to find one that sorts on multiple Ul/columns while keeping the list/row items together.
Ultimately I'd like to make something similar to what dropbox has done for displaying files. Yes, Dropbox doesn't use a sortable table. It's a list instead of tr's with a bunch of divs instead of td's.
I don't want to use a table because tables drive me crazy for styling purposes. Also, I don't want to use a plugin (like tablesorter.js) because javascript has a built in sort() function so it can't be that many lines of code.
Anyway, here is a nice single column demonstration taken from another SO question, and the original SO reference.
Basically I'd like to do what's in the fiddle but be able to sort by names, and for example addresses, here's a fiddle building upon this first fiddle where you can see where i'd like to go with this http://jsfiddle.net/trpeters1/De8Ku/955/. As you'll notice, this fiddle can't sort based on the address which is what I'd like find a solution for.
UPDATE
With many thanks to eterps (see below) now the fiddle can sort by multiple columns. I'd now like to next include sorting by date. Here is a fiddle which makes an attempt at this but doesn't quite work. You'll notice I tried to convert the innerHTML to a Date() object but this didn't solve this problem.
http://jsfiddle.net/trpeters1/De8Ku/983/
In this fiddle, can someone help explain why the sorting of the dates is still just alpha/numerical and not by date?
Using the code you provided in your jsFiddle examples, you can add some identifying information to your span tags to differentiate the text into columns. Then you can use that information to do your sort. I altered the jsFiddle example to use a sorting function that will look at whichever column is specified. I'm sure there are more elegant ways of doing this, but it should get you started.
http://jsfiddle.net/De8Ku/976/
Here is the code in case the jsFiddle isn't working or if someone want to edit this answer:
HTML:
<input type="button" id="test" value="sort by names"/>
<input type="button" id="test1" value="sort by address"/>
<ul id="list">
<li ><span class="name">peter</span><span class="address"> 812 jones st.</span></li>
<li><span class="name">zack</span><span class="address"> 512 jones st.</span></li>
<li><span class="name">alex</span><span class="address"> 712 jones st.</span></li>
<li><span class="name">sue</span><span class="address"> 112 jones st.</span></li>
<li><span class="name">jenny</span><span class="address"> 912 jones st.</span></li>
</ul>
JS:
function sortUnorderedList(ul, sortDescending, sortClass) {
if (typeof ul == "string") {
ul = document.getElementById(ul);
}
var lis = ul.getElementsByTagName("LI");
var vals = [];
// Instead of filling an array with list items,
// create an array of objects that contain the list item itself,
// and the value from the sort-by column
for (var i = 0, l = lis.length; i < l; i++) {
vals.push({
sortFieldVal: lis[i].getElementsByClassName(sortClass)[0].innerText,
val: lis[i].innerHTML
});
}
// Use a sort function to compare string values of sortFieldVal for each item
vals.sort(function(a, b) {
var nameA=a.sortFieldVal.toLowerCase(), nameB=b.sortFieldVal.toLowerCase()
if (nameA < nameB) //sort string ascending
return -1
if (nameA > nameB)
return 1
return 0 //default return value (no sorting)
});
if (sortDescending) vals.reverse();
for (var i = 0, l = lis.length; i < l; i++)
lis[i].innerHTML = vals[i].val;
}
window.onload = function() {
var desc = false;
document.getElementById("test").onclick = function() {
sortUnorderedList("list", desc, "name");
desc = !desc;
return false;
}
document.getElementById("test1").onclick = function() {
sortUnorderedList("list", desc, "address");
desc = !desc;
return false;
}
}
a nice member here helped me out to create a form which adds steps as you go. I modified it a bit to allow you to change the answers to some of those questions by clicking a change button. Of course, I realized my limits and applied it to only the first question, and badly...
The way I was approaching this was to give each question and button it's own unique id, which I think is probably the wrong way to approach this?
This is what I want and I partially accomplished some:
Fill out a field, press NEXT
Field 1 Turns into readonly text and a ChangeButton appears
a new field appears below that you can fill out
By pressing CHANGE on field one, field2 becomes non-editable and field1 is now editable. The change button also turns to "Save". By clicking save, you make field 2 editable and field1 non-editable again.
this continues on forever :)
I tried what I found on the internet but I hope that someone who's better at this could help me out a bit if that's ok :)
Here's my Jfiddle
http://jsfiddle.net/pufamuf/TEyVL/3/
Thank you!
Example of the following here
Here's how I'd accomplish what you're trying to do...
HTML:
<div id="question1" class="question active">
<label>Q1</label>
<input type="text" />
<input type="button" value="SAVE" class="button" />
</div>
jQuery:
var qa = []; // questions array of objects { text: "question text" }
$('.button').live('click', function(e) {
var $but = $(this),
$inp = $but.prev(),
$parent = $but.parent(),
i = $parent.attr('id').match(/\d+$/)[0] - 1,
$new;
if ($but.val() == 'SAVE') {
// store value to array
qa[i] = {
text: $inp.val()
};
// append new question inputs if needed
if (!$('#question' + (i + 2)).length) {
$new = $parent.clone();
$new.attr('id', 'question' + (i + 2));
$new.find('label').html('Q' + (i + 2));
$new.find('input[type="text"]').val('');
$new.insertAfter($parent);
}
// change to inactive attributes
$inp.attr('disabled', true);
$parent.removeClass('active').addClass('answered');
$but.val('CHANGE');
} else { // CHANGE
// change to active attributes
$inp.attr('disabled', false);
$parent.removeClass('answered').addClass('active');
$but.val('SAVE');
}
});
I made the array store objects so it's easy to add other properties to each question if needed.
See demo