Skip some elements in an array through jquery - javascript

i'm designing a bus seat layout using jquery. And i done it correctly too.
I'm using an array with seat numbers and i'm getting the seat layout what i want. This is my coding (here '.bus-table' is a table class) :
var row = Array(),i=0, j=0;
row = [
['1','5','9','13','17','21','25','29','33','37','41','45','49'],
['2','6','10','14','18','22','26','30','34','38','42','46','50'],
['','','','','','','','','','','','','51'],
['3','7','11','15','19','23','27','31','35','39','43','47','52'],
['4','8','12','16','20','24','28','32','36','40','44','48','53']
];
$.each(row, function(index, value) {
$('.bus-table').append('<tr>');
while(j<index+1) {
for(i=0; i<value.length; i++) {
$('.bus-table tr:nth-child('+ (index+1) +')').append(
'<td seatno="'+ row[j][i] +'">' + row[j][i] + '<input type="checkbox"/></td>' );
}
j++;
}
});
This is my results : this is my seat layout output
Now the problem is In 3rd row you can see some checkboxes only. Because in those areas there are no seats in the bus. So, i want to remove those checkboxes, which means in the array (3rd row) i left some blanks, according to those blanks i don't want the checkboxes too. I don't know how to do that. Please help me to solve this case.
(I apologies for my English)

Add an if statement inside the for loop:
for(i=0; i<value.length; i++) {
if (row[j][i] !== '') {
$('.bus-table tr:nth-child('+ (index+1) +')').append(
'<td seatno="'+ row[j][i] +'">' + row[j][i] + '<input type="checkbox"/></td>' );
}
else {
$('.bus-table tr:nth-child('+ (index+1) +')').append('<td></td>');
}
}
Note the else statement, otherwise it will break your table layout

You can skip those places using continue like shown below:
for(i=0; i<value.length; i++) {
if (row[j][i] == ""){
'<td seatno="'+ row[j][i] +'">' + row[j][i] + '</td>';
continue;//for blank don't make checkbox elements
}
$('.bus-table tr:nth-child('+ (index+1) +')').append(
'<td seatno="'+ row[j][i] +'">' + row[j][i] + '<input type="checkbox"/></td>' );
}

Related

Using variable for an identifier name (using jquery selectors)

