javascript/html flowing tables on to new line - javascript

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>

Related

Multiplication Table in Javascript space between numbers?

var numbers = "";
for (var i = 1; i <= 13; i++){
for (var j = 1; j<= 13; j++){
numbers += (i*j) + '';
}
numbers += '<br>';
}
element.innerHTML = numbers;
how can i make a space between every number?
example:
You can use <table> for that to structure your text:
let numbers = "<tbody>";
for (let i = 1; i <= 13; i++){
numbers += '</tr>';
for (let j = 1; j<= 13; j++){
numbers += `<td>${i*j}</td>`;
}
numbers += '</tr>';
}
numbers += "</tbody>"
const element = document.querySelector("#element");
element.innerHTML = numbers;
td {
text-align: right;
width: 2em;
}
<table id="element">
You can use grid approach with some CSS variables to make cell elements, for more details see comments in Code snippet:
Also you could render headings of the cells.
const gridSize = 13;
// get grid element
const grid = document.querySelector('.grid');
// create temporary wrapper
const fragmentWrapper = document.createDocumentFragment();
const renderTop = () => {
for (var i = 0; i <= gridSize; i++) {
if (i === 0) {
// render mult char
renderCell('x');
continue;
}
renderCell(i, true);
}
}
const renderGrid = () => {
// append cells to wrapper
for (var i = 1; i <= gridSize; i++) {
for (var j = 0; j <= gridSize; j++) {
if (j === 0) {
// render heading cell
const num = i;
renderCell(num, true);
continue;
}
// render default cell
const num = i * j;
renderCell(num);
}
}
}
const renderCell = (num, select = false) => {
// create temporary cell
const cell = document.createElement('div');
cell.className = 'cell';
// if requested to highligh cell
select && cell.classList.add('select');
cell.innerText = `${ num }`;
// add cell to grid
fragmentWrapper.append(cell);
}
renderTop();
renderGrid();
// set grid size
grid.style.setProperty('--grid-size', gridSize);
// render grid
grid.append(fragmentWrapper);
.grid {
display: grid;
grid-template-columns: repeat(calc(var(--grid-size) + 1), 1fr);
/* using CSS grid size (set in JS) */
gap: .25rem;
}
.cell {
display: flex;
align-items: center;
/* align content */
justify-content: center;
/* align content */
aspect-ratio: 1/1;
/* make cell square */
border: 1px solid #d4d4d4;
/* show cell borders */
}
.cell.select {
background-color: tomato;
color: white;
font-weight: bold;
}
<div class="grid"></div>
You could be ambitious and, like David said, use CSS to take the weight of how the data is meant to be displayed. I've used CSS grid in this example which means you only need to have one loop, and the CSS takes care of the rest.
const arr = [];
const size = 13;
const limit = size * size;
// Push an HTML template string into
// the array
for (var i = 1; i <= limit; i++) {
arr.push(`<div>${i}</div>`);
}
// Add the joined array (it makes an HTML string
// to the element with the `grid` class
const grid = document.querySelector('.grid');
grid.innerHTML = arr.join('');
.grid {
display: grid;
grid-template-columns: repeat(13, 30px);
grid-gap: 0.2em;
}
.grid div {
border: 1px solid #cdcdcd;
font-size: 0.8em;
background-color: #efefef;
text-align: center;
padding: 0.5em 0;
}
<div class="grid"></div>
Additional documentation
Template/string literals
Edit: you can even create a function that will accept a number, and, using CSS variables, the function will work out the size of the grid required and return the HTML.
function createGrid(columns) {
const arr = [];
const grid = columns * columns;
document.documentElement.style.setProperty('--columns', columns)
// Push an HTML template string into
// the array
for (var i = 1; i <= grid; i++) {
arr.push(`<div>${i}</div>`);
}
return arr.join('');
}
const grid = document.querySelector('.grid');
grid.innerHTML = createGrid(5);
:root {
--columns: 13;
}
.grid {
display: grid;
grid-template-columns: repeat(var(--columns), 40px);
grid-gap: 0.2em;
}
.grid div {
border: 1px solid #cdcdcd;
font-size: 0.8em;
background-color: #efefef;
text-align: center;
padding: 0.5em 0;
}
<div class="grid"></div>
Please use like this
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<script src="https://code.jquery.com/jquery-3.6.0.min.js"></script>
<style>
table {
border-collapse: collapse;
width: 100%;
}
th, td {
text-align: left;
padding: 8px;
}
</style>
</head>
<body>
<div id="test" style="width:800px;height: 350px; border: 1px solid sandybrown;">
</div>
<script>
var element = document.getElementById("test");
var html = "<table>";
for (var i = 1; i <= 10; i++){
html += "<tr>";
for (var j = 1; j<= 10; j++){
html += "<td>"+ (i*j) +"</td>";
}
html += '</tr> ';
}
html += "</table>";
element.innerHTML = html;
</script>
</body>
</html>

