I am trying to create an HTML table using JavaScript from a JSON input, however, it's not working out for me.
I am using a marker in the HTML. This will get populated from an innerHTML call in Javascript:
for (var i = 0; i < json.data.length; i++) {
listItem = json.data[i].number + "--" + "( Widget " + json.data[i].widget_id + ") x " + " " + json.data[i].pence_price + " GBP" + json.data[i].number * json.data[i].pence_price + " GBP";
table.push(listItem);
}
document.getElementById('updateOrder').innerHTML = table;
This gives me the following output:
11--Widget 8 x 10GBP 110GBP, 10--Widget 9 x 10GBP 100GBP
What I want is the following:
11--Widget 8 x 10GBP 110GBP
10--Widget 9 x 10GBP 100GBP
Where the number, widget and cost are left-aligned, and the total is right-aligned. I also want separate orders on separate lines.
The data in the JSON is:
{"id":"9518","order_id":"11380","widget_id":"9","number":"10","pence_price":"12"}
I've been pulling my hair out with this; any help or guidance would be appreciated.
var json = {data:{a: {"id":"9518","order_id":"11380","widget_id":"9","number":"10","pence_price":"12"},b:{"id":"9518","order_id":"11380","widget_id":"9","number":"10","pence_price":"12"},c: {"id":"9518","order_id":"11380","widget_id":"9","number":"10","pence_price":"12"}}}
var i,
item,
listItem = "";
for (i in json.data){
item = json.data[i];
listItem += "<div class='table'><div>"+item.number+"--"+"( Widget "+item.widget_id+") x "+" "+item.pence_price+" GBP</div><div class='right'>"+item.number*item.pence_price+" GBP</div></div>";
}
document.getElementById('updateOrder').innerHTML=listItem;
#updateOrder{
padding: 2px;
background: orange;
}
.table{
width: 100%;
background: green;
display: table;
}
.table > div{
display: table-cell;
}
.right{
background: tomato;
}
<div id="updateOrder"></div>
I created a bit larges template which gives you a possible way to create a table, add some styling to it, and do some potential calculations.
I did my best to add as much comments as possible describing what the code is doing.
Since you mentioned you wanted an html table, it is created with the basic table element, and some basic styles were added to differentiate between header, rows and footer
// the data used in this demo
var data = [{
"id": "9518",
"order_id": "11380",
"widget_id": "9",
"number": "10",
"pence_price": "12"
},
{
"id": "9518",
"order_id": "11380",
"widget_id": "9",
"number": "10",
"pence_price": "12"
}
];
function createTable(target, data, columns) {
// gets the elements required based on id for the target div
// and creates the table, thead, tbody & tfoot for the table
let element = document.getElementById(target),
table = document.createElement('table'),
thead = document.createElement('thead'),
header = document.createElement('tr'),
tbody = document.createElement('tbody'),
tfoot = document.createElement('tfoot');
// creates the header
columns.forEach(column => {
// and creates the cells in the header, adding title and class
let cell = document.createElement('td');
cell.innerHTML = column.title;
cell.className = column.class;
header.appendChild(cell);
});
thead.appendChild(header);
// totals is used for the totals for the footer
var totals = {};
for (let i = 0; i < data.length; i++) {
// creates the single rows
let row = document.createElement('tr');
columns.forEach(column => {
// and for each column creates the cell itself
let cell = document.createElement('td');
let value;
// checks what to display
if (column.field) {
// only a property on the data
value = data[i][column.field];
} else if (column.value) {
// a function with a callback value
value = column.value(data[i])
}
// if it should calculate totals, it will do so here
if (column.calculateTotal) {
// in case the column is unknown, it's initialized as 0
// warning: all values will be whole numbers
totals[column.field] = (totals[column.field] || 0) + parseInt( value );
}
// if it has a template, we will replace the %0 with value
// this template function supports only 1 value to be "templated"
if (column.template) {
value = column.template.split('%0').join(value);
}
// set the cell value
cell.innerHTML = value;
// set the class (used to align, for example)
cell.className = column.class;
// add cell to row
row.appendChild(cell);
});
// add row to tbody
tbody.appendChild(row);
}
// empty object would mean false, so only if totals needed to be calculated
// would it create the footer here
if (totals) {
let row = document.createElement('tr');
columns.forEach( column => {
let cell = document.createElement('td'), value = '';
if (column.calculateTotal) {
value = totals[column.field];
if (column.template) {
// can still use the row template
value = column.template.split('%0').join(value);
}
}
cell.innerHTML = value;
cell.className = column.class;
row.appendChild( cell );
});
tfoot.appendChild( row );
}
table.appendChild(thead);
table.appendChild(tbody);
table.appendChild(tfoot);
// set the table on the target element
// warning, calling create table twice will create 2 tables under eachother
element.appendChild(table);
}
// call the create table, with the:
// - target: id in html -> 'target'
// - data: an array defining your data itself
// - columns: an array of objects describing how the table should look
// - title: header
// - field (optional): which property it should show
// - value (optional): callback function that receives a row and should return a value
// - calculatedValue (optional): bool indicating if columns should be summed
// - template (optional): any text you wish to add to your data?
createTable('target', data, [{
title: 'id',
field: 'id',
class: 'left'
},
{
title: 'order',
field: 'order_id',
class: 'left'
},
{
title: 'widget',
field: 'widget_id',
class: 'left'
},
{
title: 'number',
field: 'number',
class: 'center'
},
{
title: 'price',
field: 'pence_price',
class: 'right',
template: '%0 GBP'
},
{
title: 'total',
value: (row) => parseInt(row['number']) * parseInt(row['pence_price']),
class: 'right',
template: '%0 GBP',
calculateTotal: true
}
]);
.left {
text-align: left;
}
.right {
text-align: right;
}
thead tr {
background-color: #777;
}
thead tr td {
font-weight: bold;
color: #fff;
}
tfoot tr td {
font-weight: bold;
}
table td {
padding: 5px;
border-bottom: solid #efefef 1px;
}
<div id="target">
</div>
Related
I have some javascript that generates a variable number of tables. Currently, these tables are displayed in a list down the page:
However, this means that, if there are more than about four tables, the bottom ones are not visible and I need to scroll, meaning I can't see the top ones.
So, what I would like to do is to somehow 'flow' the tables (I can make them narrower) across the page and then on to a new line. So, if I have five tables, then I have (say) two columns on the page, with heats 1, 3 and 5 appearing in column 1; and heats 2 and 4 in column 2.
Here is the section of the code that deals with this:
numGroups = groups.length;
for (var g = 0; g < numGroups; g++)
{
// Create a new table for each group
var t=document.createElement('table');
t.style.borderCollapse = 'collapse';
t.style.cellPadding = '5px';
// Create table header showing group number
var caption = document.createElement( "caption" );
caption.style.textAlign = 'left';
caption.style.paddingTop = '10px';
caption.style.color = "white";
thisGroup = (g+1);
caption.appendChild(document.createTextNode("Group "+thisGroup));
t.appendChild(caption);
var headers = ["Pos", "Driver", "Score", "Best Lap"];
for (var i = 0; i < headers.length; i++)
{
var th = document.createElement( "th" );
th.style.color = headerColour;
th.style.border= theBorderWidth + borderColour;
th.appendChild(document.createTextNode(headers[i]));
t.appendChild(th);
}
// Create a table record for each driver in the group
numGroupDrivers = groups[g].length
for (var k = 0; k <numGroupDrivers; k++) //run through each of the drivers in the heat.
{
var tr=document.createElement('tr'); //create variable 'tr' to create a table row
tr.style.backgroundColor = "transparent";
tr.style.color = textColour;
var name = groups[g][k]; //variable name = nickname
if (name == null && config.d) { //if name isn't blank and this is a digital race...
continue;
}
// Create column for position
var tdPos=document.createElement('td'); //create variable 'tdPos' to create a table cell with data
tdPos.style.width='50px';
tdPos.style.textAlign='center';
tdPos.style.border=theBorderWidth + borderColour;
tdPos.appendChild(document.createTextNode(k+1)); //go through the table in order setting tdPos to row number
tr.appendChild(tdPos); //add tdPos to table record
var tdName=document.createElement('td'); //create variable 'tdName' to create a table cell with data
tdName.style.width='250px';
tdName.style.textAlign='center';
tdName.style.border=theBorderWidth + borderColour;
tdName.appendChild(document.createTextNode(name));
tr.appendChild(tdName);
//Create column for score
var tdScore=document.createElement('td');
tdScore.style.width='80px';
tdScore.style.textAlign='center';
tdScore.style.border=theBorderWidth + borderColour;
for (var l = 0; l <scoreArray.length; l++)
{
if (groups[g][k] == scoreArray[l][0])
{
if (scoreArray[l] == 0)
{
tdScore.appendChild(document.createTextNode("--"));
} else
{
tdScore.appendChild(document.createTextNode(scoreArray[l][1]));
}
}
tr.appendChild(tdScore);
t.appendChild(tr);
}
//Create column for best lap
var tdTime=document.createElement('td');
tdTime.style.width='120px';
tdTime.style.textAlign='center';
tdTime.style.border=theBorderWidth + borderColour;
for (var l = 0; l <scoreArray.length; l++)
{
if (groups[g][k] == scoreArray[l][0])
{
if (scoreArray[l][2] == -1)
{
tdTime.appendChild(document.createTextNode("--"));
} else
{
tdTime.appendChild(document.createTextNode(scoreArray[l][2]));
}
}
tr.appendChild(tdTime);
t.appendChild(tr);
}
}
groupTables[g] = t;
}
Any help gratefully received!
Thanks,
Connal
This isn't a direct answer to your question.
In spirit, though, I think it's the best answer you'll get...
Learn css-flex. JavaScript as presentational layer will be brittle and is not the optimal place for it anyway. On a large screen and mouse (i.e. a laptop or desktop but not a phone) take a look at MDN's tutorial on flex. You'll be able to get what you want in a way that
degrades nicely,
is faster,
is less reliant on platform/browser,
already debugged,
helps you learn another browser-native technology that you'll have on your tool belt tomorrow
might possibly be more accessible to screen readers and other aids for the visually impaired,
flows better, and smoothly, when someone resizes their screen or changes the font size.
Bonus: Anyone in the future maintaining your code (including and especially youself) will find it much easier.
I had resisted learning flex for years, choosing instead to keep moving with my then-current projects as fast as I could. I regret that. I'm screwed; I'll never get that time back. My best way to pay it forward is to highly recommend you give it a shot.
If anyone has another great link for intro to CSS flex that they recommend, please comment.
So, if you adopt this approach, then instead of a TABLE tag contains TR tags containing TD tags, you'll need to generate a DIV (or SECTION) tag that has a specific class attribute, containing a DIV (or ARTICLE) tag per "row", which contain DIV tags per "cell", and after that it's all CSS.
If you're still not convinced, try looking at CSS Zen Garden for examples of how, if you organize your HTML to tell the browser only "what the information is" and leave "what it should look like" to CSS, both tasks are easier to accomplish.
As per my comment, You might set width: 45%; display: inline-table to your tables:
var groups = [
['John', 'Sam', 'Peter'],
['John', 'Sam', 'Peter'],
['John', 'Sam', 'Peter'],
['John', 'Sam', 'Peter'],
['John', 'Sam', 'Peter'],
],
scoreArray = [
[],
[],
[],
[],
[]
],
g = 0,
headerColour = 'gold',
textColour = 'black',
borderColour = 'black',
theBorderWidth = 'solid 1px ';
groups.forEach(idx => {
// Create a new table for each group
var t = document.createElement('table');
t.style.width = '45%';
t.style.display = 'inline-table';
t.style.marginRight = '2%';
t.style.borderCollapse = 'collapse';
t.style.cellPadding = '5px';
// Create table header showing group number
var caption = document.createElement("caption");
caption.style.textAlign = 'left';
caption.style.paddingTop = '10px';
caption.style.color = "white";
thisGroup = (g + 1);
caption.appendChild(document.createTextNode("Group " + thisGroup));
t.appendChild(caption);
var headers = ["Pos", "Driver", "Score", "Best Lap"];
for (var i = 0; i < headers.length; i++) {
var th = document.createElement("th");
th.style.color = headerColour;
th.style.border = theBorderWidth + borderColour;
th.appendChild(document.createTextNode(headers[i]));
t.appendChild(th);
}
// Create a table record for each driver in the group
numGroupDrivers = groups[g].length
for (var k = 0; k < numGroupDrivers; k++) //run through each of the drivers in the heat.
{
var tr = document.createElement('tr'); //create variable 'tr' to create a table row
tr.style.backgroundColor = "transparent";
tr.style.color = textColour;
var name = groups[g][k]; //variable name = nickname
if (name == null && config.d) { //if name isn't blank and this is a digital race...
continue;
}
// Create column for position
var tdPos = document.createElement('td'); //create variable 'tdPos' to create a table cell with data
tdPos.style.width = '50px';
tdPos.style.textAlign = 'center';
tdPos.style.border = theBorderWidth + borderColour;
tdPos.appendChild(document.createTextNode(k + 1)); //go through the table in order setting tdPos to row number
tr.appendChild(tdPos); //add tdPos to table record
var tdName = document.createElement('td'); //create variable 'tdName' to create a table cell with data
tdName.style.width = '250px';
tdName.style.textAlign = 'center';
tdName.style.border = theBorderWidth + borderColour;
tdName.appendChild(document.createTextNode(name));
tr.appendChild(tdName);
//Create column for score
var tdScore = document.createElement('td');
tdScore.style.width = '80px';
tdScore.style.textAlign = 'center';
tdScore.style.border = theBorderWidth + borderColour;
for (var l = 0; l < scoreArray.length; l++) {
if (groups[g][k] == scoreArray[l][0]) {
if (scoreArray[l] == 0) {
tdScore.appendChild(document.createTextNode("--"));
} else {
tdScore.appendChild(document.createTextNode(scoreArray[l][1]));
}
}
tr.appendChild(tdScore);
t.appendChild(tr);
}
//Create column for best lap
var tdTime = document.createElement('td');
tdTime.style.width = '120px';
tdTime.style.textAlign = 'center';
tdTime.style.border = theBorderWidth + borderColour;
for (var l = 0; l < scoreArray.length; l++) {
if (groups[g][k] == scoreArray[l][0]) {
if (scoreArray[l][2] == -1) {
tdTime.appendChild(document.createTextNode("--"));
} else {
tdTime.appendChild(document.createTextNode(scoreArray[l][2]));
}
}
tr.appendChild(tdTime);
t.appendChild(tr);
}
}
document.body.appendChild(t);
})
Use CSS with an external stylesheet and/or a <style> tag at the bottom of the <head>. You can unclutter the JavaScript by removing all of the expressions with the .style property. Use .class to apply CSS styles to the tags. In the example below, are 7 tables. When there are 5 or less tables, they have width: 100%. When there are more than 5 tables all tables are given the .half class which decreases their widths to 50%. The following styles will automatically arrange the tables in 2 columns when they have class .half:
main { display: flex; flex-flow: row wrap; justify-content: flex-start;
align-items: center;...}
/* Flexbox properties will arrange the tables in two columns when
there are more than 5 of them (because .half will be added to
each table */
.half { width: 50%; }
This flow control statement is responsible for the class change:
if (qty > 5) {
tables.forEach(t => t.classList.add('half'));
} else {
tables.forEach(t => t.classList.remove('half'));
}
Also, it's important that you have full control of the tables, in the example, it fetch()es data from a test server to create as many tables as the qty parameter dictates (in example, it's heats = 7). Normally table column widths are determined by content which makes them sporadically unseemly (especially with dynamic content). table-layout: fixed allows you to set the widths of the columns by adding explicit widths directly to the <th> (or the top <td> if <th> are not present):
table { table-layout: fixed; ...}
BTW, the Total Time does not coincide with Position (ie. lowest Total Time should be matched with Position: 1). If you want to sort the columns you'll need to start another question.
<!DOCTYPE html>
<html lang="en">
<head>
<title>NASCAR HEAT</title>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
<style>
*, *::before, *::after { box-sizing: border-box; }
:root { font: 1ch/1 'Segoe UI'; }
html, body { width: 100%; min-height: 100%; margin: 0; padding: 0; }
body { font-size: 2ch; color: white; background: linear-gradient(to bottom, #85a4e5 13%,#053cbd 66%); }
main { display: flex; flex-flow: row wrap; justify-content: flex-start; align-items: center; width: 100%; height: 100%; margin: 0 auto; }
table { table-layout: fixed; width: 100%; border-collapse: collapse; border: 1px solid white; }
caption { font-size: 1.35rem; font-weight: 900; text-align: left; }
th, td { border: 1px solid white; text-align: center; }
th:nth-of-type(2), td:nth-of-type(2) { text-align: left; }
th { font-size: 1.25rem; overflow: hidden; }
td { font-size: 1.15rem; }
th:first-of-type { width: 5%; }
th:nth-of-type(2) { width: 55%; }
th:nth-of-type(3) { width: 15%; }
th:nth-of-type(4) { width: 15%; }
th:last-of-type { width: 15%; }
.half { width: 50%; }
</style>
</head>
<body>
<main></main>
<script>
let heats = 7;
function buildTables(selector, qty = 1) {
const headers = ['Position', 'Driver', 'Average Lap', 'Best Lap', 'Total Time'];
const base = document.querySelector(selector) || document.body;
for (let i = 0; i < qty; i++) {
let t = document.createElement('table');
let tB = document.createElement('tbody');
let cap = t.createCaption();
cap.textContent = `Heat ${i + 1}`;
t.append(tB);
let tH = t.createTHead();
let hRow = tH.insertRow();
for (let j = 0; j < 5; j++) {
hRow.insertCell().outerHTML = `<th>${headers[j]}</th>`;
}
base.append(t);
}
const tables = [...document.querySelectorAll('table')];
for (let k = 0; k < qty; k++) {
fetch('https://my.api.mockaroo.com/nascar.json?key=3634fcf0').then((res) => res.json()).then(res => {
let row;
for (let r in res) {
row = `<tr>
<td>${res[r].Position}</td>
<td>${res[r].Driver}</td>
<td>${res[r]['Average Lap']}</td>
<td>${res[r]['Best Lap']}</td>
<td>${res[r]['Total Time']}</td>
</tr>`;
tables[k].tBodies[0].insertAdjacentHTML('beforeEnd', row);
}
});
}
if (qty > 5) {
tables.forEach(t => t.classList.add('half'));
} else {
tables.forEach(t => t.classList.remove('half'));
}
};
buildTables('main', heats);
</script>
</body>
</html>
Im beginner. I use a HTML table code like this (see below) for Unicode print using char's Decimal values:
<table border="1">
<tr><td>1</td><td></td>
<tr><td>2</td><td></td>
<tr><td>3</td><td></td>
...
<!-- last one: thousandth row -->
<tr><td>1000</td><td>Ϩ</td>
</table>
Is it possible to convert this code into an auto-generated table as follows using JavaScript or JQuery?
<table border="1">
<tr><td>rowNumber</td><td>innerHTML = "\u0026\u0023 + rowNumber + \u003B";</td>
...
<!-- thousandth row -->
<tr><td>rowNumber</td><td>innerHTML = "\u0026\u0023 + rowNumber + \u003B";</td>
</table>
You can start by creating a record-generating function.
function generateRecords(recordFn, limit) {
var records = [];
for (var i = 0; i < limit; i++) {
records.push(recordFn.call(null, i, records));
}
return records;
}
Then you can generate the JSON.
generateRecords(i => {
return {
dec: i,
entity: '&#' + i + ';',
rendered: '&#' + i + ';'
}
}, 1000);
Finally, all you need to do is render the JSON to a table.
Example
(function($) {
$.fn.attrs = function(attrs) {
var $self = this;
if (attrs != null) {
$.each(attrs, function(attr, value) {
$self.attr(attr, value);
});
return $self;
} else {
var result = {};
$.each(this[0].attributes, function(index, attribute) {
result[attribute.name] = attribute.value;
});
return result;
}
};
$.fn.tableFromJson = function(data, attrs, indexField) {
return this.replaceWith($.tableFromJson.apply(this, arguments));
};
$.tableFromJson = function(data, attrs, indexField) {
var fields = Object.keys(data[0]);
if (indexField != null) fields.unshift(indexField);
return $('<table>')
.append($('<thead>').append($('<tr>').append(fields
.map(field => $('<th>').text(field)))))
.append($('<tbody>').append(data
.map((rec, row) => $('<tr>').append(fields
.map((field, col) => $('<td>').html(field === indexField ? (row + 1) : rec[field])))))).attrs(attrs);
};
})(jQuery);
function generateRecords(recordFn, limit) {
var records = [];
for (var i = 0; i < limit; i++) {
records.push(recordFn.call(null, i, records));
}
return records;
}
$('.column:nth-child(1) .result').tableFromJson(generateRecords(i => {
return {
dec: i,
entity: '&#' + i + ';',
rendered: '&#' + i + ';'
}
}, 1000), $('.column:nth-child(1) .result').attrs());
$('.column:nth-child(2) .result').tableFromJson(generateRecords(i => {
return {
dec: i,
rendered: '\u0026\u0023' + i + '\u003B'
}
}, 1000), $('.column:nth-child(2) .result').attrs());
.container { width: 100%; }
.column { display: inline-block; width: 49%; }
table.result {
margin-bottom: 0.5em;
}
table.result,
table.result th,
table.result td {
border: 1px solid black;
border-collapse: collapse;
}
table.result th,
table.result td {
padding: 0.25em;
}
table.result tbody tr td {
text-align: center;
}
table.result thead tr {
background: #BBB;
}
table.result tbody tr:nth-child(even) {
background: #EEE;
}
table.result tbody tr:nth-child(odd) {
background: #FFF;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<div class="container">
<div class="column">
<table class="result"></table>
</div>
<div class="column">
<table class="result"></table>
</div>
</div>
You can try with this
var table = $('<table>').prop('border', 1);
for(i=0; i<1000; i++){
var row = $('<tr><td>' + i + '</td><td>&#'++'</td></tr>'
table.append(row);
}
$('#mytable').append(table);
Yes, Thats possible using javascript/JQuery.For that you need to use proper js code. Below is the sample code that can help you to create table and row,data using jquery.
Below code is responsible to create table tag
$('.class-name').append('<table border="3"></table>');
This will create table element inside your HTML having classname as .class-name.
Once the table tag is created you can create table row and data element as below.
$("table").append(createRow());
function createRow(){
var firstVal = '1st val';
var secondVal = '2nd val';
var tr = '<tr>' ;
tr += '<td>' + firstVal + '</td>';
tr += '<td>' + secondVal + '</td>';
tr +='</tr>';
return tr;
}
This code will generate the table row and will print the table data as st val and 2nd val.
Hope this code may help you
I am relatively new to DOM manipulation with JS and html. Here I am having to build a 10 * 10 grid, with sequential numbers in each cells representing its text node. And the requirement is that, when I click on any cell in the grid, if its text node value is 5 or a multiple of 5, then the text node of this cell and also all other cells in the grid which are multiples of 5 should be replaced with a "**".
Here's my code till now, but I am just failing to implement the conditional replacement of the cell's text node value on a click event. And here's my jsfiddle
Many thanks in advance.
<html>
<head>
<meta charset="utf-8">
<title>Grid with random numbers</title>
<style>
#grid {
margin: 10px;
font-size: 1.25em;
}
table {
border-collapse: collapse;
border: 1px solid #7f7f7f;
}
td {
border: 1px solid #7f7f7f;
width: 50px;
height: 50px;
text-align: center;
}
</style>
</head>
<body>
<div id="grid"></div>
<script>
let totalRows = 10;
let cellsInRow = 10;
let min = 1;
let max = 100;
function drawTable() {
let cellNumber = 1;
let grid = document.getElementById('grid');
let tbl = document.createElement("table");
// Create rows in the table
for (var r = 0; r < totalRows; r++) {
let row = document.createElement("tr");
row.setAttribute('id', (r));
// In each row now create cells
for (var c = 0; c < cellsInRow; c++) {
let cell = document.createElement("td");
let cellText = document.createTextNode(cellNumber++);
let cellFillingStar = document.createTextNode("**");
// each cell should have its 'id' attribute set, as its corresponding cellText value
cell.setAttribute('id', (cellNumber - 1));
cell.appendChild(cellText);
row.appendChild(cell);
// Code to check that each cell got its 'id' attribute set, as its corresponding cellText value.
cell.addEventListener(
"click",
function(e) {
var id = e.target.id;
if (id % 5 == 0) {
$('.table').each(function() {
$(this).find('id').each(function() {
alert("Multiple of 5");
cell.appendChild(cellFillingStar);
})
})
}
},
false);
}
tbl.appendChild(row);
}
grid.appendChild(tbl);
}
window.onload = drawTable;
</script>
</body>
First of all there are some error in your script, like you're trying to loop on a .table object that doesn't exist, as table has no class.
I've tried to simplify it a little bit, using jquery.
the main snippet I've added is this one:
$( "#grid table tr td" ).on( "click", function(event) {
var id = event.target.id;
if (id % 5 == 0) {
$( "#grid table tr td" ).each(function( index ) {
if ($(this).text() % 5 == 0) {
$(this).text($(this).text()+'**');
}
});
}
});
where you assign the event to all td elements, and then, based on their content or id value, you change the text of all relevant td that are multiple of 5.
here is the full working example:
https://jsfiddle.net/xpvt214o/88139/
I am making an app in Electron and have a customer list with their name and location. I have a button that allows the user to add a customer, but when doing so the table shows the index. I have another button that sorts out the names in alphabetical order, but if there are rows that have the index in them, it displays those first...
What I would like is some restriction that puts the rows with (0)(1) at the end of the list when sorted rather than the beginning.
Example:
The customers are sorted correctly, but all the rows with 0 come before the rows with actual words when I would like to have the words before the 0's.
Code:
for some reason in this code snippet it actually doesn't show the 0 or 1 index, but it still sorts the rows with nothing before the rows with text...
const back = document.getElementById('back');
const cust = document.getElementById('cust');
const custDiv = document.getElementById('custDiv');
const addCust = document.getElementById('addCust');
const inv = document.getElementById('inv');
const invDiv = document.getElementById('invDiv');
const addItem = document.getElementById('addItem');
// add customer
function appendRowCust() {
var custList = document.getElementById('custList'), // table reference
row = custList.insertRow(custList.rows.length), // append table row
i;
// insert table cells to the new row
for (i = 0; i < custList.rows[0].cells.length; i++) {
createCell(row.insertCell(i), i, 'row');
}
}
// create DIV element and append to the table cell
function createCell(cell, text, style) {
var div = document.createElement('div'), // create DIV element
txt = document.createTextNode(''); // create text node
div.appendChild(txt); // append text node to the DIV
div.setAttribute('class', style); // set DIV class attribute
div.setAttribute('className', style); // set DIV class attribute for IE (?!)
cell.appendChild(div); // append DIV to the table cell
}
// sort customers
function sortCustTable() {
var custList, rows, switching, i, x, y, shouldSwitch;
custList = document.getElementById("custList");
switching = true;
/* Make a loop that will continue until
no switching has been done: */
while (switching) {
// Start by saying: no switching is done:
switching = false;
rows = custList.getElementsByTagName("TR");
/* Loop through all table rows (except the
first, which contains table headers): */
for (i = 1; i < (rows.length - 1); i++) {
// Start by saying there should be no switching:
shouldSwitch = false;
/* Get the two elements you want to compare,
one from current row and one from the next: */
x = rows[i].getElementsByTagName("TD")[0];
y = rows[i + 1].getElementsByTagName("TD")[0];
// Check if the two rows should switch place:
if (x.innerHTML.toLowerCase() > y.innerHTML.toLowerCase()) {
// I so, mark as a switch and break the loop:
shouldSwitch = true;
break;
}
}
if (shouldSwitch) {
/* If a switch has been marked, make the switch
and mark that a switch has been done: */
rows[i].parentNode.insertBefore(rows[i + 1], rows[i]);
switching = true;
}
}
}
table {
background-color: black;
color: white;
}
tr:nth-child(even) {
background-color: #656565;
}
tr:nth-child(odd) {
background-color: #505050;
}
td {
width: 300px;
max-width: 300px;
height: 30px;
text-align: center;
}
<div id="custDiv">
<div class="addBtns">
<button id="addCust" onclick="appendRowCust()">add customer</button>
</div>
<div style="width: 355px; margin: 0 auto; height: 50px;">
<button id="sortCust" onclick="sortCustTable()">sort</button>
</div>
<div class="custScroll">
<table id="custListTop">
<tr>
<td contenteditable="false">Customers</td>
<td contenteditable="false">Main Location</td>
</tr>
</table>
<table id="custList" contenteditable="true">
<tr>
<td>Someone</td>
<td>something</td>
</tr>
</table>
</div>
</div>
First, you need to summarize what your specifications are. To my understanding, they are this:
Your table needs to be sorted by the first column lexicographically...
Except for numeric or empty strings, which need to be ordered last in the column.
Now, the given answer by #IrkenInvader is correct in pointing out that you don't need to write your own sorting algorithm, but as far as implementing a correct and efficient solution, consider using the built-in algorithm for Array#sort() with some modifications:
function sortCustTable() {
var custList = document.getElementById('custList');
var rows = custList.getElementsByTagName('tr');
var parent = rows[0].parentElement;
var length = rows.length;
var data = [], ref, charCodes;
for (var index = 0; index < length; index++) {
ref = {
row: rows[index],
value: rows[index].firstElementChild.textContent.toUpperCase()
};
if (ref.value === '') {
ref.value = 'k'; // will sort after everything else, including numbers
} else if (!isNaN(ref.value)) {
charCodes = ref.value.split('').map(function (char) {
return Number(char) + 97; // charCode for 'a'
});
// for example, '05' would become 'af'
ref.value = String.fromCharCode.apply(String, charCodes);
}
data.push(ref);
}
data.sort(function (a, b) {
if (a.value > b.value) return 1;
if (a.value < b.value) return -1;
return 0;
});
for (var index = 0; index < length; index++) {
parent.appendChild(data[index].row);
}
}
I opted to use only ECMAScript 5 features, since a comment in your code indicates wanting to support Internet Explorer. I notice you're using const though, so feel free to modify using ES6 if you feel that would be easier.
Putting this together with the rest of your code, you can see it working below. I added some more default values to the table to give you an idea of how well it works:
const back = document.getElementById('back');
const cust = document.getElementById('cust');
const custDiv = document.getElementById('custDiv');
const addCust = document.getElementById('addCust');
const inv = document.getElementById('inv');
const invDiv = document.getElementById('invDiv');
const addItem = document.getElementById('addItem');
// add customer
function appendRowCust() {
var custList = document.getElementById('custList'), // table reference
row = custList.insertRow(custList.rows.length), // append table row
i;
// insert table cells to the new row
for (i = 0; i < custList.rows[0].cells.length; i++) {
createCell(row.insertCell(i), i, 'row');
}
}
// create DIV element and append to the table cell
function createCell(cell, text, style) {
var div = document.createElement('div'), // create DIV element
txt = document.createTextNode(''); // create text node
div.appendChild(txt); // append text node to the DIV
div.setAttribute('class', style); // set DIV class attribute
div.setAttribute('className', style); // set DIV class attribute for IE (?!)
cell.appendChild(div); // append DIV to the table cell
}
// sort customers
function sortCustTable() {
var custList = document.getElementById('custList');
var rows = custList.getElementsByTagName('tr');
var parent = rows[0].parentElement;
var length = rows.length;
var data = [], ref, charCodes;
for (var index = 0; index < length; index++) {
ref = {
row: rows[index],
value: rows[index].firstElementChild.textContent.toUpperCase()
};
if (ref.value === '') {
ref.value = 'k'; // will sort after everything else, including numbers
} else if (!isNaN(ref.value)) {
charCodes = ref.value.split('').map(function (char) {
return Number(char) + 97; // charCode for 'a'
});
// for example, '05' would become 'af'
ref.value = String.fromCharCode.apply(String, charCodes);
}
data.push(ref);
}
data.sort(function (a, b) {
if (a.value > b.value) return 1;
if (a.value < b.value) return -1;
return 0;
});
for (var index = 0; index < length; index++) {
parent.appendChild(data[index].row);
}
}
table {
background-color: black;
color: white;
}
tr:nth-child(even) {
background-color: #656565;
}
tr:nth-child(odd) {
background-color: #505050;
}
td {
width: 300px;
max-width: 300px;
height: 30px;
text-align: center;
}
<div id="custDiv">
<div class="addBtns">
<button id="addCust" onclick="appendRowCust()">add customer</button>
</div>
<div style="width: 355px; margin: 0 auto; height: 50px;">
<button id="sortCust" onclick="sortCustTable()">sort</button>
</div>
<div class="custScroll">
<table id="custListTop">
<tr>
<td contenteditable="false">Customers</td>
<td contenteditable="false">Main Location</td>
</tr>
</table>
<table id="custList" contenteditable="true">
<tr>
<td>Someone</td>
<td>something</td>
</tr>
<tr>
<td>Somebody</td>
<td>1</td>
</tr>
<tr>
<td></td>
<td>1</td>
</tr>
<tr>
<td>0</td>
<td>1</td>
</tr>
<tr>
<td>someone else</td>
<td>1</td>
</tr>
<tr>
<td>somebody else</td>
<td>1</td>
</tr>
</table>
</div>
</div>
Now, to clarify why it sorts this way, let's look at the table values and how we modify them before sorting:
Customers | Main Location
---------------+---------------
Someone | something
Somebody | 1
| 1
0 | 1
someone else | 1
somebody else | 1
We'll discard the second column since we don't use that, and set all the customers to uppercase as well:
Customers |
---------------|
SOMEONE |
SOMEBODY |
|
0 |
SOMEONE ELSE |
SOMEBODY ELSE |
Next we check each string to see if it's empty, if so, we give it the value 'k':
Customers |
---------------|
SOMEONE |
SOMEBODY |
k |
0 |
SOMEONE ELSE |
SOMEBODY ELSE |
Then lastly, we modify any numbers by adding their numeric value to 97, and converting the resulting charCode into a character:
Customers |
---------------|
SOMEONE |
SOMEBODY |
k |
a |
SOMEONE ELSE |
SOMEBODY ELSE |
Sorting lexicographically, we get:
Customers |
---------------|
SOMEBODY |
SOMEBODY ELSE |
SOMEONE |
SOMEONE ELSE |
a |
k |
And putting back in the original values, we get:
Customers | Main Location
---------------+---------------
Somebody | 1
somebody else | 1
Someone | something
someone else | 1
0 | 1
| 1
I added a method to draw your table from an array of cell contents. Sorting the rows and calling drawTableRows will recreate the table in any order the rows array ends up. I added some code to insert mock data every third row so sorting numbers to the bottom can be seen.
This is a bigger change than I usually like to give in answers but I thought you might appreciate seeing a different approach.
var rows = [[ 'Someone', 'something' ]];
function drawTableRows() {
var custList = document.getElementById('custList'); // table reference
custList.innerHTML = '';
for(var i = 0; i < rows.length; i++) {
var row = rows[i];
var tableRow = custList.insertRow(i); // append table row
for(var j = 0; j < row.length; j++) {
createCell(tableRow.insertCell(j), row[j], 'row');
}
}
}
// add customer
function appendRowCust(customer = 0, location = 1) {
//throw in mock data every 3 rows (just a test - remove later)
if(rows.length % 3 === 0) {
customer = 'Real customer ' + rows.length;
location = 'Real location ' + rows.length;
}
rows.push([customer, location]);
drawTableRows();
}
// create DIV element and append to the table cell
function createCell(cell, text, style) {
var div = document.createElement('div'), // create DIV element
txt = document.createTextNode(text); // create text node
div.appendChild(txt); // append text node to the DIV
div.setAttribute('class', style); // set DIV class attribute
div.setAttribute('className', style); // set DIV class attribute for IE (?!)
cell.appendChild(div); // append DIV to the table cell
}
function sortCustTable() {
rows.sort(function(a,b){
//sort by first column
var aVal = a[0];
var bVal = b[0];
//sort by cell content - if content is a number push to bottom.
if((bVal > aVal) || !isNaN(bVal)) {
return -1;
}
if((aVal > bVal) || !isNaN(aVal)) {
return 1;
}
return 0;
});
drawTableRows();
}
table {
background-color: black;
color: white;
}
tr:nth-child(even) {
background-color: #656565;
}
tr:nth-child(odd) {
background-color: #505050;
}
td {
width: 300px;
max-width: 300px;
height: 30px;
text-align: center;
}
<div id="custDiv">
<div class="addBtns">
<button id="addCust" onclick="appendRowCust()">add customer</button>
</div>
<div style="width: 355px; margin: 0 auto; height: 50px;">
<button id="sortCust" onclick="sortCustTable()">sort</button>
</div>
<div class="custScroll">
<table id="custListTop">
<tr>
<td contenteditable="false">Customers</td>
<td contenteditable="false">Main Location</td>
</tr>
</table>
<table id="custList" contenteditable="true">
<tr>
<td>Someone</td>
<td>something</td>
</tr>
</table>
</div>
</div>
You can filter out the differences by attempting to cast to numeric and determining if the current iterated value is an integer. Afterwards simply sort and concatenate the two result sets.
var list = [0, 2, "2", "0", 1, 2, "a", "b", "c"],
numeric = list.filter(value => Number.isInteger(+value)),
alpha = list.filter(value => !Number.isInteger(+value)),
result = alpha.sort().concat(numeric.sort());
To optimize the above you can filter once and push to a separately declared array alpha if the result is false.
var list = [0, 2, "2", "0", 1, 2, "a", "b", "c"],
alpha = [],
numeric = list.filter(value => {
let torf = Number.isInteger(+value);
if (!torf) alpha.push(value);
return torf;
}),
result = alpha.sort().concat(numeric.sort());
Determining the difference between the two would be a micro-optimization that I doubt would be necessary in any situation and the former is more verbose and clear. My suggestion would be to use the first option.
var list = [0, 2, "2", "0", 1, 2, "a", "b", "c"],
numeric = list.filter(value => Number.isInteger(+value)),
alpha = list.filter(value => !Number.isInteger(+value)),
result = alpha.sort().concat(numeric.sort());
console.log(result);
Please explain why the code I borrowed from another SO question produces inconsistent results. The program accepts 2 inputs, rows and columns. It should then generate a table with the exact amount of table rows and table columns that was input. However, the first row seems to multiply out the cells and decrease each row until the final row actually renders what every other row should be.
Also, I noticed that when I move line 25 inside of the inner while loop, that a table that is akin to a pyramid is generated (which is cool) but I cannot explain what it is doing.
$("<tr class='tableRow'>").appendTo('table#container');
So with that said, please help me generate a table that will evenly render rows and columns when the input is equal.
Note: Entering 1 for rows and 1 for columns does return the intended result, but that is the only scenario where it "works". Entering 2 for rows and 2 for columns, produces unintended results.
Sandbox Fiddle
//Table Generator
var c = 10; //parseInt(prompt("Enter column "), 10);
var r = 10; //parseInt(prompt("Enter row "), 10);
var cTmp = c;
var rTmp = r;
function rowLoop() {
$('tr.tableRow').each(function(index) {
var trFound = $("tr.tableRow:eq(" + index + ")");
var rowNum = parseInt(($("tr.tableRow:eq(" + index + ")").index()), 10);
var tdAdd = "<td>test</td>";
if ($(this).index() === rowNum) {
trFound.append(tdAdd);
console.log("Add a TD");
console.log(rowNum + "=" + $(this).index());
console.log(rowNum + "=" + $(this).index());
} else {
console.log(rowNum + "<>" + $(this).index());
console.log(rowNum + "<>" + $(this).index());
}
});
}
while (0 < rTmp) {
cTmp = c;
$("<tr class='tableRow'>").appendTo('table#container');
while (0 < cTmp) {
rowLoop();
cTmp--;
}
document.getElementById("container").innerHTML = document.getElementById("container").innerHTML + "</tr>";
rTmp--;
}
table {
border-collapse: collapse;
border: solid 1px #ACE;
}
tr {
height: 15px;
}
td {
border: solid 1px red;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.8.3/jquery.min.js"></script>
<table id='container'></table>
Instead of using a while-loop why don't you just use a simple nested for-loop? This will make this task much easier to read and comprehend.
//Table Generator
var cols = 10; //parseInt(prompt("Enter column "), 10);
var rows = 10; //parseInt(prompt("Enter row "), 10);
generateTable('container', rows, cols, 'test');
function generateTable(id, rows, cols, fill) {
var elTable = document.getElementById(id);
createRows(elTable, rows, cols, fill);
}
function createRows(elTable, rows, cols, fill) {
for (var row = 0; row < rows; row++) {
elTable.appendChild(createRow(row, cols, fill));
}
}
function createRow(row, cols, fill) {
var elRow = document.createElement('tr');
elRow.className = 'tableRow';
createCols(elRow, row, cols, fill);
return elRow;
}
function createCols(elRow, row, cols, fill) {
for (var col = 0; col < cols; col++) {
elRow.appendChild(createCol(row, col, fill));
}
}
function createCol(row, col, fill) {
var elCol = document.createElement('td');
elCol.innerHTML = fill || row + 'x' + col;
return elCol;
}
table {
border-collapse: collapse;
border: solid 1px #ACE;
}
tr {
height: 15px;
}
td {
border: solid 1px red;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js"></script>
<table id='container'></table>
jQuery Plugin
You can skip the loop altogether and use a range-map. You can actually define an array using:
Array.apply(null, Array(n)) // Where n is an integer greater than 0
You can then map each item in the array to either a function or the current index in the map function.
map(function(_, idx) {
// Return the current index in the map's "loop" callback.
if (val == null) return idx;
// Execute the value as a function; pass index as 1st param.
if (isFunction(val)) return val.call(null, idx);
// Return the value as an (assumed) scalar value.
return val;
});
Here is a jQuery plugin to generate rows and columns using the explained trick above to generate an array at a desired size with an option default value.
//Table Generator
(function($) {
$.fn.generateTable = function(options) {
var defaults = {
rows: 0,
cols: 0,
fill: function(row, col) {
return row + 'x' + col;
}
};
var opts = $.extend(defaults, options);
function fillArray(n, val) {
return Array.apply(null, Array(n)).map(function(_, idx) {
return val == null ? idx : isFunction(val) ? val.call(null, idx) : val;
});
}
function isFunction(value) {
return typeof value == 'function';
}
return $(this).append(fillArray(opts.rows, function(row) {
return $('<tr>', {
class: 'tableRow'
}).append(fillArray(opts.cols, function(col) {
return $('<td>', {
text: isFunction(opts.fill) ? opts.fill.call(null, row, col) : opts.fill
});
}));
}));
};
}(jQuery));
$(function() {
$('#container').generateTable({
rows: 10, //parseInt(prompt("Enter row count"), 10)
cols: 10, //parseInt(prompt("Enter column count"), 10)
fill: 'test'
});
});
table {
border-collapse: collapse;
border: solid 1px #ACE;
}
tr {
height: 15px;
}
td {
border: solid 1px red;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js"></script>
<table id='container'></table>
Stages of Development
Below, you will see how each successive block of code has evolved from its predecessor.
I. Vanilla JavaScript translated to jQuery
//Table Generator
var cols = 10; //parseInt(prompt("Enter column "), 10);
var rows = 10; //parseInt(prompt("Enter row "), 10);
generateTable('#container', rows, cols, 'test');
function generateTable(selector, rows, cols, fill) {
var $el = $(selector)
createRows($el, rows, cols, fill);
}
function createRows($table, rows, cols, fill) {
for (var row = 0; row < rows; row++) {
$table.append(createRow(row, cols, fill));
}
}
function createRow(row, cols, fill) {
var $row = $('<tr>', {
class: 'tableRow'
});
createCols($row, row, cols, fill);
return $row;
}
function createCols($row, row, cols, fill) {
for (var col = 0; col < cols; col++) {
$row.append(createCol(row, col, fill));
}
}
function createCol(row, col, fill) {
return $('<td>', {
text: fill || row + 'x' + col
});
}
table {
border-collapse: collapse;
border: solid 1px #ACE;
}
tr {
height: 15px;
}
td {
border: solid 1px red;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js"></script>
<table id='container'></table>
II. Simple jQuery Plugin
//Table Generator
(function($) {
$.fn.generateTable = function(options) {
var $table = $(this);
_createRows($table, options.rows, options.cols, options.fill);
};
_createRows = function($table, rows, cols, fill) {
for (var row = 0; row < rows; row++) {
$table.append(_createRow(row, cols, fill));
}
};
_createRow = function(row, cols, fill) {
var $row = $('<tr>', {
class: 'tableRow'
});
_createCols($row, row, cols, fill);
return $row;
};
_createCols = function($row, row, cols, fill) {
for (var col = 0; col < cols; col++) {
$row.append(_createCol(row, col, fill));
}
};
_createCol = function(row, col, fill) {
return $('<td>', {
text: fill || row + 'x' + col
});
};
}(jQuery));
var cols = 10; //parseInt(prompt("Enter column "), 10);
var rows = 10; //parseInt(prompt("Enter row "), 10);
$(function() {
$('#container').generateTable({
rows: rows,
cols: cols,
fill: 'test'
});
});
table {
border-collapse: collapse;
border: solid 1px #ACE;
}
tr {
height: 15px;
}
td {
border: solid 1px red;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js"></script>
<table id='container'></table>
III. Advanced jQuery Plugin
//Table Generator
(function($) {
$.fn.generateTable = function(options) {
$(this).append(_fillArray(options.rows, function(row) {
return $('<tr>', {
class: 'tableRow'
}).append(_fillArray(options.cols, function(col) {
return $('<td>', {
text: options.fill || row + 'x' + col
});
}));
}));
};
function _fillArray(n, defaultValue) {
return Array.apply(null, Array(n)).map(function(val, idx) {
if (defaultValue === undefined) return idx;
if (typeof defaultValue == 'function') return defaultValue.call(null, idx);
return defaultValue;
});
}
}(jQuery));
var colCount = 10; //parseInt(prompt("Enter column "), 10);
var rowCount = 10; //parseInt(prompt("Enter row "), 10);
$(function() {
$('#container').generateTable({
rows: rowCount,
cols: colCount,
fill: 'test'
});
});
table {
border-collapse: collapse;
border: solid 1px #ACE;
}
tr {
height: 15px;
}
td {
border: solid 1px red;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js"></script>
<table id='container'></table>
Your problem likely stems from the fact that within each rowLoop() execution, you are using this as selector for iteration:
$('tr.tableRow')
This will grab all rows that are currently in DOM for iteration, not just the current row you are working on.
A few options:
Build a single row object with X number of columns first, then make clones of it and append to table for Y number of rows.
Build Y empty TR's first, then iterate over each TR, appending X TD's.