Believe me, I've been looking for examples online for hours. None of them seem to help.
I'm working on making a table. There are some columns with dropdown menu and I've assigned ID to each menu. Inside a loop, I'm trying to assign selected value for each dropdown menu.
var row$ = $('<tr/>');
function updateDataBodyGenerator(myList) {
for (var i = 0 ; i < myList.length ; i++) {
var row$ = $('<tr/>');
var colIndex = 0;
for (var key in myList[i]) {
var cellValue = myList[i][columns[colIndex]];
if (cellValue == null) { cellValue = ""; }
var severityDropDownMenu = "severityDropDownMenu" + i;
colIndex++;
switch (key) {
case "Test Case":
...
break;
case "Test Result":
...
break;
case "Severity":
var severitySting = '<td><select id="' + severityDropDownMenu + '" class="dropDownMenu">' +
'<option value="Red">Red</option>' +
'<option value="Green">Green</option>'+
'<option value="Yellow">Yellow</option>';
row$.append($(severitySting));
//failed
//$("#severityDropDownMenu" + i).val(cellValue);
//failed
//var selectorString = "#" + severityDropDownMenu.toString();
//$(selectorString).val("Green");
//failed
//$("#" + severityDropDownMenu).val(cellValue);
//failed
//var selectorString = '#' + severityDropDownMenu;
//$(selectorString).val(cellValue);
//works
//$('#severityDropDownMenu0').val(cellValue);
...
As you can see in the comments, I've tried several approaches and only 1 worked which was $('#severityDropDownMenu0').val(cellValue); but that will only change 1 dropdown menu.
I appreciate your time and assistance.
Currently you're trying to use the # selector to target the dropdown by ID.
The issue here (as mentioned in the comments) is that this selector will search the DOM for the element, however because you've never added this element to the DOM, it doesn't exist on the page; the selector will return nothing.
What you can do instead is actually turn your severitySting into a jQuery element to set its value. Whenever you do append it, the value will be properly set. Like so:
var $severity = $(severitySting); //This is the <td>
var $dropdown = $severity.find("select") //This is the <select>
$dropdown.val(cellValue); //Set dropdown value
Demo:
var severityDropDownMenu = "mytest";
var cellValue = "Yellow";
var severitySting = '<td><select id="' + severityDropDownMenu + '" class="dropDownMenu">' +
'<option value="Red">Red</option>' +
'<option value="Green">Green</option>' +
'<option value="Yellow">Yellow</option>';
var $severity = $(severitySting);
var $dropdown = $severity.find("select");
$dropdown.val(cellValue);
$("tr").append($severity);
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<table>
<tr></tr>
</table>

Create Rows in a Table using Javascript - adds too many cells...>

I'm trying to create a table on the fly... it almost works but I have an issue with the number of cell it adds.
My table has 8 columns (monday to Friday plus a total column at the end).
Here is the fiddle for it: http://jsfiddle.net/kaf9qmh0/
As you can see, on row 1, it adds the 8 columns 3 times, then on row 2 it adds the columns twice and only on the last row does it only add 8 columns.
I suspect it is because Im using .append to add the rows as follows (line 105 in the fiddle) but my Javascript being very limited, Im not entirely sure how to make it stop add the columns to row 1 and 2.
$("#timesheetTable > tbody").append('<tr id="' + sourceTableRowID + '" data-tt-id="' + sourceTableRowID + '" class="timesheetRow row' + rowIndex2 + '"></tr>');
How can I make it to only add the cells (td) to the next row when it loops through rowIndex2 and increments it?
Can somebody point me in the right direction please?
You had a loop within a loop - which was causing the additional cells. Here's an updated fiddle; and here's an extract of the modified code:
function createSampleRows() {
var sourceTable = document.getElementById('activityTable');
var sourceTableRows = sourceTable.rows.length;
var targetTable = document.getElementById('timesheetTable');
var rowindex;
var targetTableColCount;
for (var rowIndex = 0; rowIndex < targetTable.rows.length; rowIndex++) {
if (rowIndex == 1) {
targetTableColCount = targetTable.rows.item(rowIndex).cells.length;
continue; //do not execute further code for header row.
}
}
for (rowIndex = 0; rowIndex < (parseInt(sourceTableRows)-2); rowIndex++) {
var sourceTableRowID = document.getElementsByClassName('activityTaskRow')[rowIndex].id;
$("#timesheetTable > tbody").append('<tr id="' + sourceTableRowID + '" data-tt-id="' + sourceTableRowID + '" class="timesheetRow row' + rowIndex + '"></tr>');
};
// This loop was nested within the above loop
for (x = 0; x < targetTableColCount; x++) {
$("#timesheetTable > tbody > tr").append('<td id="' + x + '" style="width: 60px;height: 34px;" class=""></td>');
};
};

Check Checkbox in jquery

This is my current code. What I am trying to accomplish is as follows.
If json[i].enabled is true, I need to check the corresponding checkbox. If not, leave it empty.
function createTable(json) {
var element = "";
var i;
for (i = 0; i < json.length; i++) {
element = element
+ '<tr><td><input type= "checkbox"/></td><td>'
+ json[i].a_id + '</td><td>' + json[i].name + '</td><td>'+ json[i].enabled
+ '</td></tr>';
if(json[i].enabled== "TRUE"){
$('checkbox').prop('checked', true);
}
}
//Had forgotten to add this before.
element = element + '</tbody>';
$('#dataTable > tbody').remove();
$("#dataTable").append(element);
}
I tried it by including the following if condition but it fails.
if(json[i].enabled== "TRUE"){
$('checkbox').prop('checked', true);
}
So, how do I go about doing this? How do I access that particular checkbox in the loop?
Thanks!
Using string concatenation and a ternary operator:
'<tr><td><input type="checkbox"' + ( json[i].enabled== "TRUE" ? ' checked' : '' ) + '/></td><td>'
$('input[type="checkbox"]').prop('checked', true);
complete example:
var $table=jQuery('<table></table>');
for (i = 0; i < json.length; i++) {
var $tr=jQuery(
'<tr><td><input type= "checkbox"/></td><td>'
+ json[i].a_id + '</td><td>' + json[i].name + '</td><td>'+ json[i].enabled
+ '</td></tr>');
if(json[i].enabled== "TRUE"){
$tr.find('input["checkbox"]').prop('checked', true);
}
$table.append($tr);
}
$jQuery('body').append($table);
The checbox element has not been created yet in the DOM. So you have to put the html first. Then use
input[type="checkbox"]
to match an actual checkbox element. And at last, you might want to get the last checkbox only (in the last row of your table) with this selector :
mytable tbody tr:last input[type="checkbox"]
So you have the following code :
function createTable(json) {
var i;
for (i = 0; i < json.length; i++) {
// construct the html
element = '<tr><td><input type= "checkbox"/></td><td>'
+ json[i].a_id + '</td><td>' + json[i].name + '</td><td>'+ json[i].enabled
+ '</td></tr>';
// put the html in the page
$('#mytable tbody').append(element);
if(json[i].enabled== "TRUE"){
// get the last inserted line in the table and check the checkbox
$('#mytable tbody tr:last input[type="checkbox"]').prop('checked', true);
}
}
}
Try this:
$('input[type="checkbox"]:eq(i)').prop('checked', true);

Adding ending </tr> tags to an HTML strings within loop

I have an array of elements I would like to put into an HTML table:
var tags_arr = [1,2,3,4,5,6,7,8,9,10,12,13,14,15,16,17,18,19];
I can make an HTML table by simply placing beginning <tr> tags every 4th iteration:
var checks = "<table border=1>";
for (var c = 0; c < tags_arr.length; c++){
if (c%4 == 0){
checks += "<tr>";
}
checks += "<td>" + tags_arr[c] + "</td>";
}
checks += "</table>";
$("body").append(checks);
JSBIN
However this solution relies on the browser to inject the closing <\tr> tag when it "sees" the new opening tag. The browser also seems not to care that the last row has fewer <td> cells than the previous rows do.
It works, but is there a way to make expand this so as not to completely rely on the browser. I've tried using a regex to inject them into the string, but it seems like there should be a way to do so in the loop. Is it feasible? Or since it only has to work in modern browsers, can I just rely on Chrome and Firefox to do the cleanup for me?
EDIT:
hacky regex way:
checks = checks.replace(/(<tr>)/g, "</tr><tr>").replace(/<\/tr>/, "");
checks += "</tr></table>";
The HTML5 spec explicitly tells us that it's not necessary to close <tr> and <td> tags in the obvious scenarios:
No need to close a <td> before the next <td> or <tr> or table block section (<tbody>, <tfoot>), or the </table> closing tag.
No need to close a <tr> before the next <tr>, block section, or table close.
I seriously doubt you'll run into modern browsers that won't do the right thing here. I bet even IE6 will do it properly.
You can simply append the TR closing tag before appending the starting TR tag:
for (var c = 0; c < tags_arr.length; c++){
if (c%4 == 0){
if (c !== 0) checks +="</tr>";
checks += "<tr>";
}
checks += "<td>" + tags_arr[c] + "</td>";
}
checks += "</tr></table>";
PS: Take care of the edge cases.
EDIT:
A more elgant solution is to distribute the items in arrays before hand:
var distributed = [];
var tags_arr = [1,2,3,4,5,6,7,8,9,10,12,13,14,15,16,17,18,19];
while(tags_arr.length > 0) {
distributed.push(tags_arr.splice(0,4));
}
And then use some smart loops to create the html:
var html = distributed.reduce(function(html, item){
var td = item.reduce(function(html, item){
return html + '<td>' + item + '</td>';
}, '');
return html + '<tr>' + td + '</tr>';
}, '');
html = '<table border=1>' + html + '</table>';
for (var c = 0; c < tags_arr.length; c++){
if (c%4 == 0){
if (c > 0) {
checks += "</tr>";
}
checks += "<tr>";
}
checks += "<td>" + tags_arr[c] + "</td>";
}
if (c > 0) { // Don't add a closing tag if there were no rows at all
checks += "</tr>";
}
Just close the tr tag before opening one. If c == 0 no tag have been opened yet.
Don't forget to close the last tag after the for loop
var checks = "<table border=1>";
for (var c = 0; c < tags_arr.length; c++){
if (c%4 == 0){
if (c > 0)
checks += "</tr>"
checks += "<tr>";
}
checks += "<td>" + tags_arr[c] + "</td>";
}
if (tags_arr.length > 0)
checks += "</tr>"
var cell = 0, len = tags_arr.length;
for(var row = 0; cell < len; row++) {
checks += '<tr>';
for(var col = 0; col < 4 && cell < len; col++, cell++)
checks += '<td>' + tags_arr[cell] + '</td>';
checks += '</tr>';
}
The correct solution - no divisions, no exceptional cases, no extra memory.

Dynamic creation of large html table in javascript performance

I have an application which is used for data analysis and I'm having a few performance issues with the creation of the table. The data is extracted from documents and it is important that all data is presented on one page (pagination is not an option unfortunately).
Using jQuery, I make an ajax request to the server to retrieve the data. On completion of the request, I pass the data to an output function. The output function loops through the data array using a for loop and concatenating the rows to a variable. Once the looping is complete, the variable containing the table is then appended to an existing div on the page and then I go on to bind events to the table for working with the data.
With a small set of data (~1000-2000 rows) it works relatively good but some of the data sets contain upwards of 10,000 rows which causes Firefox to either crash and close or become unresponsive.
My question is, is there a better way to accomplish what I am doing?
Here's some code:
//This function gets called by the interface with an id to retrieve a document
function loadDocument(id){
$.ajax({
method: "get",
url: "ajax.php",
data: {action:'loadDocument',id: id},
dataType: 'json',
cache: true,
beforeSend: function(){
if($("#loading").dialog('isOpen') != true){
//Display the loading dialog
$("#loading").dialog({
modal: true
});
}//end if
},//end beforesend
success: function(result){
if(result.Error == undefined){
outputDocument(result, id);
}else{
<handle error code>
}//end if
if($('#loading').dialog('isOpen') == true){
//Close the loading dialog
$("#loading").dialog('close');
}//end if
}//end success
});//end ajax
};//end loadDocument();
//Output document to screen
function outputDocument(data, doc_id){
//Begin document output
var rows = '<table>';
rows += '<thead>';
rows += '<tr>';
rows += '<th>ID</th>';
rows += '<th>Status</th>';
rows += '<th>Name</th>';
rows += '<th>Actions</th>';
rows += '<th>Origin</th>';
rows += '</tr>';
rows += '</thead>';
rows += '<tbody>';
for(var i in data){
var recordId = data[i].id;
rows += '<tr id="' + recordId + '" class="' + data[i].status + '">';
rows += '<td width="1%" align="center">' + recordId + '</td>';
rows += '<td width="1%" align="center"><span class="status" rel="' + recordId + '"><strong>' + data[i].status + '</strong></span></td>';
rows += '<td width="70%"><span class="name">' + data[i].name + '</span></td>';
rows += '<td width="2%">';
rows += '<input type="button" class="failOne" rev="' + recordId + '" value="F">';
rows += '<input type="button" class="promoteOne" rev="' + recordId + '" value="P">';
rows += '</td>';
rows += '<td width="1%">' + data[i].origin + '</td>';
rows += '</tr>';
}//end for
rows += '</tbody>';
rows += '</table>';
$('#documentRows').html(rows);
I was initially using a jQuery each loop but switched to the for loop which shaved off some ms.
I thought of using something like google gears to try offloading some of the processing (if that's possible in this scenario).
Any thoughts?
joinHi,
The rendering is a problem, but there is also a problem with concatenating so many strings inside the loop, especially once the string gets very large. It would probably be best to put the strings into individual elements of an array then finally use "join" to create the huge string in one fell swoop. e.g.
var r = new Array();
var j = -1, recordId;
r[++j] = '<table><thead><tr><th>ID</th><th>Status</th><th>Name</th><th>Actions</th><th>Origin</th></tr></thead><tbody>';
for (var i in data){
var d = data[i];
recordId = d.id;
r[++j] = '<tr id="';
r[++j] = recordId;
r[++j] = '" class="';
r[++j] = d.status;
r[++j] = '"><td width="1%" align="center">';
r[++j] = recordId;
r[++j] = '</td><td width="1%" align="center"><span class="status" rel="';
r[++j] = recordId;
r[++j] = '"><strong>';
r[++j] = d.status;
r[++j] = '</strong></span></td><td width="70%"><span class="name">';
r[++j] = d.name;
r[++j] = '</span></td><td width="2%"><input type="button" class="failOne" rev="';
r[++j] = recordId;
r[++j] = '" value="F"><input type="button" class="promoteOne" rev="';
r[++j] = recordId;
r[++j] = '" value="P"></td><td width="1%">';
r[++j] = d.origin;
r[++j] = '</td></tr>';
}
r[++j] = '</tbody></table>';
$('#documentRows').html(r.join(''));
Also, I would use the array indexing method shown here, rather than using "push" since, for all browsers except Google Chrome it is faster, according to this article.
Displaying that many rows is causing the browser's rendering engine to slow down, not the JavaScript engine. Unfortunately there's not a lot you can do about that.
The best solution is to just not display so many rows at the same time, either through pagination, or virtual scrolling.
The way you are building your string will cause massive amounts of garbage collection.
As the string gets longer and longer the javascript engine has to keep allocating larger buffers and discarding the old ones. Eventually it will not be able to allocate sufficient memory without recycling the remains of all the old strings.
This problem gets worse as the string grows longer.
Instead try adding new elements to the DOM one at a time using the jQuery manipulation API
Also consider only rendering what is visible and implement your own scrolling.
You can do couple of things to increase the performance:
your rows variable is getting bigger and bigger so, don't store the html in one variable. solution can be $.each() function and each function you append the element into DOM. But this is minor adjustment.
Html generating is good, but you can try DOM creating and appending. Like $('<tr></tr>').
And finally, this will solve your problem for sure : use multiple ajax call in the first ajax call collect how many data is available and fetch approximately 1,000 or may be more data. And use other calls to collect remaining data. If you want, you can use synchronous call or Asynchronous calls wisely.
But try to avoid storing the value. Your DOM size will be huge but it should work on moder browsers and forget about IE6.
#fuel37 : Example
function outputDocumentNew(data, doc_id) {
//Variable DOM's
var rowSample = $('<tr></tr>').addClass('row-class');
var colSample = $('<td></td>').addClass('col-class');
var spanSample = $('<span></span>').addClass('span-class');
var inputButtonSample = $('<input type="button"/>').addClass('input-class');
//DOM Container
var container = $('#documentRows');
container.empty().append('<table></table>');
//Static part
var head = '<thead>\
<tr>\
<th width="1%" align="center">ID</th>\
<th width="1%" align="center">Status</th>\
<th width="70%">Name</th>\
<th width="2%">Actions</th>\
<th width="1%">Origin</th>\
</tr>\
</thead>';
container.append(head);
var body = $('<tbody></tbody>');
container.append(body);
//Dynamic part
$.each(data, function (index, value) {
var _this = this;
//DOM Manupulation
var row = rowSample.clone();
//Actions
var inpFailOne = inputButtonSample.clone().val('F').attr('rev', _this.id).addClass('failOne').click(function (e) {
//do something when click the button.
});
var inpPromoteOne = inputButtonSample.clone().val('P').attr('rev', _this.id).addClass('promoteOne').click(function (e) {
//do something when click the button.
});
row
.append(colSample.clone().append(_this.id))
.append(colSample.clone().append(spanSample.colne().addClass('status').append(_this.status)))
.append(colSample.clone().append(spanSample.colne().addClass('name').append(_this.name)))
.append(colSample.clone().append(inpFailOne).append(inpPromoteOne))
.append(colSample.clone().append(_this.origin));
body.append(row);
});
}
in this process you need to create & maintain id's or classes for manipulation. You have the control to bind events and manipulate each elements there.
Answering to get formatting
What happens if you do
for(var i in data){
var record = data[i];
var recordId = record.id;
rows += '<tr id="' + recordId + '" class="' + record.status + '">';
rows += '<td width="1%" align="center">' + recordId + '</td>';
rows += '<td width="1%" align="center"><span class="status" rel="' + recordId + '"><strong>' + data[i].status + '</strong></span></td>';
rows += '<td width="70%"><span class="name">' + record.name + '</span></td>';
rows += '<td width="2%">';
rows += '<input type="button" class="failOne" rev="' + recordId + '" value="F">';
rows += '<input type="button" class="promoteOne" rev="' + recordId + '" value="P">';
rows += '</td>';
rows += '<td width="1%">' + record.origin + '</td>';
rows += '</tr>';
}//end for
Per others suggestions (I'm not reputable enough to comment yet, sorry!), you might try the TableSorter plugin to handle only displaying a usable amount of data at a time.
I don't know how it fares at very high numbers of rows, but their example data is 1000 rows or so.
This wouldn't help with JS performance but would keep the burden off the browser renderer.
Could try this...
Improve Loops
Improve String Concat
var tmpLst = [];
for (var i=0, il=data.length; i<il; i++) {
var record = data[i];
var recordId = record.id;
tmpLst.push('<tr id="');
tmpLst.push(recordId);
tmpLst.push('" class="');
tmpLst.push(record.status);
tmpLst.push('">');
tmpLst.push('<td width="1%" align="center">');
...ect...
}
rows += tmpLst.join('');
This might squeeze an extra bit of performance...
var lstReset = i * lstReset.length;
tmpLst[lstReset + 1]='<tr id="';
tmpLst[lstReset + 2]=recordId;
tmpLst[lstReset + 3]='" class="';

Categories

Resources