Random generated images inside a table disappear

Problem
I tried to randomly generate the position of two images( the images of the characters) in a table td, but sometimes they wouldn't show. One of them or both would just disappear. Try to loading more times (from 1 to 5) the pen to let the bug occur.
Pen
Here is all the code: penHere
Interested functions
How the table\map is building?
it's a 2d array random generated, something like this:
map = [[1,1,1,1,0],
[1,0,0,0,0],
[1,0,1,1,1],
[1,0,0,0,1],
[1,1,1,0,1]]
After I build the map in the table with this one:
function mapGenerate(map){
//loop the 2d array map and change the number with the appropriate img
for(var i = 0; i < map.length; i++) {
var innerArrayLength = map[i].length;
for(var j = 0; j<innerArrayLength; j++){
if(map[i][j] === 0){
map[i][j]="<div class=\"tile\"><img class=\"walkable\" src=\"https://image.ibb.co/bGanFz/floor_Resized.png\"></div>";
}else{
map[i][j]="<img class=\"nonWalkable\" src=\"https://image.ibb.co/m9s1az/volcanoresize.png\">";
}
;
}
$("#tableGame").append("<tr><td>"+ map[i].join('</td><td>') + "</td></tr>")
}
}
with the function below i select the coordinate ( they are corrects, i check it several times in the console)
function placeCharAndItem(char){
let rowCoord= mapA.length;
let cellCoord = mapA[1].length;
//this object is to save 2 random number, the row and the cell
let coord={
row: Math.floor(Math.random() * rowCoord),
cell: Math.floor(Math.random() * cellCoord)
};
//I need this 2 variables to check if the tiles is a correct one
let toCheck = mapA[coord.row][coord.cell];
let check= toCheck.search('nonWalkable');
//if it's not correct(you found the sub-string "non-
//walkable"), this while loop have to generate random new
//coordinates.
while(check != -1){
coord.row=Math.floor(Math.random() * rowCoord);
coord.cell=Math.floor(Math.random() * cellCoord);
toCheck = mapA[coord.row][coord.cell];
check= toCheck.search('nonWalkable');
};
place(coord, char);
};
and finally, after i have 2 valid coordinates i can show the character:
function place(coord, char){
console.log('sei entrato nella funzione place');
var charImage = $("<img>").attr("src", char.image).addClass('char');
var row = $($("#tableGame tr")[coord.row]);
var cell = $($("td", row)[coord.cell]);
var tile = $($(".tile", row)[coord.cell]);
tile.prepend(charImage);
};
these are the css regarding the table and the image char:
#tableGame .td{
position: relative;
}
.char{
z-index: 1000;
}
#tableGame .char {
position: absolute;
}
table{
background-color: black;
border-collapse: collapse;
border: 2px solid white;
box-shadow: 1px 1px 30px white;
}
tr{
border: 0px;
padding: 0px;
margin:0px;
}
td{
border: 0px;
padding: 0px;
margin:0px;
}
I don't understand why sometimes one or both of the images, disappear, any kind of help will be really appreciated.
Assume the map =
[[1,1,1,1,0],
[1,0,0,0,0],
[1,0,1,1,1],
[1,0,0,0,1],
[1,1,1,0,1]]
and coord.row = 0, coord.cell = 4, then $(".tile", row) would be a jQuery object within only 1 .tile. It would set tile to undifined and won't prepend the image.
function place(coord, char){
console.log('sei entrato nella funzione place');
var charImage = $("<img>").attr("src", char.image).addClass('char');
var row = $($("#tableGame tr")[coord.row]);
var cell = $($("td", row)[coord.cell]);
var tile = $($(".tile", row)[coord.cell]);
tile.prepend(charImage);
};
So I think you can replace var tile = $($(".tile", row)[coord.cell]); with var tile = $(".tile", cell);.
function place(coord, char){
console.log('sei entrato nella funzione place');
var charImage = $("<img>").attr("src", char.image).addClass('char');
var row = $($("#tableGame tr")[coord.row]);
var cell = $($("td", row)[coord.cell]);
// will out of bounds if the coord.cell is greater than the jQuery object $(".tile", row)
// var tile = $($(".tile", row)[coord.cell]);
var tile = $(".tile", cell);
tile.prepend(charImage);
};

