I need a table where the user enters how many rows and columns needed, they enter the numbers and the next page creates the table.
They will enter the info which will be saved into a database. The only way I can think to do this is with dynamic tables, is there a better way? Here is some super basic code, I haven't worked out the full table, wanted to get feedback before I continue in case there is a better way and I need to change course.
Simple form:
How many rows <input type="number" id="rowNumber"/><br>
How many columns <input type="number" id="colNumber"/><br>
<button onclick="myFunction()">Checkout</button>
function myFunction() {
var rowNumber = document.getElementById('rowNumber').value;
var colNumber = document.getElementById('colNumber').value;
window.location.href = "website/test.php?rowNumber="+rowNumber+"&colNumber="+colNumber;
}
test.php
<?php
$rowNumber=$_GET['rowNumber'];
$colNumber=$_GET['colNumber'];
?>
<script>
var numRows = "<? echo $rowNumber ?>";
var numCols = "<? echo $colNumber ?>";
var tableString = "<table>",
body = document.getElementsByTagName('body')[0],
div = document.createElement('div');
for (row = 1; row < numRows; row += 1) {
tableString += "<tr onclick=\"fnselect(this)\"<? if($rowID == "A") { echo "class ='selected'";} ?>>";
for (col = 1; col < numCols; col += 1) {
tableString += "<td>" + "R" + row + "C" + col + "" + "<input type='text' />" + "</td>";
}
tableString += "</tr>";
}
tableString += "</table>";
div.innerHTML = tableString;
body.appendChild(div);
</script>
Looking into jQuery DataTables. A lot of nice functionality in there.
You can either bind to a JSON data source, or create your own rows manually like this URL:
https://datatables.net/examples/api/add_row.html
So, to use this, you have to reference jquery AND the data tables script. You'll have to either reference them from their given URLs, or download the scripts (I recommend the latter otherwise you create references to outside servers).
Related
I have a GAS google apps script web app that has a search box and a button, when clicked, it dynamically loads an HTML table using the input of the user from the previous searchbox, and looks up some data from a google spreadsheet and returns an html table with 3 to 5 rows. the table is created correctly,the first column has only radiobuttons, the second third and 4th has other fields about each user. all of the radiobuttons have the same name i.e. "choiceSelectionRadio", thus they are grouped and only one of them can be selected at once. however, for the next step (posting the information back to the spreadsheet) i need to succesfully identify which radiobutton, and accordingly, which row has the user selected, and for identifying the radiobuttons i need to assign them an id. I tried to name their id by using the count variable in the FOR LOOP, but it is not working. no errors from the console but no assignment so far.
Here is the function i have.
function crearTabla(arrayDatos) {
//si el array de datos es diferente a indefinido y su longitud es diferente a cero
if(arrayDatos && arrayDatos !== undefined && arrayDatos.length != 0){
var tablaResultados = "<table class='table table-sm table-striped' id='dtable' style='font-size:0.8em'>"+
"<thead style='white-space: nowrap'>"+
"<tr>"+
"<th scope='col'>LOTE</th>"+
"<th scope='col'>OP PI</th>"+
"<th scope='col'>CODIGO PI</th>"+
"<th scope='col'>NOMBRE PROD INTERM</th>"+
"<th scope='col'>CANTIDAD (LX)</th>"+
"<th scope='col'>CONVERSION</th>"+
"<th scope='col'>FECHA USAR EN PLANTA</th>"+
"<th scope='col'>CODIGO PT</th>"+
"<th scope='col'>DESCRIPCION</th>"+
"<th scope='col'>SELECCIONADO?</th>"+
"</tr>"+
"</thead>";
//FIRST LOOP: create as many rows as the filtered range in the sheet has
for(var fila=0; fila<arrayDatos.length; fila=fila+1) {
tablaResultados = tablaResultados + "<tr>";
//SECOND LOOP: in each row created, populate with the information filtered
for(var col=0; col<arrayDatos[fila].length+1; col=col+1){
//NOTE: using lenght+1 since i want a final column for placing the radiobuttons
//tablaResultados = tablaResultados + "<td>"+arrayDatos[fila][col]+"</td>";//do loop en each array value
if (col==arrayDatos[fila].length){tablaResultados = tablaResultados + "<td>"+"<input type='radio' name='chooseLote' id='fila'>"+"</td>"} //HERE IS THE PROBLEM************************************************************************
else{
tablaResultados = tablaResultados + "<td>"+arrayDatos[fila][col]+"</td>";//do loop en each array value
}
}
tablaResultados = tablaResultados + "</tr>";
}
tablaResultados = tablaResultados + "</table>";
var div = document.getElementById('espacioParaTablaResultados');
div.innerHTML = tablaResultados;
}else{
var div = document.getElementById('espacioParaTablaResultados');
//div.empty()
div.innerHTML = "No se encontraron coincidencias";
}
}
I prefer to use DOM. This is how I would do it. The reason I prefer DOM is because to achieve this with writing html would require concatination of strings to get the complete html tag. And its easier to add attributes to an html element.
First I would include the table with header in the body of the html page with an id. In this case "myTable".
Then I would get the data from the spreadsheet getData() or this could be done through templated html.
Then I create a radio button in the first column. I assign a value equal to the row it is on.
Then I have a "Submit" button to identify which of the radio buttons has been selected.
My spreadsheet looks like this.
<!DOCTYPE html>
<html>
<head>
<base target="_top">
</head>
<body>
<table id="myTable">
<input id="selectButton" type="button" value="Select" onclick="selectOnClick()">
<tr>
<th>Button</th>
<th>Id</th>
<th>Name</th>
<th>Date</th>
</tr>
</table>
<script>
function selectOnClick() {
let table = document.getElementById("myTable");
for( let i=1; i<table.rows.length; i++ ) { // skip header row
let row = table.rows[i];
let cell = row.cells[0];
if( cell.firstChild.checked ) {
alert(cell.firstChild.value);
return;
}
}
}
function createRow(i,data) {
let table = document.getElementById("myTable");
let row = document.createElement("tr");
let cell = document.createElement("td");
let button = document.createElement("input");
button.setAttribute("type", "radio");
button.setAttribute("name", "radioGroup");
button.setAttribute("value",i+1);
cell.appendChild(button);
row.appendChild(cell);
data.forEach( value => {
let text = document.createTextNode(value);
cell = document.createElement("td");
cell.appendChild(text);
row.appendChild(cell);
}
)
table.appendChild(row);
}
(function() {
google.script.run.withSuccessHandler(
function (data) {
for( let i=0; i<data.length; i++ ) {
createRow(i+1,data[i]);
}
}
).getData();
})();
</script>
</body>
</html>
When the page is displayed it looks like this. And if I select a radio button and press the select button a dialog is displayed.
Perhaps this will work for you:
function crearTabla(datA) {
if (datA && datA !== undefined && datA.length !== 0) {
var tblres = "<table class='table table-sm table-striped' id='dtable' style='font-size:0.8em'>" +
"<thead style='white-space: nowrap'>" +
"<tr>" +
"<th scope='col'>LOTE</th>" +
"<th scope='col'>OP PI</th>" +
"<th scope='col'>CODIGO PI</th>" +
"<th scope='col'>NOMBRE PROD INTERM</th>" +
"<th scope='col'>CANTIDAD (LX)</th>" +
"<th scope='col'>CONVERSION</th>" +
"<th scope='col'>FECHA USAR EN PLANTA</th>" +
"<th scope='col'>CODIGO PT</th>" +
"<th scope='col'>DESCRIPCION</th>" +
"<th scope='col'>SELECCIONADO?</th>" +
"</tr>" +
"</thead>";
for (var i = 0; i < datA.length; i++) {
tblres = tblres + "<tr>";
for (var j = 0; j < datA[i].length + 1; j++) {
tblres = tblres + "<td>" + datA[i][j] + "</td>";
}
tblres = tblres + "<td>" + "<input type='radio' name='chooseLote' id='fila'>" + "</td>"
}
tblres = tblres + "</tr>";
}
tblres = tblres + "</table>";
//I lost track of what was going on down here. I hate everything
not being in English sorry.
}
I was going to use jquery to append rows from sql, but resulting all rows appended in thead first td.
enter image description here
and the code is
<body>
<table id="table">
<thead><td>A</td><td>B</td><td>C</td><td>D</td><td>E</td><td>F</td><td>G</td></thead>
</table>
</body>
</html>
<script>
$(document).ready(function(){
var sql = "select*from test";
$.ajax({
url:"query.php",
data:{
sqls:sql
},
async:false,
success:function(data){
var returned_data = JSON.parse(data);
for(var i=0;i<returned_data.length;i++){
var temp_str = "<tr class='row'>";
temp_str += "<td class='cell'>";
temp_str += "<input class='cell_vale' type='text' value='";
temp_str += returned_data[i]['firstcell']+"'>";
temp_str += "</td>";
... the other 5 cells is same ...
temp_str += "</tr>";
$("table").append(temp_str);
}
}
});
});
</script>
I tried to use tbody but failed,
also tried to remove thead,
but after it the table width will not suit to the cells,
if there are many cells in a row, the cells may display in 2 rows but one tr.
Can anyone help me?Thanks
First of all, I would suggest going over the modern approach of data binding for much easier and cleaner implementations of what you're trying to do. Have a look at VueJS or React, they will certainly help you big time!
Secondly, try moving your temp_str and row appendage outside of the for..loop, like so
var returned_data = [
{ firstcell: 'cell1' },
{ firstcell: 'cell2' },
{ firstcell: 'cell3' }
];
var temp_str = "";
for(var i = 0; i < returned_data.length; i++) {
temp_str += "<tr class='row'>";
temp_str += "<td class='cell'>";
temp_str += "<input class='cell_vale' type='text' value='";
temp_str += returned_data[i]['firstcell'] + "'>";
temp_str += "</td>";
temp_str += "</tr>";
}
$("table").append(temp_str);
And see if it works for you?
<input type="number" id="arrayLength" />
<button id="myButton">Submit</button>
<table id="finalTable"></table>
var tableData = ["<td>"];
function myFunction(){
var tableRow;
tableData.length = document.getElementById("arrayLength").value;
for(i=0; i<tableData.length; i++){
tableData[i] = "This is your table";
tableRow = "<tr>" + tableData[i] + "</tr>";
}
return tableRow;
}
document.getElementById("myButton").onclick = function(){
document.getElementById("finalTable").innerHTML = myFunction();
}
Here what I want, when a user input some number in field, it should create a table with same rows & with data "this is your table" in it. I mean if I input 5, it should make 5 rows & 5 columns with "this is your table" in each cell. Currently it's making only one cell. I know it's far away from desired output, but please help me. Thanks in advance.
The reason you are only getting 1 cell, is because you are assigning to tableRow instead of concatenating. Also, you might want to use a nested for loop, to add columns as well as rows:
function myFunction()
{
var tableRow = ""; //Give a default value here
var length = document.getElementById("arrayLength").value;
for(i=0; i<length; i++){
tableRow += "<tr>";
for(j=0; j<length; j++)
{
tableRow += "<td>";
tableRow += "This is your table";
tableRow += "</td>";
}
tableRow += "</tr>";
}
return tableRow;
}
And here's where I tested it, to make sure it works: http://jsfiddle.net/k8dxqu6h/
You are overwriting tableRow with every iteration. Concatenate, don't assign.
tableRow += "<tr>" + tableData[i] + "</tr>";
^
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.
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="';