HTML table display JQuery checkbox off-by-one and not preserving data - javascript

I have some JavaScript code that should display a matrix of checkboxes. I want to list the column titles across the top, and then put rows where there is a column of checkboxes under each header, plus a left-hand column with row names under a blank header box. I'm going for the look on this page:
http://codepen.io/marclundgren/pen/hgelI
I wrote a Fiddle that almost works:
https://jsfiddle.net/bv01xvdf/
The first problem is that my table displays the checkboxes on a separate line from the cell with the row name. I checked my HTML, and it seems correct, but I'm wondering if I'm missing a <tr> or a </tr> somewhere. I add the row name cell like this (see the Fiddle for complete code):
var chugNames = ["Ropes", "Cooking", "Outdoor Cooking"];
for (x = 0; x < chugNames.length; x++) {
// Add a row for each name.
target.append("<tr><td>" + chugNames[x] + "</td>");
for (y = 0; y < chugNames.length; y++) {
target.append("<td><input type=\"checkbox\" />");
checkbox = $('</input>', {
'type': 'checkbox',
'data-x': chugNames[x],
'data-y': chugNames[y],
});
target.append(checkbox);
target.append("</td>");
}
target.append("</tr>");
}
The other problem is that data-x and data-y return "undefined" when I access them later in my "on" method:
target.on('change', 'input:checkbox', function() {
var $this = $(this),
x = $this.data('x'),
y = $this.data('y'),
checked = $this.prop('checked');
alert('checkbox changed chug intersection (' + x + ', ' + y + '): ' + checked);
});
When I check a box, I get "checkbox changed chug intersection (undefined, undefined): true". It should print something like (Ropes, Cooking), depending on which box was checked.
Any help would be greatly appreciated.

When you append with jQuery the tag is automatically closed.
check the jsfiddle
Try this:
$(function() {
var target = $('#checkboxes');
var chugNames = ["Ropes", "Cooking", "Outdoor Cooking"];
var i, x, y, checkbox, html;
html = "<table class=\"responsive-table-input-matrix\"><thead><tr><th></th>";
// Table column headers
for (i = 0; i < chugNames.length; i++) {
html += "<th>" + chugNames[i] + "</th>";
}
html += "</tr></thead><tbody>";
for (x = 0; x < chugNames.length; x++) {
// Add a row for each chug.
html += "<tr><td>" + chugNames[x] + "</td>";
for (y = 0; y < chugNames.length; y++) {
html += "<td>";
checkbox = '<input type=checkbox ';
checkbox += ' data-x=' + chugNames[x]
checkbox += ' data-y=' + chugNames[y]
checkbox += '/>'
html += checkbox;
html += "</td>";
}
html += "</tr>";
}
html += "</tbody></table>";
target.append(html).width(function() {
return $(this).find("input:checkbox").outerWidth() * chugNames.length
});
target.on('change', 'input:checkbox', function() {
var $this = $(this),
x = $this.data('x'),
y = $this.data('y'),
checked = $this.prop('checked');
alert('checkbox changed chug intersection (' + x + ', ' + y + '): ' + checked);
});
});

I fixed your jsfiddle here.
For the record, you had a few problems, an extra <th></th> in the opening string, an extra output of ChugName[x] and you didn't use the attr() jQuery function to get the data attributes properly.

Related

jquery function calculation no working right on second call

I have a subtotals function that does some calculations on an html table (sums up columns that have assigned classes). I have modified it so that I can pass arguments to it, and on the first function call it works as desired. When I call it again it creates the cell and assigns the class, but the math or apparently something else is wrong because it is returning NaN. Here is my JSFiddle and below is my function. Any help would be appreciated!
//v is parent,z is child element,w is which child column....ex: 0 or 1, y is first run or not, z is class name you want assigned
function subtotals(v, w, x, y, z) {
$(v).each(function (index, element) {
var subTotalAmt = 0;
var numRows = parseInt($(this).attr("rowspan"));
var firstRow = $(this).parent();
var lastRow = firstRow.nextAll('tr').slice(numRows - 2, numRows - 1);
var currentRow = firstRow;
for (i = 0; i < numRows; i++) {
subTotalAmt += parseInt($(currentRow.children(w)[x]).text());
currentRow = currentRow.next("tr");
}
if(y == 'yes'){
lastRow.after('<tr><td class="sub0">Sub Total</td><td class="' + z + '">' + subTotalAmt + '</td></tr>');
$(this).attr('rowspan', numRows + 1);
}
else {
lastRow.append('<td class="' + z + '">' + subTotalAmt + '</td>');
}
});
}
$(function doSubtotals() {
subtotals('.parent','.child','1','yes','sub1');
subtotals('.parent','.child','2','no','sub2');
});
Some of your <td> are not containing a valid number , which case the calculation to fail and put NaN (stands for 'Not a Number') instead of a valid number (NaN + number = NaN).
To fix this i added this condition to your code :
for (i = 0; i < numRows; i++) {
var tdValue=0;
if(!isNaN(parseInt($(currentRow.children(w)[x]).text())))
{
tdValue=parseInt($(currentRow.children(w)[x]).text());
}
subTotalAmt += tdValue;
currentRow = currentRow.next("tr");
}