In a grid built in javascript and html, how do I assign a new text-node value to all cells in the grid that fulfils a condition

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/

DOM nodes creation vs perfomance

I have a function that dynamically creates DOM nodes to accomodate GPS JSON data from a server. As number of nodes increases, browser performance also
degrades. Is there any means to rewrite/tweak the function to improve performance? The simplified node creation part of the function works as shown below:
var table = document.createElement('table');
var tbody = document.createElement('tbody');
table.appendChild(tbody);
for(var i = 0; i<3000;i++){
var tr = document.createElement('tr');
for(var j=0;j<50;j++){
var td = document.createElement('td');
td.style.backgroundColor="#a"+j%10+'c';
tr.appendChild(td);
}
tbody.appendChild(tr);
}
You might want to consider using some kind of Recyclable List.
Such lists recycle their elements by deleting DOM nodes that lie outside the viewport and recreating them when currently viewed.
Long story short - only currently-viewed DOM nodes exist in the DOM at any given time
Another technique is to use a fixed number of cells which you then fill with data according to where the user is scrolled
AFAIK there's no other way to go about this - naively creating 150,000 DOM nodes would completely decimate your scrolling performance
Some Examples
An example in pure JS
An example that's build on top of Polymer
Another example build on top of React
Here's how the pure JS example handles this:
Source: http://elliottsprehn.com/personal/infinite-scroll.html
<!DOCTYPE html>
<style>
* { box-sizing: border-box; }
ul, li { list-style-type: none; padding: 0; margin: 0; }
h1 { font-size: 1.2em; }
#infinite-list {
width: 300px;
border: 1px solid #ccc;
margin: 1em;
position: relative;
}
#infinite-list .scroll-view {
overflow-y: scroll;
height: -webkit-calc(20em + 2px);
}
#infinite-list ul {
position: absolute;
top: 0;
width: -webkit-calc(100% - 16px);
}
#infinite-list li {
height: 2em;
line-height: 2em;
padding-left: 1em;
border-bottom: 1px solid #ccc;
}
#infinite-list li:last-child {
border-bottom: none;
}
#infinite-list .scroll-view .spacer {
height: -webkit-calc(2em * 20000);
}
</style>
<h1>Infinite Scrolling with a fixed number of DOM nodes</h1>
<div id="infinite-list">
<div class="scroll-view">
<ul>
<li></li>
<li></li>
<li></li>
<li></li>
<li></li>
<li></li>
<li></li>
<li></li>
<li></li>
<li></li>
</ul>
<div class="spacer"></div>
</div>
</div>
<script>
(function() {
function randRange(low, high) {
return Math.floor(Math.random() * (high - low)) + low;
}
function randPick(array) {
return array[randRange(0, array.length)];
}
// Randomly create a bunch of test data.
var tlds = ['.com', '.net', '.org', '.edu', '.co.uk'];
var domains = ['google', 'facebook', 'yahoo', 'apple', 'youtube', 'amazon'];
var data = [];
for (var i = 0; i < 20000; ++i) {
data.push(i + ' ' + randPick(domains) + randPick(tlds));
}
var list = document.querySelector('#infinite-list');
var listItems = document.querySelectorAll('#infinite-list li');
var scrollView = document.querySelector('#infinite-list .scroll-view');
var itemHeight = listItems[0].getBoundingClientRect().height;
var previous;
// Propagate scrolling with the mouse wheel.
list.onmousewheel = function(e) {
var delta = e.wheelDeltaY;
if (Math.abs(delta) < itemHeight) {
delta = itemHeight * (delta > 0 ? 1 : -1);
}
scrollView.scrollTop -= delta;
};
function update() {
var current = scrollView.scrollTop;
if (previous == current) {
webkitRequestAnimationFrame(update);
return;
}
previous = current;
var first = Math.ceil(current / itemHeight);
for (var i = 0; i < listItems.length; ++i) {
listItems[i].firstElementChild.textContent = data[first++];
}
webkitRequestAnimationFrame(update);
}
update();
})();
</script>
If you are not already, you might want to hide the element you are creating nodes in, and only show it at the end of your loop.
E.g. set display:none; on it while you are working on it, and then switch back to display:block; (or whichever it is) afterwards.
Update: Actually, you don't even need to hide it, though that's one way to do it. The cleaner way is to wait with adding your table to the document until you finish adding all nodes to it, and then only add it to the body afterwards.
var table = document.createElement('table');
var tbody = document.createElement('tbody');
for(var i = 0; i<3000;i++){
var tr = document.createElement('tr');
for(var j=0;j<50;j++){
var td = document.createElement('td');
td.innerHTML = "#a"+j%10+'c';
td.style.backgroundColor="#a"+j%10+'c';
tr.appendChild(td);
}
tbody.appendChild(tr);
}
table.appendChild(tbody);
document.body.appendChild(table);
It might help to enlist the browser's help by making a template for a single row, then cloning it repeatedly:
var table = document.createElement('table');
var tbody = document.createElement('tbody');
table.appendChild(tbody);
// Make a template row once up front
var rowtemplate = document.createElement('tr');
for(var j=0;j<50;j++){
var td = document.createElement('td');
td.style.backgroundColor="#a"+j%10+'c';
tr.appendChild(td);
}
// Now clone it over and over instead of creating it piecemeal repeatedly
// Must pass true to ensure it clones whole tree, not just top level tr
for(var i = 0; i<3000;i++){
table.appendChild(rowtemplate.cloneNode(true));
}
Obviously, it depends on the browser internals whether this helps, but typically, the browser should be able to clone an existing node tree faster than manually creating each element from scratch.

