JQuery each(): index is always 0 [duplicate] - javascript

This question already has answers here:
Why doesn't index() working as expected
(5 answers)
Closed 7 years ago.
I've got a jquery .each() nested in a Datatables action function. I'm trying to get the values(td) of the checked row, so I figured I use the index that's passed to the each() function and use the index to select the row(tr). The index is always 0. My approach seems flawed, but it's a start. I need the values in the checked rows.
var data = hesTable.$('input[type="checkbox"]:checked').each(function (index, element) {
var item = $(this);
alert(index);
alert('Row index: ' + item.index())
alert('Row index: ' + item.parent().index());
});
HTML (MVC)
<tbody>
#if (Model.HesViewModels != null)
{
// Can't use the DisplayTemplate here because need to assign Data-* attributes
//
//foreach (var item in Model.HesViewModels)
for (var i = 0; i < Model.HesViewModels.Count; i++)
{
// Make sure Hes Service returns data
//
if (Model.HesViewModels[i] != null)
{
<tr>
<td>
#Html.CheckBoxFor(s => Model.HesViewModels[i].Process,
new
{
data_detailid = Model.HesViewModels[i].PartnerReferralDetailId,
data_reasoncode = Model.HesViewModels[i].ReferralDetailReasonCode,
data_statuscode = Model.HesViewModels[i].ReferralDetailStatusCode,
data_outcomecode = Model.HesViewModels[i].ReferralDetailOutcomeCode
})
</td>
<td>#Model.HesViewModels[i].PartnerReferralDetailId</td>
<td>#Html.TextBoxFor(m => Model.HesViewModels[i].ReferralDetailStatusCode)</td>
<td>#Html.TextBoxFor(m => Model.HesViewModels[i].ReferralDetailReasonCode)</td>
<td>#Html.TextBoxFor(m => Model.HesViewModels[i].ReferralDetailOutcomeCode)</td>
</tr>
}
}
}
</tbody>
RENDERED HTML
<input data-detailid="1578441" data-outcomecode="154" data-reasoncode="9" data-statuscode="8" data-val="true" data-val-required="The Process field is required." id="HesViewModels_0__Process" name="HesViewModels[0].Process" type="checkbox" value="true" /><input name="HesViewModels[0].Process" type="hidden" value="false" />

Try using selector "table tr:has(:checked)" to select only tr which have input element where checked is true . Approach iterates tr elements instead of input elements , to avoid need to select parent elements within .each()
$("table tr:has(:checked)").each(function(index, element) {
console.log($(this).index())
})
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js">
</script>
<table>
<tbody>
<tr>
<td>
<input type="checkbox" />
</td>
</tr>
<tr>
<td>
<input type="checkbox" checked />
</td>
</tr>
<tr>
<td>
<input type="checkbox" checked />
</td>
</tr>
</tbody>

Because the index() is based on the siblings. Not all the checkboxes on the page. So use the index of the row.
item.closest("tr").index()
If you want to get the tds, than you really do not need the index()
item.closest("tr").find("td")

Related

for statement keeps put first empty value