How to sort a multidimensional array using items in Javascript [duplicate]

This question already has answers here:
Sort array of objects by string property value
(57 answers)
Closed 7 years ago.
Basically all I want is to sort this array based on each item that is shown below, with the exception of the "Action' and 'Thumb Image' ones. So the way I have it set up is that the header for each of rows is a link, and when that link is clicked the list will be sorted based on what was clicked. So for example, if Title is clicked, then I want to have a "titleSort()" function that will sort based on title. I have no idea how to accomplish this, so any help is much appreciated. I was hoping that VideoList.sort(Title) would work, for example.
Thanks,
JS
for(var i = 0; i<VideoList.length; i++) {
content += "<tr>";
content += "<td width='20%'><a href='https://www.youtube.com/watch?v=" + VideoList[i].VideoID + "'onclick='playVideo("+i+")'>" + "<img src ='https://i.ytimg.com/vi/" + VideoList[i].VideoID + "/hqdefault.jpg' width=175 height=130></a></td>";
content += "<td>" + VideoList[i].Title + "</td>";
content += "<td>" + VideoList[i].VideoID + "</td>";
content += "<td>" + VideoList[i].DateUploaded + "</td>";
content += "<td>" + VideoList[i].Category+ "</td>";
content += "<td>" + VideoList[i].Time+ "</td>";
content += "<td width='20%'>" + VideoList[i].Action + "</td>";
content += "</tr>";
You can use sort to sort VideoList according to title this code may work for you
VideoList.sort(function(a,b){
return a.Title > b.Title;
});
I agree with #manishrw about lodash. AND any number of libraries would make this easier - like jQuery and Angular. There are a ton of table-specific libraries out there that have sort function built in. However, I built it to show how you could do it, including re-building the table once it's sorted. To do that I had to create the array with mock data. Here's a jsfiddle:
https://jsfiddle.net/mckinleymedia/c02nqdbz/
And here's the code:
<div id="target"></div>
<script>
var VideoList = [],
content,
fields = ["Title", "VideoID", "DateUploaded", "Category", "Time", "Action"],
num = 10,
sortField = "Title",
sortDirection = 1,
compare = function(a, b) {
if (a[sortField] < b[sortField]) return -1 * sortDirection;
if (a[sortField] > b[sortField]) return 1 * sortDirection;
return 0;
},
sortArray = function(field) {
if( sortField === field ) sortDirection = -1 * sortDirection;
sortField = field;
VideoList.sort(compare);
buildTable();
},
creatVideos = function() {
for (var x = 0; x < num; x++) {
var video = {},
z = Math.floor(Math.random() * 200);
for (var i in fields) {
if(fields[i]==='VideoID') {
video[fields[i]] = z;
} else {
video[fields[i]] = fields[i] + "-" + z;
}
}
VideoList.push(video);
}
},
buildTable = function() {
content = "<table>";
content += "<tr>";
content += "<th>image</th>";
for (var x in fields) {
content += "<th class='field field-" + fields[x] + "' onclick='sortArray(\"" + fields[x] + "\")'>" + fields[x] + "</th>";
}
content += "</tr>";
for (var i in VideoList) {
content += "<tr>";
content += "<td width='20%'><a href='https://www.youtube.com/watch?v=" + VideoList[i].VideoID + "'onclick='playVideo(" + i + ")'>" + "<img src ='https://i.ytimg.com/vi/" + VideoList[i].VideoID + "/hqdefault.jpg' width=175 height=130></a></td>";
for (var x in fields) {
content += "<td>" + VideoList[i][fields[x]] + "</td>";
}
content += "</tr>";
}
content += "</table>";
document.getElementById('target').innerHTML = content;
};
creatVideos();
buildTable();
</script>
Here's a generic function for you
function sortBy(list, field) {
return list.sort(function(a,b) {
return a[field] < b[field];
});
}
sortBy(VideoList, 'Title');
Warning: sortBy will mutate the list input
You could also make it take a comparator so you control the 'direction' of the sort
// you you need to return -1, 0, or 1 for the sort to work reliably
// thanks, #torazaburo
function compareAsc(a,b) {
if (a < b) return -1;
else if (a > b) return 1;
else return 0;
}
function compareDesc(a,b) {
return compareAsc(a,b) * -1;
}
function sortBy(list, field, comparator) {
return list.sort(function(a,b) {
if (comparator instanceof Function)
return comparator(a[field], b[field]);
else
return compareAsc(a[field], b[field]);
});
}
// default sort ascending
sortBy(VideoList, 'Title');
// sort descending
sortBy(VideoList, 'Title', compareDesc);
Use Lodash library. It's easy to use and efficient in run-time. It has a function sortBy, which can be used to sort a collection based on they key you provide.
P.S. Lodash is my goto Library for any operation to be performed on any collection.

JQuery mobile list prepend

trying to prepend my list in jquery mobile but I just can't get the divider to be on top of the most recent item added to the listview.
I've tried prepending the item that's being added but it then switches the divider to the bottom.
function loadScanItems(tx, rs) {
var rowOutput = "";
var $scanItems = $('#scanItems');
$scanItems.empty();
var bubbleCount = 0;
for (var i = 0; i < rs.rows.length; i++) {
bubbleCount = bubbleCount + 1;
//rowOutput += renderScan(rs.rows.item(i));
var row = rs.rows.item(i)
var now = row.added_on;
var date = get_date(now);
rowOutput += '<li data-icon="false"><div class="ui-grid-b"><div class="ui-block-a" style="width:50%"><h3>Su # ' + row.sunum + '</h3><p> Bin # ' + row.binnum + '</p></div><p class="ui-li-aside"><strong>' + date + '</strong></p><div class="ui-block-b" style="width:20%"></div><div class="ui-block-c" style="width:25%"><br><p>User: ' + row.userid + '</p></div></div></li>';
// rowOutput += '<li>' + row.sunum + row.binnum+ "<a href='javascript:void(0);' onclick='webdb.deleteScan(" + row.ID + ");'>Delete</a></li>";
}
$scanItems.append('<li data-role="list-divider">Scanned Items <span class="ui-li-count">' + bubbleCount + '</span></li>').listview('refresh');
$scanItems.append(rowOutput).listview('refresh');
}
The code is above with it correctly formatted with the divider on top but the list items being appended to the bottom instead of prepended to the top.
Thanks!
The problem is that your are building a string with all the scan items. That string already has an order so whether you prepend or append makes no difference. Try this simple change.
Change:
rowOutput += '<li data-icon="false">...</li>';
to:
rowOutput = '<li data-icon="false">...</li>' + rowOutput;
This will put your rowOutput string in the correct order before appending to the listview.
Here is a working DEMO

JavaScript & HTML - Modifying dynamically created subclasses within a dynamically created class

Problem:
I have a dynamically created HTML table, that is used for filling out time sheets. It is created programmatically - there is no formal control. The design is a mix of CSS with text boxes being created through JavaScript. Now each 'row' of this table is in a class called 'divRow', and is separated from the others by having 'r' and the number of the row assigned to it as the class (i.e 'divRow r1', 'divRow r2', etc.).
Within each of these 'divRow's, I have cells in a class called 'divCell cc'. These do not have any identifiers in the class name. At the very last cell, I have a 'Total' column, which ideally calculates the total of the row and then adds it into a dynamically created text box.
What I have at the moment:
// Function to create textboxes on each of the table cells.
$(document).on("click", ".cc", function(){
var c = this;
if(($(c).children().length) === 0) {
var cellval = "";
if ($(c).text()) {
cellval = $(this).text();
if(cellval.length === 0) {
cellval = $(this).find('.tbltxt').val();
}
}
var twidth = $(c).width() + 21;
var tid= 't' + c.id;
if(tid.indexOf('x17') >= 0){
var thtml = "<input id='t" + c.id + "' type='text' Class='tbltxt' style='width: " + twidth + "px;' readonly />";
eval(spproc(spcol(t[getx(c.id)],thtml,tid,twidth)));
//var getRow = $(this).parent().attr('class'); - this gets the 'divRow r#' that it is currently on.
var arr = document.getElementsByClassName('cc');
var tot = 0;
for(var i = 0; i<arr.length; i++){
if(parseInt(arr[i].innerHTML) > 0){
tot += parseInt(arr[i].innerHTML);}
}
$('#t' + c.id).focus();
$(this).children().val(tot);
}else{
var thtml = "<input id='t" + c.id + "' type='text' Class='tbltxt' style='width: " + twidth + "px;' />";
eval(spproc(spcol(t[getx(c.id)],thtml,tid,twidth)));
$('#t' + c.id).focus();
$('#t' + c.id).val(cellval);
}}
});
As you can see, when the user clicks on the 'divCell cc', it creates a text box if one is not present. If the user clicks on the 17th column ('x17'), then it runs the for loop, and assigns the value of the total to the text box.
What I need to happen:
So what happens now is that the last cell sums the total of each cell that has a value. However, they are not row-dependent. I need it to calculate based on the row that it is currently 'on'. So if I'm calculating the 2nd row, I don't want the sum of the first, second and third being entered into the total, I just want the 2nd rows' values summed.
What I've tried:
I've tried looping through and using the 'divRow r#' number to try and get the items in the array that end in that number. (cells are given an id of 'x#y#' and the text boxes assigned to those cells are given an id of 'tx#y#').
I've tried getting elements by the cell class name, and then getting their parent class and sorting by that; didn't get far though, keep running into simple errors.
Let me know if you need more explanation.
Cheers,
Dee.
For anyone else that ever runs into this issue. I got it. I put the elements by the row class into an array, and then using that array, I got the childNodes from the row class. The reason the variable 'i' starts at 2 and not 0 is because I have 2 fields that are not counted in the TimeSheet table (Jobcode and description). It's working great now.
Cheers.
$(document).on("click", ".cc", function(){
var c = this;
if(($(c).children().length) === 0) {
var cellval = "";
if ($(c).text()) {
cellval = $(this).text();
if(cellval.length === 0) {
cellval = $(this).find('.tbltxt').val();
}
}
var twidth = $(c).width() + 21;
var tid= 't' + c.id;
if(tid.indexOf('x17') >= 0){
var thtml = "<input id='t" + c.id + "' type='text' Class='tbltxt' style='width: " + twidth + "px;' readonly />";
eval(spproc(spcol(t[getx(c.id)],thtml,tid,twidth)));
// Get current row that has focus
var getRow = $(this).parent().attr('class');
// Get the row number for passing through to the next statement
var rowPos = getRow.split('r', 5)[1];
// Get all the elements of the row class and assign them to the rowClass array
var rowClass = document.getElementsByClassName('r' + rowPos)
// Given the rowClass, get the children of the row class and assign them to the new array.
var arr = rowClass.item(0).childNodes
// Initialize the 'total' variable, and give it a value of 0
var tot = 0;
// Begin for loop, give 'i' the value of 2 so it starts from the 3rd index (avoid the Req Code and Description part of the table).
for(var i = 2; i<arr.length; i++){
if(parseInt(arr[i].innerHTML) > 0){
tot += parseInt(arr[i].innerHTML);}
}
// Assign focus to the 'Total' cell
$('#t' + c.id).focus();
// Assign the 'total' variable to the textbox that is dynamically created on the click.
$(this).children().val(tot);
}else{
var thtml = "<input id='t" + c.id + "' type='text' Class='tbltxt' style='width: " + twidth + "px;' />";
eval(spproc(spcol(t[getx(c.id)],thtml,tid,twidth)));
$('#t' + c.id).focus();
$('#t' + c.id).val(cellval);
}}
});

Javascript not writing to HTML definition list

Can anyone help me with why this JavaScript is not writing to the definition list in the body? When I debug, the object is there and the lines are all executed. Also, if I use document.write the information will overwrite the page. I'm just having trouble with adding this HTML to the predefined definition list. There are no errors in the console. Any help is appreciated.
Javascript in head
function writeGph(obj, chartNum) {
var data = obj;
for (i = 0; i < obj.tab1.length; i++) { //Loop to create each column of the graph
document.getElementById(chartNum).innerhtml += '<dt>' + data.tab1[i].name + '</dt>'
document.getElementById(chartNum).innerhtml += '<dd class="p100"><span><b>' + data.tab1[i].top + '</b></span></dd>'
document.getElementById(chartNum).innerhtml += '<dd class="sub p' + data.tab1[i].bot + '"><span><b>' + data.tab1[i].bot + '</b></span></dd>';
console.log(data.tab1[i].top);
}
}
function writeAxis(obj, axisNum) {
for (i = 0; i < obj.tab1.length; i++) { //Loop to create each x-axis label
document.getElementById(axisNum).innerhtml += '<li>' + obj.tab1[i].name + '</li>';
}
}
function writeTable(obj, tableNum) {
document.getElementById(tableNum).innerhtml += '<tr><th>Business</th><th>Number</th><th>Percent</th></tr>';
for (i = 0; i < obj.tab1.length; i++) { //Loop to fill in table information
obj.tab1[i].botl = Math.round(10000 * (obj.tab1[i].num / obj.tab1[i].all)) / 100;
document.getElementById(tableNum).innerhtml += '<tr><td>' + obj.tab1[i].name + '</td><td>' + obj.tab1[i].num + '</td><td>' + obj.tab1[i].botl + '%</td></tr>';
}
}
HTML in body
<dl class="chart" id="chart1"></dl>
<ul class="xAxis" id="xAxis1"></ul>
<table id="table1"></table>
It's not .innerhtml, it's .innerHTML

Categories