chess board using javascript and dom

I'm trying to create a chessboard just like this.
I did create a table And don'r know how to colour it. A also need to print the board name (like A1, A2, ... H8) and be able to pust any figuere in any of the cell.
For start this is the code to create a table:
<!doctype html>
<html>
<head>
<meta charset="utf-8">
<title>ChessBoard</title>
<script type="text/javascript">
function CreateTable(){
var poz = document.getElementById('space');
// createing table adn inserting into document
tab = document.createElement('table');
poz.appendChild(tab);
tab.border = '5';
for (var i = 0; i < 8; i++){
// creating row and inserting into document
var row = tab.insertRow(i);
for(var j = 0; j < 8; j++){
// creating cells and fill with data (numbers)
var cell = row.insertCell(j);
cell.innerHTML = i*j;
cell.style.backgroundColor = 'blue';
cell.style.color = 'white';
cell.style.height = '50px';
cell.style.width = '50px';
};
};
}
</script>
</head>
<body onload="CreateTable()" id ="space">
</body>
</html>
How do i fill specific cell with figure (like A3, E5, H8, ...)? Figure are imgages.
Part 2:
I did create a board with your help.
Now I'm trying to do some more from this code.
How do I put several different images into several cells? I'm trying to get right working code, but with no success. This images should appear when the tabel will be loaded (when i press button CreateTable).
I try to create with this code:
In this point I would like to put figures on board. When i create table it should be blank. Then there will be buttons to add figures. At the beginning for each different figure own button
something like this:
function addKing(idStr){
cell = document.getElementById(idStr);
if(cell != null){
// works for color, doesn't work for images!!
// cell.style.backgroundColor = 'red';
cell.src = 'http://akramar.byethost8.com/images/SplProg/DN3/images/50px/king_m.png'
}
}
Button addKing in html:
<button id="king" onclick="addKing(prompt('Insert field'))">Add King</button>
upgrading previousu code to even better if i can put all together and select which one I like to insert (promtpt window 1: what figure:'king, queen, ...', prompt window 2: on what position would you like to insert: 'A1, B3, ...')).
function addImage (type, position){
var img = ??
}
When I pres button add image the prompt window should appear and ask for type (king, queen, root, ...) and location (A1, B4, ...) (for further update perhaps even color (black or white) but let build step by step).
All tis chessboard I would like to build just in javascript and with dom.
link to not working exaple: jsfiddle
Assuming you need to support only modern browsers, the chess-board is entirely do-able with CSS using counters, and generated-content:
table {
empty-cells: show;
}
td {
width: 2em;
height: 2em;
line-height: 2em;
text-align: center;
border: 1px solid #000;
}
tbody tr:nth-child(odd) td:nth-child(even),
tbody tr:nth-child(even) td:nth-child(odd) {
color: #fff;
background-color: #00f;
}
tbody tr:nth-child(even) td:nth-child(even),
tbody tr:nth-child(odd) td:nth-child(odd) {
background-color: #999;
}
tbody {
counter-reset: rowNumber;
}
tr {
counter-increment: rowNumber;
counter-reset: cellNumber;
}
td {
counter-increment: cellNumber;
}
td::before {
content: counter(rowNumber, upper-alpha) counter(cellNumber, decimal);
}
JS Fiddle demo.
The above tested in Chromium 24 and Firefox 19, both on Ubuntu 12.10.
And for a JavaScript approach:
var chess = {
createBoard: function (dimension) {
if (!dimension || isNaN(dimension) || !parseInt(dimension, 10)) {
return false;
} else {
dimension = typeof dimension === 'string' ? parseInt(dimension, 10) : dimension;
var table = document.createElement('table'),
tbody = document.createElement('tbody'),
row = document.createElement('tr'),
cell = document.createElement('td'),
rowClone,
cellClone;
table.appendChild(tbody);
for (var r = 0; r < dimension; r++) {
rowClone = row.cloneNode(true);
tbody.appendChild(rowClone);
for (var c = 0; c < dimension; c++) {
cellClone = cell.cloneNode(true);
rowClone.appendChild(cellClone);
}
}
document.body.appendChild(table);
chess.enumerateBoard(table);
}
},
enumerateBoard : function (board) {
var rows = board.getElementsByTagName('tr'),
text = document.createTextNode(),
rowCounter,
len,
cells;
for (var r = 0, size = rows.length; r<size; r++){
rowCounter = String.fromCharCode(65 + r);
cells = rows[r].getElementsByTagName('td');
len = cells.length;
rows[r].className = r%2 == 0 ? 'even' : 'odd';
for (var i = 0; i<len; i++){
cells[i].className = i%2 == 0 ? 'even' : 'odd';
cells[i].appendChild(text.cloneNode());
cells[i].firstChild.nodeValue = rowCounter + i;
}
}
}
};
chess.createBoard(10);
JS Fiddle demo.
You can tie an ID to the cell, and then use that ID to reference and updated the background as needed. Here is one example using your code: http://jsfiddle.net/7Z6hJ
function CreateTable(){
var poz = document.getElementById('space');
// createing table adn inserting into document
tab = document.createElement('table');
poz.appendChild(tab);
tab.border = '5';
for (var i = 0; i < 8; i++){
// creating row and inserting into document
var row = tab.insertRow(i);
for(var j = 0; j < 8; j++){
// creating cells and fill with data (numbers)
var cell = row.insertCell(j);
var idStr = String.fromCharCode(97 + i).toUpperCase() + (j+1);
cell.innerHTML = idStr;
cell.id = idStr;
cell.style.backgroundColor = 'blue';
cell.style.color = 'white';
cell.style.height = '50px';
cell.style.width = '50px';
};
};
}
function updateRow(idStr)
{
cell = document.getElementById(idStr);
if(cell != null)
{
cell.style.backgroundColor = 'red';
}
}
As some have mentioned, there is probably a better way to go about this (using css and jQuery, etc) but this answer sticks with what you have so far.
Create a new variable inside the top loop to save the "letter" name of the row (eg. A, B, C).
// creating row and inserting into document
var row = tab.insertRow(i);
var row_letter = String.fromCharCode(65 + i);
Then in the second loop combine the row name and column number.
cell.innerHTML = row_letter + j;
Actually, you need to do some math for correctly coloring and adding labels. Here is the part of code for doing magic:
1 cell.innerHTML = String.fromCharCode(65 + i) + (j + 1);
2 if((i+j)%2){ cell.style.backgroundColor = 'white'; }
3 else{ cell.style.backgroundColor = 'blue'; }
4 cell.style.color = 'black';
5 cell.style.height = '50px';
6 cell.style.width = '50px';
Let me explain. In first line, you take constant 65, which is ASCII code for letter 'A'. While you change the letter by rows, you add i counter to it, so you get 65+0, 65+1, ..., 65+7. Their ASCII equivalents (which you get with String.fromCharCode()) are A, B, ..., H. Now when you have that letter, easily add number of cell to it (j + 1). You can remove '1' and leave just j and make inner loop go from 1 to 8.
Lines 2, 3: Colors are alternating - every second row have the same color. So, just test if is i+j dividable by 2.
For adding figure, you have to make some function that will do cell.innerHTML = <SOME IMAGE>. But, I guess, it's for second question.
Hope I helped you understand the logic.
I case if someone is looking for a way to visualize a chessboard using JS (as I was doing and accidentally came to this question), here is an excellent JS library to do this.
It can create something like this
and much more in no time just by doing the following:
JavaScript
var ruyLopez = 'r1bqkbnr/pppp1ppp/2n5/1B2p3/4P3/5N2/PPPP1PPP/RNBQK2R';
var board = new ChessBoard('board', ruyLopez);
HTML
<div id="board" style="width: 400px"></div>

Categories