I'm trying to get only checked checkbox values
so I used for statement.
It goes well if I check from the first
but if I check from second It put empty value in array.
even I set this contidion, it still put empty value.
if (document.getElementsByName("check")[i].checked == true)
what is problem?
js
var arr_downloadName = new Array();
var chekced_download = 0;
$(".compress").click(function () {
var size = document.getElementsByName("check").length;
for (var i = 0; i < size; i++) {
if (document.getElementsByName("check")[i].checked == true) {
arr_downloadName[i] = document.getElementsByName("check")[i].value;
chekced_download++
}
}
});
html
<tr>
<td id="10Mb.dat"><input type="checkbox" name='check' value='10Mb.dat'
data-url="https://eoimages.gsfc.nasa.gov/images/imagerecords/73000/73751/world.topo.bathy.200407.3x21600x10800.jpg"/>File10MB
</td>
</tr>
<tr>
<td id="100mb.bin"><input type="checkbox" name='check' value='100mb.bin'
data-url="http://speedtest-ny.turnkeyinternet.net/100mb.bin"/>File100MB
</td>
</tr>
<tr>
<td id="500MB.test"><input type="checkbox" name='check' value='500MB.test'
data-url="http://nl.altushost.com/500MB.test"/>File500MB</td>
</tr>
<tr>
<td id="1000mb.bin"><input type="checkbox" name='check' value='1000mb.bin'
data-url="http://speedtest.tele2.net/1GB.zip"/>File1GB</td>
</tr>
<button class="btn btn-primary compress">압축하기</button>
This is happening because you're not specifying an element at index 0 (or previous indexes). Thus, if you try and add an element into an empty array at index 1 (which happens when you check the second checkbox) then you're array needs to put in an empty value in at index 0 so that it can add an element into index 1.
A solution to this would be to use .push which appends items to the end of your arr_downloadName array:
let chekced_download = 0;
$(".compress").click(function() {
let arr_downloadName = [];
var checkElem = document.getElementsByName("check");
var size = checkElem.length;
for (var i = 0; i < size; i++) {
if (checkElem[i].checked == true) {
arr_downloadName.push(checkElem[i].value);
chekced_download++
}
}
console.log(arr_downloadName);
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<tr>
<td id="10Mb.dat"><input type="checkbox" name='check' value='10Mb.dat' data-url="https://eoimages.gsfc.nasa.gov/images/imagerecords/73000/73751/world.topo.bathy.200407.3x21600x10800.jpg" />File10MB
</td>
</tr>
<tr>
<td id="100mb.bin"><input type="checkbox" name='check' value='100mb.bin' data-url="http://speedtest-ny.turnkeyinternet.net/100mb.bin" />File100MB
</td>
</tr>
<tr>
<td id="500MB.test"><input type="checkbox" name='check' value='500MB.test' data-url="http://nl.altushost.com/500MB.test" />File500MB</td>
</tr>
<tr>
<td id="1000mb.bin"><input type="checkbox" name='check' value='1000mb.bin' data-url="http://speedtest.tele2.net/1GB.zip" />File1GB</td>
</tr>
<button class="btn btn-primary compress">압축하기</button>
As a side note, while it isn't needed, I recommend that you add a variable such as checkElem which holds your collection of elements. Then, instead of having to re-query the DOM each time you can instead just reference your element list, which will improve overall performance.
Also, I noticed that you're not making use of jQuery to it's fullest. Instead, you can get all the elements with the name check which are checked using a different selector:
$("[name='check']:checked")
and then .map all the elements selected to an to their values:
$(".compress").click(function() {
let arr_downloadName = $("[name='check']:checked").toArray().map(function(e) {
return e.value;
})
console.log(arr_downloadName);
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<tr>
<td id="10Mb.dat"><input type="checkbox" name='check' value='10Mb.dat' data-url="https://eoimages.gsfc.nasa.gov/images/imagerecords/73000/73751/world.topo.bathy.200407.3x21600x10800.jpg" />File10MB
</td>
</tr>
<tr>
<td id="100mb.bin"><input type="checkbox" name='check' value='100mb.bin' data-url="http://speedtest-ny.turnkeyinternet.net/100mb.bin" />File100MB
</td>
</tr>
<tr>
<td id="500MB.test"><input type="checkbox" name='check' value='500MB.test' data-url="http://nl.altushost.com/500MB.test" />File500MB</td>
</tr>
<tr>
<td id="1000mb.bin"><input type="checkbox" name='check' value='1000mb.bin' data-url="http://speedtest.tele2.net/1GB.zip" />File1GB</td>
</tr>
<button class="btn btn-primary compress">압축하기</button>
try
if (document.getElementsByName("check")[i].checked != null)
seems your code will work. you are using incorrect variable to put the values in array. Make minor correction. i.e use 'chekced_download' instead of i as below, to push the values in array
arr_downloadName[chekced_download] = document.getElementsByName("check")[i].value;
Checked is a very sneaky HTML attribute
where if checked looks like
<input checked />
<input checked="checked" />
and produces true result
where if NOT checked looks like
<input />
which produces NULL - because there is no checked attribute. So check for the checked check. McCheck Check!
.checked != null
Hence you getting empty value.

How to get size of a list(returning from Controller) in onChange() JQuery

I am returning a list from Controller class.
httpReq.setAttribute("playersList", playersList);
I want size of this list in onchange() method.
$('#teams').on('change', function(){
// code to get the size/count
if(count < 11){
.........
}else{
.........
}
}
here I am displaying that list:
<tr id = "players" style="display:none;">
<td class="tabHead">PLAYER NAMES</td>
<td>
<div class="" id="playerNames" name="playerNames" >
<c:forEach items="${playersList}" var="playersListItem">
${playersListItem.name}<br>
</c:forEach>
</div>
</td>
</tr>
I tried -
var count = playersList.length;
var count = $("#players").length;
but that didn't work.
It seems that each item in your list is some unformatted text followed by a <br> element:
${playersListItem.name}<br>
...all within a particular <div> within a particular <tr>. So to count them just count the <br> elements that are inside the <tr> or <div> using one of the following:
var count = $("#players").find("br").length;
var count = $("#players br").length;
var count = $("#playerNames").find("br").length;
var count = $("#playerNames br").length;
(Note that the second thing you tried, $("#players").length, should always return either 0 or 1, because selecting by element id should always find either 0 or 1 element.)
How about using a tribute selector
$("[id='players'] br").length;
or
$('#playerNames > *').length;
if you change your table structure a little, you can easily find the count. Like here I add class 'playerName' and count it inside the players TR tag
<tr id="players" style="display:none;">
<td class="tabHead">PLAYER NAMES</td>
<td>
<div id="playerNames" name="playerNames" >
<c:forEach items="${playersList}" var="playersListItem">
<div class="playerName">${playersListItem.name}</div>
</c:forEach>
</div>
</td>
</tr>
$('#players .playerName').length

Having incremental order ids for tr in tables after deleting rows too

I have table with following format
<table>
<tr>
<td>
<input type="hidden" name="help[0].id" />
</td>
<td> <span class="tr-close">X</span>
</tr>
<tr>
<td>
<input type="hidden" name="help[1].id" />
</td>
<td> <span class="tr-close">X</span>
</tr>
<tr>
<td>
<input type="hidden" name="help[2].id" />
</td>
<td> <span class="tr-close">X</span>
</tr>
</table>
Now I have Add button which will add row to the end of table. On clicking X icon I am deleting the entire row. Suppose if I delete row with name help[1].id, Now if I again add the new row, it should have help[1].id..So, on each addition/deletion the name should have differenct incrementatal non repeating numbers like id's 0,1,2,3....
After deleting the row, you can loop through each hidden field to update the name attribute. You can use .each() for looping. use .attr() method to update the name attribute
$(".tr-close").click(function(){
$(this).parents("tr").remove();
$("input[type=hidden]").each(function(i){
$(this).attr("name","help["+i+"]");
});
});
Demo
I think you can use a "Heap" data structure to maintain the smallest number of your deleted index. Pick from the Heap when you add a new row.
RemoveRow ->
Insert index to Heap
AddRow ->
if Heap is empty:
Index = Rows.length
else:
Index = top of heap
remove top of heap
You can do it like this
//In order to have the dynamically created rows to have the
//delete function also you can use you on() like this
$('table').on('click', '.tr-close', function(){
$(this).parents('tr').remove();
});
// the #add refers to the add button you use
$('#add').click(function(){
var counter = 0;
$('table')
.append('<tr><td><input type="hidden" name=""/></td><td> <span class="tr-close">X</span></td></tr>');
$('tr').each(function(){
$(this).find('input').prop('name', 'help[' + counter + '].id');
counter++;
});
});

clear values of all (including nested) form fields within a certain tbody using jquery

I have the following structure inside of a table, which has rows and also other tables nested within #tbody_name.
<tbody id="tbody_name">
<tr>
<td>
<select>
<option value="" selected="selected">---------</option>
<option value="3">Something</option>
<option value="2">Something else</option>
</select>
</td>
</tr>
<tr>
<td>
<input type="radio" value="0" /> Normal<br>
<input type="radio" value="1" /> Abnormal<br>
</td>
<tr>
<td>
<table>
<tr>
<td>
<input type="text />
</td>
<td>
<input type="checkbox" />
</td>
</tr>
</table>
</td>
</tr>
</tbody>
What I am trying to do is write a function that is able to clear the values of all of the form fields within #tbody_name.
I have attempted using .children and .each, but I just cannot seem to boil it all the way down to the form fields within #tbody_name.
Any help would be greatly appreciated.
Thank you,
JD
$('#tbody_name').find('input').each(function(){
if ((this.type === 'radio') || (this.type === 'checkbox'))
{
this.checked = false;
}
else
{
this.value = '';
}
});
$('#tbody_name').find('select').each(function() {
this.selectedIndex = 0;
});
EDIT: I just fixed the code to take care of some rather stupid bugs. It should work fine now.
You can either transverse your table using the find method for finding children, and closest for finding parents, and next for finding the next element:
$('#tbody_name').find('option:first-child').attr('selected', 'selected').closest('tr').next().next().find('input:text').val('');
Or you can give your form fields ID's and find them by id:
$('#defaultOption').attr('selected', 'selected');
$('#textInput').val('');
Put these inside a function and call it from a button.
There are a number of ways this could be done. Which one you decide to use depends on your coding style. The following solution uses the prop() method of jQuery.
$('#tbody_name input[type=radio], #tbody_name [type=checkbox]').each(function() {
$(this).prop('checked', false);
});
$('#tbody_name input[type=text]').each(function() {
$(this).prop('value', '');
});
$('#tbody_name select').each(function() {
$(this).prop('selectedIndex', 0);
});
Or you could set the properties on the objects expicitly. This method may be a bit more direct and readable:
$('#tbody_name input[type=radio], #tbody_name [type=checkbox]').each(function() {
this.checked = false;
});
$('#tbody_name input[type=text]').each(function() {
this.value = '';
});
$('#tbody_name select').each(function() {
this.selectedIndex = 0;
});

How to use javascript to select a row of radio buttons when a checkbox is selected

What I need to do is check to see if the checkbox is checked, and if so, select all the radio buttons located within the same element?
I've set up the elements with id's, b/c that is the "physical" grouping of the elements that will be affecting each other.
I'm trying to do something like this onchange('some_row_id'):
function select_row(row_id) {
// Get 1st td element (where checkbox is located) and assign its
// checked value to variable "checked"
var checked = document.getElementById(row_id).td[0].input.checked;
if(checked) {
var children = document.getElementById(row_id).childNodes;
for(var i = 0; i< children.length; i++) {
if(children.td.type == radio) {
children.td.radio = checked;
}
}
}
}
I know that javascript is almost 200% wrong, but I can't figure out how to properly select only td children (or prefereably, only input grandchildren) of a tr element and check them.
Here's the basic form structure in actual working html:
<form name="form2" action="testfile4.php" method="get">
<table border="1"><thead>
<tr><th>Select entire row</th><th>item_code</th><th>description</th><th>page</th>
</tr>
</thead>
<tbody>
<tr id="534">
<td ><input type="checkbox" onchange="select_row(534);"></td> <td>15038 <input type="radio" name="15819-038|item_code" value="534" /></td>
<td>For 1st item, alternate 1
<input type="radio" name="15819-038|description" value="534" /></td>
<td>5
<input type="radio" name="15819-038|page" value="534" /></td>
</tr>
<tr id="535">
<td ><input type="checkbox" onchange="select_row(535);"></td> <td>15038 <input type="radio" name="15819-038|item_code" value="535" /></td>
<td>For 1st item, alternate 2 <input type="radio" name="15819-038|description" value="535" /></td>
<td>5
<input type="radio" name="15819-038|page" value="535" /></td>
</tr>
</tbody>
</table>
</form>
EDIT:
I'm willing to accept jquery solutions. Thank you.
EDIT 2:
Thanks to nnnnnn. I used your JS and added this to have the uncheck behavior I wanted. If you want you can update your answer with it and I'll remove it from here:
else if (!row.cells[0].childNodes[0].checked) {
inputs = row.getElementsByTagName("input");
for(var i=0; i<inputs.length; i++) {
if(inputs[i].type === "radio") {
inputs[i].checked = false;
}
}
}
Well here's one way to do it:
function select_row(row_id) {
// get a reference to the row based on the id passed in
var row = document.getElementById(row_id),
inputs;
// .cells[0] gives the row's first td element,
// then .childNodes[0] gives that td's first child element which
// will be the checkbox
if (row.cells[0].childNodes[0].checked) {
// getElementsByTagName gives all descendent elements with the matching
// tag, not just direct children, so get all the inputs for the current
// row and loop through them looking for the radios
inputs = row.getElementsByTagName("input");
for (var i=0; i<inputs.length; i++) {
if (inputs[i].type==="radio")
inputs[i].checked = true;
}
}
}
And change your checkbox to use onclick=... instead of onchange=....
Note that using checkboxes to select a row doesn't really make sense because after selecting one, if you click another row's checkbox the first row's checkbox is still checked. You might be better off with a button or <a> element for this purpose.
Note also that instead of passing the row ID to the function and then getting a reference to that row using the ID, like this:
<input type="checkbox" onclick="select_row(534)">
function select_row(row_id) {
var row = document.getElementById(row_id);
// etc
You can directly pass a reference to the checkbox that was clicked and use that instead:
<input type="checkbox" onclick="select_row(this);">
function select_row(cb) {
var row = cb.parentNode.parentNode;
if (cb.checked) {
// etc
However in my full solution above I stuck with passing the ID like you did in case you are calling the function from somewhere other than just the click event.
There were several things wrong with your code as posted:
You can't get children of a particular element just by refering to their type, e.g., getElementById(row_id).td[0].input doesn't work. To get the first td you can use a row's cells collection and say .cells[0] (like in my code).
Your for loop doesn't use the i variable within the loop to select the individual items. You should've said children[i] within the loop.
Your if statement: if(children.td.type = radio) { is doing an assignment (single = sign) instead of a comparison (double == or triple === equals sign), and should be comparing to the string "radio" (with quotes).
If you want jquery solution then here it is:
$(document).ready(function() {
$("#your_checkbpx_id").click(function() {
if($(this).is(":checked")) {
//select all your radio
var id = $(this).attr("id");
$("tr[id='"+id+"'] > input[type='radio']").each(function() {
//make it checked
$(this).attr("checked", "checked");
});
}
});
});
Hope it helps
Here is a way:
<script type="text/javascript">
/*
Loops through all input elements finding all checkboxs.
Testing if the current checkbox obj is equal to the selected checkbox object.
If equal: Set the radio button to the current state of the checkbox.
Not equal: Set its children radio buttons checked property false
--------------------------------------------------------------------------- */
function setCheckboxs(checkBox) {
var parent, i;
var checkBoxs = document.getElementsByTagName("input");
for (i = 0; i < checkBoxs.length; i++) {
if (checkBoxs[i].getAttribute("type") === "checkbox") {
parent = checkBoxs[i].parentNode.parentNode;
if (checkBox === checkBoxs[i]) {
toggleRadios(parent.childNodes, checkBoxs[i].checked);
} else {
checkBoxs[i].checked = false;
toggleRadios(parent.childNodes, false);
}
}
}
}
/*
Loops through all its children nodes finding a node's attribute equal to radio
----------------------------------------------------------------------------------- */
function toggleRadios(radios, isChecked) {
var i;
for (i = 0; i < radios.length; i++) {
if (radios[i].childNodes.length > 0) {
toggleRadios(radios[i].childNodes, isChecked);
}
if (radios[i].nodeName === "INPUT") {
if (radios[i].getAttribute("type") === "radio") {
radios[i].checked = isChecked;
}
}
}
}
</script>
<table border="1">
<thead>
<tr>
<th>
Select entire row
</th>
<th>
item_code
</th>
<th>
page
</th>
<th>
usd_dn
</th>
</tr>
</thead>
<tbody>
<tr id="534" class="15838">
<td>
<input type="checkbox" onclick="setCheckboxs(this);" />
</td>
<td>
15838<input type="radio" name="15838|item_code" value="534" />
</td>
<td>
284<input type="radio" name="15838|page" value="534" />
</td>
<td>
$73.00<input type="radio" name="15838|usd_dn" value="534" />
</td>
</tr>
<tr id="535" class="15838">
<td>
<input type="checkbox" onclick="setCheckboxs(this);" />
</td>
<td>
15838
<input type="radio" name="15838|item_code" value="535" />
</td>
<td>
299
<input type="radio" name="15838|page" value="535" />
</td>
<td>
$73.00<input type="radio" name="15838|usd_dn" value="535" />
</td>
</tr>
<tr id="565">
<td>
<input type="checkbox" onclick="setCheckboxs(this);" />
</td>
<td>
1611
<input type="radio" name="1611|item_code" value="565" />
</td>
<td>
66<input type="radio" name="1611|page" value="565" />
</td>
<td>
$3,350.00
<input type="radio" name="1611|usd_dn" value="565" />
</td>
</tr>
<tr id="566">
<td>
<input type="checkbox" onclick="setCheckboxs(this);" />
</td>
<td>
1611
<input type="radio" name="1611|item_code" value="566" />
</td>
<td>
66<input type="radio" name="1611|page" value="566" />
</td>
<td>
$3,225.00
<input type="radio" name="1611|usd_dn" value="566" />
</td>
</tr>
</tbody>
</table>

Categories