How to loop over nested input forms with jquery - javascript

I have the following html form, which is generated dynamically:
<ul class="precursorList">
<li>
Precursor Name: <input name="precursorName" type="text">
<ul class="portList">
<li>Portname of precursor: <input name="precursorPort" type="text">
Portname of this: <input name="thisPort" type="text">
</li>
</ul>
</li>
<li>
<li>
Precursor Name: <input name="precursorName" type="text">
<ul class="portList">
<li>Portname of precursor: <input name="precursorPort" type="text">
Portname of this: <input name="thisPort" type="text">
</li>
</ul>
</li>
<li>
</ul>
I want to get the values using jquery, therefore I have defined this loop:
ports = [];
$(".precursorList :input").each(function() {
if(this.name == "precursorName") {
var precursorName_ = this.value
$(".portList :input ").each(function() {
if(this.name == "precursorPort") {
precursorPort_ = this.value;
} else if(this.name == "thisPort") {
ports.push({
filterName : precursorName_,
portNameOfFilter : precursorPort_,
portNameOfThis : this.value
});
}
});
}
});
Unfortunately this function does not work like I want it to work. The following loop $(".portList :input ").each( will always only loop over all elements in portList in the html document. How can I achieve that this loop will only loop over the corresponding portList, for each precursor?
UPDATE: The structure of the html element will stay the same only, the number of inputs will change. But there will only be a portList if a PrecursorElement exist.

id must be a unique field, instead use class to select multiple elements. Also use .live() for dynamically added elements.
EDIT:
If you are using Jquery > 1.7 use .on() instead of .live().
I tried following and it seems that its working
$(".precursorPort").each(function() {
temp = this.name;
$(this).parents(".portList").each(function() {
$(this).parents(".precursorList").each(function() {
alert(temp);
// use temp var and save it in ports
});
});
});
Check this fiddle : http://jsfiddle.net/Ajinkya_Parakh/DR233/
It may not be the best/most efficient but it is one of the working alternative.

An id must be unique in a document. If you want to create a group of similar things, then use a class instead.

I'd do something like this:
<ul id="precursorList">
<li>
Precursor Name1: <input class="precursor" name="precursorName1" id="precursorName1" type="text">
<ul id="portList1">
<li>Portname of precursor1: <input class="precursorPort" name="precursorPort1" id="precursorPort1" type="text">
Portname of this1: <input class="thisPort" name="thisPort1" id="thisPort1" type="text">
</li>
</ul>
</li>
<li>
Precursor Name2: <input class="precursor" name="precursorName2" id="precursorName2" type="text">
<ul id="portList2">
<li>Portname of precursor2 <input class="precursorPort" name="precursorPort2" id="precursorPort2" type="text">
Portname of this2: <input class="thisPort" name="thisPort2" id="thisPort2" type="text">
</li>
</ul>
</li>
</ul>
and then:
ports = [];
$("#precursorList > li").each(function()
{
precursor = $(this).find(".precursor")[0];
precursorPort = $(this).find(".precursorPort")[0];
thisPort = $(this).find(".thisPort")[0];
ports.push({filterName: precursor.value, portNameOfFilter: precursorPort.value, portNameOfThis: thisPort.value});
});

Step back a minute, I think you're overthinking this a bit.
Wrap your dynamic form in <form> tags. Then do a $('form').serialize() No need for crazy loops. Serialize is essentially a built-in, one line version of what you're already trying to do. Of course, serialize works on names so you'll have to have distinct ones (even if it's just name1, name2,etc) ID's must be unique too, so either fix that or drop them.
Simplicity always trumps crazy technical genius.
Fiddle for proof

Related

Retrieving the values from input elements and storing in an array

I've got a form that takes input from the user
<form id='myForm'>
<div id='x'>
<h2> start </h2>
<ul class="rounded">
<li><input type="text" placeholder="text" id="o" /></li>
</ul>
<h2>dests </h2>
<ul class="rounded">
<li><input type="text" placeholder="text" id="d" /></li>
</ul>
<ul class="rounded">
<li><input type="text" placeholder="text" id="d" /></li>
</ul>
</div>
<li class="arrow"><a href="#page2" onclick='function()'>Run </a></li>
</form>
I need a way of getting the user input from every field in the form, and placing it into an array. I have looked into getElementByTagName('input'), but this returns a HTMLCollection object. Any suggestions? (P.S i'm using a jqtouch if you're wondering what's up with the weird syntax)
You can use Array.prototype.map to create an array from the list of input elements obtained from querySelectorAll.
Try filling the inputs and clicking Run in the demo below:
var elements = document.querySelectorAll('#myForm input');
function callme() {
var result = Array.prototype.map.call(elements, function(e) {
return e.value;
});
console.log(result);
}
<form id='myForm'>
<div id='x'>
<h2> start </h2>
<ul class="rounded">
<li>
<input type="text" placeholder="text" />
</li>
</ul>
<h2>dests </h2>
<ul class="rounded">
<li>
<input type="text" placeholder="text" />
</li>
</ul>
<ul class="rounded">
<li>
<input type="text" placeholder="text" />
</li>
</ul>
</div>
<ul>
<li class="arrow"><a href="#page2" onclick='callme()'>Run </a>
</li>
</ul>
</form>
As others have mentioned be careful to use the html id attribute with a unique id, i.e. each input should have its own.
Using vanilla JavaScript, document.getElementsByTagName() returns a live html collection and you can access its members as properties using [index] to get the element you want. Then use textObject.value to access the inputs' value.
All in all, document.getElementsByTagName('input')[0].value will provide the value of the first input! That is the logic, also check the snippet.
Please also consider the following:
element.querySelectorAll() is generally slower than element.getElementsByTagName(), as the first uses a depth-first pre-order traversal of the document's nodes
element.querySelectorAll() returns a StaticNodeList
I found this article quite interesting for this topic.
function retrieve(){
let list = document.getElementById('inputListContainer');
let input = list.getElementsByTagName('input');
let array = [];
for( let i = 0; i < input.length; i++ ){
//input[i].value is the same as document.getElementsByTagName('input')[i].value
array.push(input[i].value);
}
console.log( array );
}
<!DOCTYPE html>
<html>
<head>
</head>
<body>
<div id='inputListContainer'>
<!-- Using a single ul to parent list items. Note that this does not affect the script -->
<ul id='inputList' class='rounded'>
<li><input type="text" placeholder="one"/></li>
<li><input type="text" placeholder="two"/></li>
<li><input type="text" placeholder="three"/></li>
</ul>
<button onclick='retrieve()'>Retrieve</button>
</div>
</body>
</html>
Try this:
function getValues() {
var values = []; // Array of input values
var elements = document.querySelectorAll("#myForm input");
for(var i = 0; i < elements.length; i++) {
values.push(elements[i].value);
//If you need the input ID:
//values.push({name: elements[i].id , value: elements[i].value});
}
}
For this you can use Document.querySelectorAll()
Returns a list of the elements within the document (using depth-first pre-order traversal of the document's nodes) that match the specified group of selectors. The object returned is a NodeList.
Using document.querySelectorAll("#myForm input"); will target all inputs within the form with the ID of myForm.
Then a for() loop is used to iterate through the collection and push the input values into the array .
The for statement creates a loop that consists of three optional expressions, enclosed in parentheses and separated by semicolons, followed by a statement (usually a block statement) to be executed in the loop.
for ([initialization]; [condition]; [final-expression])
statement
function BuildArray() {
var myArray = [];
var input = document.querySelectorAll("#myForm input");
for (var i = 0; i < input.length; i++) {
myArray.push(input[i].value);
}
console.log(myArray);
}
<form id='myForm'>
<div id='x'>
<h2> start </h2>
<ul class="rounded">
<li>
<input type="text" placeholder="text" id="o" value="first" />
</li>
</ul>
<h2>dests </h2>
<ul class="rounded">
<li>
<input type="text" placeholder="text" id="d" value="second" />
</li>
</ul>
<ul class="rounded">
<li>
<input type="text" placeholder="text" id="d" value="third" />
</li>
</ul>
</div>
<li class="arrow"><a href="#page2" onclick='BuildArray()'>Run </a>
</li>
</form>
I would also like to recommend keeping ID's unique if ID's are required but for this task ID's are not used in the selector so no problems if ID's are the same.
If you have any questions about the source code above please leave a comment below and i will get back to you as soon as possible.
I hope this helps. Happy coding!
You just need to iterate through each input and push each value into an array. You would call something like this on click. See jQuery example here.
function click() {
var arr = [];
$.each($('input'), function(k,v) {
arr.push($(v).val());
})
console.log(arr);
}

JQuery Multiple Hidden Values

I have the following HTML;
<ul class="list-group">
<li class="list-group-item">
www.andrewspiers.co.uk
<input type="hidden" name="url" value="www.andrewspiers.co.uk">
</li>
<li class="list-group-item">
wikipedia.org
<input type="hidden" name="url" value="wikipedia.org">
</li>
</ul>
The plan is to add a button to each row and then get the value from the hidden field when the button is clicked.
I have this;
alert( $('input[name="url"]').val() );
But that returns the value of the first row no matter which button is clicked.
This should work:
$('button').click(function(){
console.log($(this).closest('li').find('input[name="url"]').val())
})
You need to use $(this) within the click function to refer to that specific element and then traverse the DOM accordingly (multiple ways to skin a cat on this one) to get the hidden input.
jsFiddle example
Assuming your buttons are placed inside the same li, you need a more specific selector:
alert( $(this).siblings('input[name="url"]').val() );
You can add buttons like this :
$('.list-group-item').each(function(){
$(this).append('<input class="clicked" type="button" value="Click me"/>')
})
YOu can then get value of each hidden according to the button like this:
$('.clicked').on('click',function(){
var hiddenval =$(this).parent().find('input[name="url"]').val();
console.log(hiddenval);
})

get value of each class of span and put string to input

I have a list of values in a spans called 'tag-label'. I wish to add the value of each of these spans and insert them in a input field separated by commas.
I have set up a JSFiddle to show it. http://jsfiddle.net/stefanselby/7wrg611k/
My HTML:
<label for="TagsHolder">Keywords</label>
<ul>
<li><span class="tag-label">test 1</span></li>
<li><span class="tag-label">test 2</span></li>
<li><span class="tag-label">test 3</span></li>
</ul>
<input type="text" id="TagsHolder" name="TagsHolder" value="" />
<button id="btn">Update</button>
<p></p>
<p>This is what I require</p>
<input type="text" id="TagsHolder2" name="TagsHolder2" value="test 1,test 2,test 3" />
My Javascript (where I am stuck):
$(document).ready(function(){
$("#btn").click(function(){
$.each($('.tag-label'), function (index, value) {
$("#TagsHolder").val(index);
});
});
});
You can use .map to get an array, then join on a ,
var values = $(".tag-label").map(function() {
return $(this).text();
}).get().join(",");
$("#TagsHolder").val(values);
Fiddle: http://jsfiddle.net/7wrg611k/3/
Several problems with your approach.
index is the array index of the current element, not it's text.
Your loop will replace the value each pass, so only the last value will be seen since each previous value set will be replaced
Your best bet is to create an array and join that array:
$("#btn4").click(function () {
var values = $('.tag-label').map(function () {
return $(this).text();
}).get().join();
$("#TagsHolder").val(values);
});
DEMO

Get Checkbox to toggle div based on only the string inside a checkbox's label - jQuery

I'm trying to create a script to hide divs based on just the label of a "checked" checkbox. And do it without having to use a specific value, id or attribute. I was previously shown how to do it using spans/divs, but checkboxes have got me stumped.
My example in jsfiddle:
http://jsfiddle.net/6qLX7/2/
HTML:
<fieldset class="filter-row">
<div class="row-section">
<div class="row-heading">art</div>
<ul>
<li>
<div class="checkbox">
<input type="checkbox" name="studio art" value=".studio_art" id="studio_art" />
<label for="studio_art">studio art</label>
</div>
</li>
<li>
<div class="checkbox">
<input type="checkbox" name="ceramics" value=".ceramics" id="ceramics" />
<label for="ceramics">ceramics</label>
</div>
</li>
</ul>
</div>
</fieldset>
<fieldset class="filter-row">
<div class="row-section">
<div class="row-heading">studio art</div>
<ul>
<li>
<div class="checkbox">
<input type="button" value=".option1" name="option1" id="option1" />
</div>
</li>
<li>
<div class="checkbox">
<input type="button" value=".option2" name="option2" id="option2" />
</div>
</li>
</ul>
</div>
<div class="row-section ">
<div class="row-heading">ceramics</div>
<ul>
<li>
<div class="checkbox">
<input type="button" value=".option1" name="option1" id="option1" />
</div>
</li>
<li>
<div class="checkbox">
<input type="button" value=".option2" name="option2" id="option2" />
</div>
</li>
</ul>
</div>
</fieldset>
JS:
$(document).ready(function () {
$("input:checked").click(function () {
var clickedText = $(this).text();
$(".row-section").filter(function () {
return $(this).find("div").text() === clickedText;
}).toggle();
});
});
Forked fiddle: http://jsfiddle.net/ru8YL/1/
I've created a new selector to get the label text that accords with the checkbox: $(this).siblings('label').text().
Select all checkboxes instead of the buttons you previously used: $("input:checkbox").
Use the change event rather than click.
Use .is(':checked') to see if the checkbox is checked or not.
Send toggle a parameter to tell it to hide or show. This second parameter is specified in the documentation: http://api.jquery.com/toggle/
$("input:checkbox").change(function () {
var clickedText = $(this).siblings('label').text();
console.log(clickedText);
$(".row-section").filter(function () {
return $(this).find("div.row-heading").text() === clickedText;
}).toggle(this.checked);
});
$(document).ready(function () {
$("input").change(function () {
var isChecked = $(this).is(":checked");
});
});
use change event instead of click event
Here's my contribution. It just shows there's a large number of ways you can express the same thing using jQuery.
$(document).ready(function () {
$("input:checkbox").click(function () {
var clickedText = $(this).next('label').text();
$("fieldset:nth-child(2) .row-section").filter(function () {
return $('.row-heading', this).text() === clickedText;
}).toggle($(this).is(':checked'));
});
});
http://jsfiddle.net/LhuT9/1/
Compared this with Joe Frambach's answer, it differs by:
This example uses next instead of siblings
The filter function extracts the text using $('.row-heading', this).text() instead of $(this).find("div.row-heading").text()
Yet another alternative, though it's much the same with only minor differences:
// selects all input elements whose type is 'checkbox':
$('input[type="checkbox"]')
// binds an anonymous function to the 'change' event:
.on('change', function(){
// caching the this variable for later use:
var input = this,
// finding whether node.textContent or node.innerText is supported in the browser:
textProp = 'textContent' in document.body ? 'textContent' : 'innerText';
// selecting the '.row-section' elements,
$('.row-section')
// filtering that collection:
.filter(function(){
// using '$.trim()' to remove leading/trailing white-space
return $.trim($(this)
// finding the 'div.row-heading' elements within the current '.row-section':
.find('div.row-heading')
// retrieving its text:
.text())
// comparing for strict equality:
===
// finding the text of the label attached to the element
// the benefit of this approach is that it's independent
// of the DOM structure:
$.trim(input.labels[0][textProp]);
// showing if the input is checked, hiding if not:
}).toggle(input.checked);
// triggering the change event, so the anonymous function is called on page-load:
}).change();
JS Fiddle demo.
References:
JavaScript:
HTMLInputElement, to see a brief introduction to the labels property
jQuery:
$.trim().
change().
find().
filter().
on().
text().
toggle().

jQuery product filter using checkboxes and :contains() show/hide

I'm attempting to put together a simple jQuery product filter for a store that lists all products of a category on one page. This can't be achieved through AJAX because of the way the store is set up.
Simply put, all products of the category are on one page. They have varying brands product names and team names. The markup looks something like this (the form at the end is how I'm planning on doing the filter).
<div id="CategoryContent">
<ul>
<li class="product">Brand1 PRODUCT1 TeamA</li>
<li class="product">Brand1 PRODUCT2 TeamB</li>
<li class="product">Brand2 PRODUCT3 TeamB</li>
<li class="product">Brand2 PRODUCT4 TeamC</li>
<li class="product">Brand3 PRODUCT5 TeamA</li>
<li class="product">Brand3 PRODUCT6 TeamD</li>
<li class="product">Brand4 PRODUCT7 TeamD</li>
<li class="product">Brand1 PRODUCT8 TeamA</li>
<li class="product">Brand1 PRODUCT9 TeamA</li>
<li class="product">Brand1 PRODUCT10 TeamB</li>
<li class="product">Brand4 PRODUCT11 TeamD</li>
<li class="product">Brand2 PRODUCT12 TeamA</li>
</ul>
<div style="clear:both;"></div>
<div class="filter">
<form id= "brandfilter" action="">
<h2>Brands:</h2>
<input type="checkbox" name="brand" value="Brand1"/>Brand1 </br>
<input type="checkbox" name="brand" value="Brand2"/>Brand2 </br>
<input type="checkbox" name="brand" value="Brand3"/>Brand3 </br>
<input type="checkbox" name="brand" value="Brand1"/>Brand4 </br>
</form>
<form id="teamfilter" action="">
<input type="checkbox" name="team" value="TeamA"/>TeamA </br>
<input type="checkbox" name="team" value="TeamB"/>TeamB </br>
<input type="checkbox" name="team" value="TeamC"/>TeamC </br>
<input type="checkbox" name="team" value="TeamD"/>TeamD </br>
</form>
I have found this filter works as I want. In console replacing hide with show and Brand1 with Brand2, TeamA, etc works just fine.
$("#CategoryContent li").not(
$("#CategoryContent li:contains(Brand1)")
).hide();
The next step is getting a double filter which works as well:
$("#CategoryContent li").not(
$("#CategoryContent li:contains(Brand1):contains(TeamA)")
).hide();
My problem with getting this working is two fold. 1 is replacing the Brand1 / Team A with variables (hence the formids).
The second is trying to run the script when a checkbox is clicked. It should work if either one is clicked and if both are clicked (meaning that with the script above it would need to reset by showing all and then hiding).
Currently to initiate it I'm running this script but I'm running into problems so I've gone back to just 1 filter.
$("input:checkbox[name='brand']").click(function() {
var brandfilter = $(this).val();
alert (brandfilter);
$("#CategoryContent li:contains(' + brandfilter + ')").parent().hide();
});
The alert that pops up is what I want (i.e. Brand1) but the hide function afterwards doesn't work and when I alert (brandfilter) again in console again I get [object HTMLFormElement]. So I think the variable isn't storing correctly or something?
Here is the simple working basic script http://jsfiddle.net/7gYJc/
Assuming you want to show items which match any currently ticked box, you can use this:
$('input:checkbox').change(showHideProducts);
function showHideProducts()
{
var checked = $('input:checked');
var products = $('.product');
// If all the boxes are unchecked, show all the products.
if (checked.length == 0)
{
products.show();
}
else
{
products.hide();
checked.each
(
function()
{
$('.product:contains("' + $(this).val() + '")').show();
}
);
}
}​
EDIT: Since you want all the boxes to display when nothing is checked, just add an if (length = 0) check and show everything in there (see above).
I can't tell where the problem is but you can try using filter() instead of the :contains selector. With filter() you can create your own custom filtering rules as it takes a function as parameter.
var myProducts = function (brand, team) {
brand = brand || '';
team = team || '';
return $('.product').filter(function () {
var re = new RegExp(brand + '.+' + team, 'i');
return re.test($(this).text());
});
};
myProducts('Brand1', 'TeamA').hide();
myProducts('Brand2').hide();
Example: http://jsfiddle.net/elclanrs/hYZcW/
I came up with the following approach:
Start with all the checkboxes checked, because you want to show all the products. If the user unticks a checkbox, then it should hide products matching that brand/team.
Here is the code I had:
var $filters = $("input:checkbox[name='brand'],input:checkbox[name=team]").prop('checked', true); // start all checked
var $categoryContent = $('#CategoryContent li'); // cache this selector
$filters.click(function() {
// if any of the checkboxes for brand or team are checked, you want to show LIs containing their value, and you want to hide all the rest.
$categoryContent.hide();
$filters.filter(':checked').each(function(i, el) {
$categoryContent.filter(':contains(' + el.value + ')').show();
});
});​
jsFiddle link.
I had the same kind of problem this week with multiple selects. The selectors worked fine, but hide did not work. I was able to solve it using
.css('visibility', 'hidden'); // instead of .hide() and
.css('visibility', ''); // instead of .show()
I do not understand it, but it worked.

Categories