Change color of cells based on value in DB - javascript

I'm trying to accomplish the following.
I have a "grid" in which each cell can be 'owned' by a user.
To 'own' some new cells, the user should be able to click on two points (in a straight line) on the grid and then confirm by clicking on a button or cancel by clicking on another button, which updates the state of the grid. When both points are clicked, the cells between the two clicked ones must change color.
I should mention that I would like to achieve this only by using plain Javascript / PHP, if possible.
What I've done so far:
I have stored the 'grid' in a DB, having a record for each cell, containing position (i, j), owner and some other feature.
I query the DB using PHP, I save everything in memory (2D array - the grid is supposed to be small) and represent the grid by generating an HTML table
I'm trying to use JS to change the color of the cells when clicking, but I'm having problems (I'm new to JS and web programming in general).
I'm sure there's some kind of pattern to do what I want to do (surely is not rocket science), and would completely agree the following is pure Spaghetti code, but that's the only way I thought of doing it given my very very limited experience.
I do the following.
I have an HTML page with a named div in which I represent the table
<div id="Vis_table"> <?php echo $table ?> </div>
At the end of the body I have a script, which I report in its essential elements
var click = 0;
var grid = <?php echo json_encode($grid); ?>;
var Y_grid = <?php echo Y_g; ?>;
if (click == 0) { //first click
var cells = document.getElementsByTagName("th"); //take all cells
for (var i = 0; i < cells.length; i++) { // all rows
var row = parseInt(i / (Y_grid)); //dimensions
var col = i % (Y_grid);
cells[i].onclick = (function (xr, yc, index) {
return function () {
if (click == 0) { //first click, start
var tab = "<table>";
for (var x = 0; x < grid.length; x++) {
tab += "<tr>";
for (var y = 0; y < grid[0].length; y++) {
if (x == xr && y == yc) { //if it's the cell i clicked on
tab += "<th class = 'clicked'>" + x + " " + y + "</th>";
} else {
tab += "<th class = 'free'> </th>";
}
}
tab += "</tr>";
}
tab += "</table>";
click = 1;
document.getElementById("Vis_table").innerHTML = tab;
}
};
})(row, col, i);
}
}
Now, this works just fine, and the clicked cell changes color according to the CSS rules. The problem is that I don't know how to go on (ie color the cells between the first and the second clicked cells).
Do you have any suggestion?

Table prepared via JS(to do this in PHP is your task).
Area marking in JS :)
I've split the task in small steps to its easier to understand. If you got questions, feel free to ask!
/* This is generated by PHP (for testing i do it with js here) >>> */
var rows = 5;
var cols = 10;
var $table = $('#myTable');
for( let row = 1; row <= rows; row++ ) {
$row = $('<tr>');
for( let col = 1; col <= cols; col++ ) {
$col = $('<td>');
$col.text(row + "|" + col);
$col.attr('data-row', row);
$col.attr('data-col', col);
$row.append($col);
}
$table.append($row);
}
/* <<< */
var cells = [];
$('#myTable').click(function(e) {
$cell = $(e.target);
cells.unshift($cell);
if(cells.length > 2) {
cells.pop();
}
resetCells();
markActiveCells();
if ( cells.length == 2 ) {
fillArea();
}
});
function resetCells() {
$('#myTable td').removeClass('active');
$('#myTable td').removeClass('area');
}
function markActiveCells() {
$(cells).each(function() {
$(this).addClass('active');
});
}
function fillArea() {
if( cells.length < 2 ) return;
start_row_cell = (cells[0].data('row') <= cells[1].data('row'))?0:1;
start_col_cell = (cells[0].data('col') <= cells[1].data('col'))?0:1;
start_row = cells[start_row_cell].data('row');
end_row = cells[(start_row_cell+1)%2].data('row');
start_col = cells[start_col_cell].data('col');
end_col = cells[(start_col_cell+1)%2].data('col');
for( let row = start_row; row <= end_row; row++ ) {
for( let col = start_col; col <= end_col; col++ ) {
$('#myTable td[data-row=' + row + '][data-col=' + col + ']').addClass('area');
}
}
}
td {
font-size: 10px;
width: 25px;
height: 25px;
background-color: #EEE;
text-align: center;
}
td.active {
background-color: #FA0 !important;
}
td.area {
background-color: #FDA;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<table id="myTable" cellspacing=0></table>

Related

How to append multiplication result of 2 vars in table cells?

I'm prompting the user to enter a number for row and another for column, then construct a table using the given numbers and numbering each cell accordingly.
However, I want my final result to be displayed as a multiplication table, like the image below:
multiplication table image
And here's what my code looks like so far:
var table = document.getElementById("table");
var temp = "<table border = 1 border-collapse = collapse>";
for (var i = 0; i < row; i++){
temp += "<tr>";
if (i == 0){
for (var j = 0; j < column; j++){
temp += "<td height = 20 width = 40>" + (j+1) + "</td>";
}
} else{
for (var j = 0; j < column; j++){
temp += "<td height = 20 width = 40>" + (i+1) + "</td>";
}
}
temp += "</tr>";
}
temp += "</table>";
table.innerHTML=temp;
That is nearly right already. What you are looking for is to put (i+1)*(j+1) in the second for statement.
I'm not sure how you get your user input. Prompt is not usually a great way to get input but because you said that's what you used the example below uses prompt. The problem with it is that it blocks everything else: not only your own webpage, but the entire browser. If you have not done so already, you might consider getting your user-inputted numbers from an HTML form.
You might also want input checking code. I've included it below in a full HTML/JS solution. It gives the user 3 chances to enter a number for both the row and the column. If the user fails to do so, it outputs an error message.
<html>
<head>
</head>
<body>
<div id='table'></div>
<script>
var table = document.getElementById('table');
var attemptCounter = 0;
var maxAttempts = 2;
var temp='<table border = 1 border-collapse = collapse>';
var row;
var column;
while ((typeof row !=='number' || attemptCounter <= maxAttempts) && isNaN(row) ){
var row = parseInt(prompt("Please enter the number of ROWS for your table:"),10);
if(typeof row ==='number' && !isNaN(row)){break;};
attemptCounter++;
}
if(attemptCounter >= maxAttempts+1){
table.innerHTML = 'Error: expected NUMBER for number of rows';
}
if(attemptCounter < maxAttempts+1){
attemptCounter = 0;
while ((typeof column !=='number' || attemptCounter <= maxAttempts) && isNaN(column) ){
var column = parseInt(prompt("Please enter the number of COLUMNS for your table:"),10);
if(typeof column ==='number' && !isNaN(column)){break;};
attemptCounter++;
}
if(attemptCounter < maxAttempts+1){
for (var i = 0; i < row; i++){
temp += "<tr>";
if (i == 0){
for (var j = 0; j < column; j++){
temp += "<td height = 20 width = 40>" + (j+1) + "</td>";
}
} else {
for (var j = 0; j < column; j++){
temp += "<td height = 20 width = 40>" + (i+1)*(j+1) + "</td>";
}
}
temp += "</tr>";
}
temp += "</table>";
table.innerHTML=temp;
} else {
table.innerHTML = 'Error: expected NUMBER for number of columns';
}
}
</script>
</body>
</html>
Your code nearly works. If you change your (i+1) and (j+1) to (i * j) you'll see that the table created works, except for the first row and first column (as multiplying by 0 will always give 0).
However to deal with the multiplication with 0 in the first column and row there needs to be some conditional that checks if one of the values (either i or j are 0) and then changes the value to 1.
I've only changed what happens in your for loop.
for (var i = 0; i < row; i++){
temp += "<tr>";
for (var j = 0; j < column; j++){
// Here I have split your temp string.
temp += "<td height = 20 width = 40>";
if (i == 0 && j ==0){ // if both i and j are 0 then add a 0 to temp.
temp += 0;
} else {
// Multiply them together changing 0 to 1 (solving the 0's problem)
temp += (i == 0 ? 1 : i) * (j == 0 ? 1 : j);
}
temp += "</td>";
}
temp += "</tr>";
}
temp += "</table>";
table.innerHTML=temp;
i==0?1:i is a ternary operator which is just like a little if statement. It checks if i is 0 and if it evaluates to true it returns 1, otherwise it returns the value of i. Read more about it here
Putting it all together,
temp += (i==0?1:i) * (j==0?1:j) multiplies the values in the table together and also prevents multiplication with 0 in the headings.
Build HTML as string is not bad but it is better to work with DOM model. Here is an example.
<!DOCTYPE html>
<html>
<head>
<title>Build Table</title>
<meta charset="utf-8" />
<style type="text/css">
.tbl{border:solid 1px #ccc}
.tbl tr:first-child td,
.tbl td:first-child{background:#ccc;padding:4px}
</style>
<script type="text/javascript">
'use strict';
function buildTable() {
//get numbers from micro-form
var rows = document.getElementById('rows').value;
var cols = document.getElementById('cols').value;
//create table
var tbl = document.createElement('table');
tbl.className = 'tbl';//it is better then inline style
//note that HTML table has its own DOM model
var tr = tbl.insertRow(-1);//insert new row
//first row is special
tr.insertCell(-1).innerHTML = 'X';
//so treat it accordingly
for (var i = 1; i < cols; i++) {
tr.insertCell(-1).innerHTML = i;//insert new cell and set value inside
}
//remaining rows
for (i = 1; i < rows; i++) {
tr = tbl.insertRow(-1);
//first column is special
tr.insertCell(-1).innerHTML = i;
for (var j = 1; j < cols; j++) {
tr.insertCell(-1).innerHTML = i * j;
}
}
//well done. Place our table in a container
document.getElementById('table').appendChild(tbl);
}
</script>
</head>
<body>
<div>
Rows: <input type="number" id="rows" min="2" max="10" value="10" />
Columns: <input type="number" id="cols" min="2" max="10" value="10" />
<button onclick="buildTable()">Build Table</button>
</div>
<div id="table"></div>
</body>
</html>

How to populate a table with vertical and horizontal results?

HeaderA HeaderB HeaderA HeaderB
stuff 232 hey 3434
world 033 boy 221
bat 435 girl 930
This table is dynamic and gets populated live. Here's my JS code, but the logic is not working correctly. I need to tell it that every 2 columns is a new record, and make a new row every 4th column. Here's what I have so far:
function html_data(data) {
var html = '';
$.each(data, function (index, value) {
if ((index+1) % 4 == 0 && index != 0) {
html += '<td>'+value+'</td>';
html += '</tr>'
} else if ((index+1) % 5 == 0) {
html += '<tr>';
html += '<td>'+value+'</td>';
} else {
html += '<td>'+value+'</td>';
}
html += '</tr>';
});
return html;
}
Obviously the above code is completely wrong, but that's all I have so far. If I can the get the mod logic, I can fill in the blanks.
Try this http://jsfiddle.net/G3JK5/
HTML
<table id="table">
<tr>
<th>HeaderA</th>
<th>HeaderB</th>
<th>HeaderA</th>
<th>HeaderB</th>
<th>HeaderA</th>
<th>HeaderB</th>
</tr>
</table>
<input type="button" id="btn" value="Add some random data" />
Javascript
//Sample usage
var tbl = new weirdTable('table');
document.getElementById('btn').addEventListener('click', function(){
tbl.addData([
parseInt(Math.random() * 100),
parseInt(Math.random() * 100)
]);
});
weirdTable
function weirdTable(tableId){
var _me = null;
var _currentIndex = 0;
var _colCount = 0;
var _lastRowIndex = 0;
var construct = function(tableId){
_me = document.getElementById(tableId);
_colCount = _me.rows[0].cells.length;
_currentIndex = _colCount;
};
this.addData = function(data){
var row = _me.rows[_lastRowIndex];
//or var data = arguments;
for(var i = 0; i < data.length; i++){
if(_currentIndex >= _colCount){
_lastRowIndex++;
_currentIndex = 0;
row = _me.insertRow(_lastRowIndex);
}
row.insertCell(_currentIndex).innerText = data[i];
_currentIndex++;
}
};
construct(tableId);
}
Unless I'm missing something, you can just do this:
if (index%4===0) { // start of a row
html += '<tr>';
}
html += '<td>'+value+'</td>';
if (index%4===3) { // end of row
html += '</tr>';
}

Separate rows and columns while creating 2d dynamic table

I can't separate row and column td's as I create a 2d table with jquery..
How do I create 10 rows 10 columns 2d table:
what I have done so far:
$(document).ready(function () {
for (var i = 1; i <= 10; i++) {
$('.box').append('<td/>' + '</p>');
for (var j = 1; j <= 10; j++); {
$('.box').append('<td/>');
}
}
});
http://jsfiddle.net/VS37n/
thnx in advance!
You want a table that has 10 columns and 10 rows.
var rows = 10;
var cols = 10;
In an HTML table structure, rows come first in the hierarchy, so, create those first:
$(document).ready(function() {
var rows = 10;
var cols = 10;
var box = $('.box');
for (var r = 0; r < rows; r++) {
var tr = $('<tr>');
//Here we will append the columns to the row before appending it to the box.
box.append(tr);
}
});
The above code only makes 10 rows for us. Now we need to add 10 columns to each row:
$(document).ready(function() {
var rows = 10;
var cols = 10;
var box = $('.box');
for (var r = 0; r < rows; r++) {
var tr = $('<tr>');
for (var c = 0; c < cols; c++) {
tr.append($('<td><p></p></td>')); //Create the table cell, with a p element as in your example, and append it to the row.
}
box.append(tr);
}
});
See this FIDDLE
UPDATE
I just noticed that the jQuery selector from your post selects the <div> element with class .box. You want to add these rows and columns, however, to a <table> element, which doesn't exist. I'd suggest you add a <table> element into your HTML, or, add it with Javascript before adding the rows.
If you can add a <table> element inside of your .box div, then you would just change the following line:
var box = $('.box');
to:
var box = $('.box table:first');
If you can't change the HTML for some reason, then you can dynamically create the table before the rows and columns:
var box = $('<table>').appendTo('.box');
Is this what you're trying to do?
$(document).ready(function () {
var tdHtml = "":
var trHtml = "";
var tableHtml = "";
for(var i=1;i<=10;i++)
{
tdHtml += "<td></td>";
}
for(var j=1;j<=10;j++);
{
trHtml += ("<tr>" + tdHtml + "</tr>");
}
tableHtml = ("<table>" + trHtml + "</table>");
$('.box').innerHtml(tableHtml);
});
You had a ; after your for loop :
for (var j = 1; j <= 10; j++); {
$('.box').append('<td/>');
}
Furthermore, you are not adding <tr> elements.
See the updated fiddle

Handle cells with rowspan when hiding table rows

I have a table containing cells with rowspan attributes, I would like to:
Whenever a tr is hidden, the table will rearrange itself correctly
Whenever a tr is shown again, it will be restored to original state
So if you have a table like this clicking on X shouldn't destroy the layout.
and click a come back button, should restore the original layout.
(try removing all rows from bottom-up, and than restoring them from right-to-left, this is a desired flow)
I had some semi-solutions, but all seem too complicated, and i'm sure there is a nice way to handle this.
OK I really spent a hell of a long time over this question, so here goes...
For those of you who just want to see the working solution, click here
Update: I've changed the visual columns calculation method to iterate over the table and create a 2-dimensional array, to see the old method which used the jQuery offset() method, click here. The code is shorter, but more time costly.
The problem exists because when we hide a row, whilst we want all the cells to be hidden, we want the pseudo-cells — that is, the cells that appear to be in the following rows due to the cells rowspan attribute — to persist. To get around this, whenever we come across a hidden cell with a rowspan, we try to move it down the the next visible row (decrementing it's rowspan value as we go). With either our original cell or it's clone, we then iterate down the table once more for every row that would contain a pseudo-cell, and if the row is hidden we decrement the rowspan again. (To understand why, look at the working example, and note that when the blue row is hidden, red cell 9's rowspan must be reduced from 2 to 1, else it would push green 9 right).
With that in mind, we must apply the following function whenever rows are shown/hidden:
function calculate_rowspans() {
// Remove all temporary cells
$(".tmp").remove();
// We don't care about the last row
// If it's hidden, it's cells can't go anywhere else
$("tr").not(":last").each(function() {
var $tr = $(this);
// Iterate over all non-tmp cells with a rowspan
$("td[rowspan]:not(.tmp)", $tr).each(function() {
$td = $(this);
var $rows_down = $tr;
var new_rowspan = 1;
// If the cell is visible then we don't need to create a copy
if($td.is(":visible")) {
// Traverse down the table given the rowspan
for(var i = 0; i < $td.data("rowspan") - 1; i ++) {
$rows_down = $rows_down.next();
// If our cell's row is visible then it can have a rowspan
if($rows_down.is(":visible")) {
new_rowspan ++;
}
}
// Set our rowspan value
$td.attr("rowspan", new_rowspan);
}
else {
// We'll normally create a copy, unless all of the rows
// that the cell would cover are hidden
var $copy = false;
// Iterate down over all rows the cell would normally cover
for(var i = 0; i < $td.data("rowspan") - 1; i ++) {
$rows_down = $rows_down.next();
// We only consider visible rows
if($rows_down.is(":visible")) {
// If first visible row, create a copy
if(!$copy) {
$copy = $td.clone(true).addClass("tmp");
// You could do this 1000 better ways, using classes e.g
$copy.css({
"background-color": $td.parent().css("background-color")
});
// Insert the copy where the original would normally be
// by positioning it relative to it's columns data value
var $before = $("td", $rows_down).filter(function() {
return $(this).data("column") > $copy.data("column");
});
if($before.length) $before.eq(0).before($copy);
else $(".delete-cell", $rows_down).before($copy);
}
// For all other visible rows, increment the rowspan
else new_rowspan ++;
}
}
// If we made a copy then set the rowspan value
if(copy) copy.attr("rowspan", new_rowspan);
}
});
});
}
The next, really difficult part of the question is calculating at which index to place the copies of the cells within the row. Note in the example, blue cell 2 has an actual index within its row of 0, i.e. it's the first actual cell within the row, however we can see that visually it lies in column 2 (0-indexed).
I took the approach of calculating this only once, as soon as the document is loaded. I then store this value as a data attribute of the cell, so that I can position a copy of it in the right place (I've had many Eureka moments on this one, and made many pages of notes!). To do this calculation, I ended up constructing a 2-dimensional Array matrix which keeps track of all of the used-visual columns. At the same time, I store the cells original rowspan value, as this will change with hiding/showing rows:
function get_cell_data() {
var matrix = [];
$("tr").each(function(i) {
var $cells_in_row = $("td", this);
// If doesn't exist, create array for row
if(!matrix[i]) matrix[i] = [];
$cells_in_row.each(function(j) {
// CALCULATE VISUAL COLUMN
// Store progress in matrix
var column = next_column(matrix[i]);
// Store it in data to use later
$(this).data("column", column);
// Consume this space
matrix[i][column] = "x";
// If the cell has a rowspan, consume space across
// Other rows by iterating down
if($(this).attr("rowspan")) {
// Store rowspan in data, so it's not lost
var rowspan = parseInt($(this).attr("rowspan"));
$(this).data("rowspan", rowspan);
for(var x = 1; x < rowspan; x++) {
// If this row doesn't yet exist, create it
if(!matrix[i+x]) matrix[i+x] = [];
matrix[i+x][column] = "x";
}
}
});
});
// Calculate the next empty column in our array
// Note that our array will be sparse at times, and
// so we need to fill the first empty index or push to end
function next_column(ar) {
for(var next = 0; next < ar.length; next ++) {
if(!ar[next]) return next;
}
return next;
}
}
Then simply apply this on page load:
$(document).ready(function() {
get_cell_data();
});
(Note: whilst the code here is longer than my jQuery .offset() alternative, it's probably quicker to calculate. Please correct me if I'm wrong).
Working solution - http://codepen.io/jmarroyave/pen/eLkst
This is basically the same solution that i presented before, i just changed how to get the column index to remove the restriction of the jquery.position, and did some refactor to the code.
function layoutInitialize(tableId){
var layout = String();
var maxCols, maxRows, pos, i, rowspan, idx, xy;
maxCols = $(tableId + ' tr').first().children().length;
maxRows = $(tableId + ' tr').length;
// Initialize the layout matrix
for(i = 0; i < (maxCols * maxRows); i++){
layout += '?';
}
// Initialize cell data
$(tableId + ' td').each(function() {
$(this).addClass($(this).parent().attr('color_class'));
rowspan = 1;
if($(this).attr('rowspan')){
rowspan = $(this).attr("rowspan");
$(this).data("rowspan", rowspan);
}
// Look for the next position available
idx = layout.indexOf('?');
pos = {x:idx % maxCols, y:Math.floor(idx / maxCols)};
// store the column index in the cell for future reposition
$(this).data('column', pos.x);
for(i = 0; i < rowspan; i++){
// Mark this position as not available
xy = (maxCols * pos.y) + pos.x
layout = layout.substr(0, xy + (i * maxCols)) + 'X' + layout.substr(xy + (i * maxCols) + 1);
}
});
}
Solution: with jquery.position() - http://codepen.io/jmarroyave/pen/rftdy
This is an alternative solution, it assumes that the first row contains all the information about the number of the table columns and the position of each on.
This aproach has the restriction that the inizialitation code must be call when the table is visible, because it depends on the visible position of the columns.
If this is not an issue, hope it works for you
Initialization
// Initialize cell data
$('td').each(function() {
$(this).addClass($(this).parent().attr('color_class'));
$(this).data('posx', $(this).position().left);
if($(this).attr('rowspan')){
$(this).data("rowspan", $(this).attr("rowspan"));
}
});
UPDATE
According to this post ensuring the visibility of the table can be manage with
$('table').show();
// Initialize cell data
$('td').each(function() {
$(this).addClass($(this).parent().attr('color_class'));
$(this).data('posx', $(this).position().left);
if($(this).attr('rowspan')){
$(this).data("rowspan", $(this).attr("rowspan"));
}
});
$('table').hide();
As Ian said, the main issue to solve in this problem is to calculate the position of the cells when merging the hidden with the visible rows.
I tried to figure it out how the browser implements that funcionality and how to work with that. Then looking the DOM i searched for something like columnVisiblePosition and i found the position attributes and took that way
function getColumnVisiblePostion($firstRow, $cell){
var tdsFirstRow = $firstRow.children();
for(var i = 0; i < tdsFirstRow.length; i++){
if($(tdsFirstRow[i]).data('posx') == $cell.data('posx')){
return i;
}
}
}
The js code
$(document).ready(function () {
add_delete_buttons();
$(window).on("tr_gone", function (e, tr) {
add_come_back_button(tr);
});
// Initialize cell data
$('td').each(function() {
$(this).addClass($(this).parent().attr('color_class'));
$(this).data('posx', $(this).position().left);
if($(this).attr('rowspan')){
$(this).data("rowspan", $(this).attr("rowspan"));
}
});
});
function calculate_max_rowspans() {
// Remove all temporary cells
$(".tmp").remove();
// Get all rows
var trs = $('tr'), tds, tdsTarget,
$tr, $trTarget, $td, $trFirst,
cellPos, cellTargetPos, i;
// Get the first row, this is the layout reference
$trFirst = $('tr').first();
// Iterate through all rows
for(var rowIdx = 0; rowIdx < trs.length; rowIdx++){
$tr = $(trs[rowIdx]);
$trTarget = $(trs[rowIdx+1]);
tds = $tr.children();
// For each cell in row
for(cellIdx = 0; cellIdx < tds.length; cellIdx++){
$td = $(tds[cellIdx]);
// Find which one has a rowspan
if($td.data('rowspan')){
var rowspan = Number($td.data('rowspan'));
// Evaluate how the rowspan should be display in the current state
// verify if the cell with rowspan has some hidden rows
for(i = rowIdx; i < (rowIdx + Number($td.data('rowspan'))); i++){
if(!$(trs[i]).is(':visible')){
rowspan--;
}
}
$td.attr('rowspan', rowspan);
// if the cell doesn't have rows hidden within, evaluate the next cell
if(rowspan == $td.data('rowspan')) continue;
// If this row is hidden copy the values to the next row
if(!$tr.is(':visible') && rowspan > 0) {
$clone = $td.clone();
// right now, the script doesn't care about copying data,
// but here is the place to implement it
$clone.data('rowspan', $td.data('rowspan') - 1);
$clone.data('posx', $td.data('posx'));
$clone.attr('rowspan', rowspan);
$clone.addClass('tmp');
// Insert the temp node in the correct position
// Get the current cell position
cellPos = getColumnVisiblePostion($trFirst, $td);
// if is the last just append it
if(cellPos == $trFirst.children().length - 1){
$trTarget.append($clone);
}
// Otherwise, insert it before its closer sibling
else {
tdsTarget = $trTarget.children();
for(i = 0; i < tdsTarget.length; i++){
cellTargetPos = getColumnVisiblePostion($trFirst, $(tdsTarget[i]));
if(cellPos < cellTargetPos){
$(tdsTarget[i]).before($clone);
break;
}
}
}
}
}
}
// remove tmp nodes from the previous row
if(rowIdx > 0){
$tr = $(trs[rowIdx-1]);
if(!$tr.is(':visible')){
$tr.children(".tmp").remove();
}
}
}
}
// this function calculates the position of a column
// based on the visible position
function getColumnVisiblePostion($firstRow, $cell){
var tdsFirstRow = $firstRow.children();
for(var i = 0; i < tdsFirstRow.length; i++){
if($(tdsFirstRow[i]).data('posx') == $cell.data('posx')){
return i;
}
}
}
function add_delete_buttons() {
var $all_rows = $("tr");
$all_rows.each(function () {
// TR to remove
var $tr = $(this);
var delete_btn = $("<button>").text("x");
delete_btn.on("click", function () {
$tr.hide();
calculate_max_rowspans();
$(window).trigger("tr_gone", $tr);
});
var delete_cell = $("<td>");
delete_cell.append(delete_btn);
$(this).append(delete_cell);
});
}
function add_come_back_button(tr) {
var $tr = $(tr);
var come_back_btn = $("<button>").text("come back " + $tr.attr("color_class"));
come_back_btn.css({"background": $(tr).css("background")});
come_back_btn.on("click", function () {
$tr.show();
come_back_btn.remove();
calculate_max_rowspans();
});
$("table").before(come_back_btn);
}
if you have any questions or comments let me know.
I'm assuming you want the the rows to shift upward when you hide the row but you do not want the cells to shift left.
Here is what I got http://codepen.io/anon/pen/prDcK
I added two css rules:
#come_back_container{height: 30px;}
td[rowspan='0']{background-color: white;}
Here is the html I used:
<div id="come_back_container"></div>
<table id="dynamic_table" cellpadding=7></table>
<table id="dynamic_table2" cellpadding=7>
<tr style="background-color: red">
<td rowspan="5">a</td>
<td rowspan="1">b</td>
<td rowspan="5">c</td>
<td rowspan="1">d</td>
<td rowspan="2">e</td>
</tr>
<tr style="background-color: grey">
<td rowspan="0">f</td>
<td rowspan="1">g</td>
<td rowspan="0">h</td>
<td rowspan="1">i</td>
<td rowspan="0">j</td>
</tr>
<tr style="background-color: blue">
<td rowspan="0">k</td>
<td rowspan="1">l</td>
<td rowspan="0">m</td>
<td rowspan="1">n</td>
<td rowspan="1">o</td>
</tr>
<tr style="background-color: yellow">
<td rowspan="0">p</td>
<td rowspan="1">q</td>
<td rowspan="0">r</td>
<td rowspan="1">s</td>
<td rowspan="2">t</td>
</tr>
<tr style="background-color: green">
<td rowspan="0">u</td>
<td rowspan="1">v</td>
<td rowspan="0">w</td>
<td rowspan="1">x</td>
<td rowspan="0">y</td>
</tr>
</table>
The first rule is just to keep the top edge of the table in the same place. The second rule is to make the cells appear blank by blending in with the background, so change accordingly.
Finally here is the js:
$(function () {
//firstTable()
var myTb2 = new dynamicTable();
myTb2.createFromElement( $("#dynamic_table2") );
myTb2.drawTable()
$(window).on("tr_hide", function (e,data){
var tbl = data.ctx,
rowIndex = data.idx;
tbl.hideRow.call(tbl, rowIndex);
})
$(window).on("tr_show", function (e,data){
var tbl = data.ctx,
rowIndex = data.idx;
tbl.showRow.call(tbl, rowIndex);
})
})
function dynamicTableItem(){
this.height = null;
this.content = null;
}
function dynamicTableRow(){
this.color = null;
this.items = []
this.show = true
this.setNumColumns = function(numCols){
for(var i=0;i<numCols;i++){
var item = new dynamicTableItem();
item.height = 0;
this.items.push(item)
}
}
this.addItem = function(index, height, content){
var item = new dynamicTableItem();
item.height = height;
item.content = content;
if(index>=this.items.length){ console.error("index out of range",index); }
this.items[index] = item;
}
}
function dynamicTable(){
this.element = null;
this.numCols = null;
this.rows = []
this.addRow = function(color){
var row = new dynamicTableRow();
row.color = color;
row.setNumColumns(this.numCols)
var length = this.rows.push( row )
return this.rows[length-1]
}
this.drawTable = function(){
this.element.empty()
var cols = [],
rowElements = [];
for(var i=0;i<this.numCols;i++){
cols.push( [] )
}
for(var r=0; r<this.rows.length; r++){
var row = this.rows[r]
if(row.show){
var $tr = $("<tr>"),
delete_cell = $("<td>"),
delete_btn = $("<button>").text("x")
var data = {ctx: this, idx: r};
delete_btn.on("click", data, function(e){
$(window).trigger("tr_hide", e.data);
})
delete_cell.addClass("deleteCell");
$tr.css( {"background": row.color} );
delete_cell.append(delete_btn);
$tr.append(delete_cell);
this.element.append($tr);
rowElements.push( $tr );
for(var i=0; i<row.items.length; i++){
cols[i].push( row.items[i] );
}
}
}
for(var c=0; c<cols.length; c++){
var cellsFilled = 0;
for(var r=0; r<cols[c].length; r++){
var item = cols[c][r]
var size = item.height;
if(r>=cellsFilled){
cellsFilled += (size>0 ? size : 1);
var el = $("<td>").attr("rowspan",size);
el.append(item.content);
rowElements[r].children().last().before(el);
}
}
}
}
this.hideRow = function(rowIndex){
var row = this.rows[rowIndex]
row.show = false;
var come_back_btn = $("<button>").text("come back");
come_back_btn.css( {"background": row.color} );
var data = {ctx:this, idx:rowIndex};
come_back_btn.on("click", data, function(e){
$(window).trigger("tr_show", e.data);
$(this).remove();
});
$("#come_back_container").append(come_back_btn);
this.drawTable();
}
this.showRow = function(rowIndex){
this.rows[rowIndex].show = true;
this.drawTable();
}
this.createFromElement = function(tbl){
this.element = tbl;
var tblBody = tbl.children().filter("tbody")
var rows = tblBody.children().filter("tr")
this.numCols = rows.length
for(var r=0;r<rows.length;r++){
var row = this.addRow( $(rows[r]).css("background-color") );
var items = $(rows[r]).children().filter("td");
for(var i=0;i<items.length;i++){
var item = $(items[i]);
var height = parseInt(item.attr("rowspan"));
var contents = item.contents();
row.addItem(i,height,contents);
}
}
//console.log(this);
}
}
function firstTable(){
var myTable = new dynamicTable();
myTable.element = $("#dynamic_table");
myTable.numCols = 5
var red = myTable.addRow("red");
red.addItem(0,5);
red.addItem(1,1);
red.addItem(2,5);
red.addItem(3,1);
red.addItem(4,2);
var white = myTable.addRow("grey");
//white.addItem(0,0);
white.addItem(1,1);
//white.addItem(2,0);
white.addItem(3,1);
//white.addItem(4,0);
var blue = myTable.addRow("blue");
//blue.addItem(0,3); //try uncommenting this and removing red
blue.addItem(1,1);
//blue.addItem(2,0);
blue.addItem(3,1);
blue.addItem(4,1);
var yellow = myTable.addRow("yellow");
//yellow.addItem(0,0);
yellow.addItem(1,1);
//yellow.addItem(2,0);
yellow.addItem(3,1);
yellow.addItem(4,2);
var green = myTable.addRow("green");
//green.addItem(0,0);
green.addItem(1,1);
//green.addItem(2,0);
green.addItem(3,1);
//green.addItem(4,0);
myTable.drawTable();
}
I tried to use clear variable and method names but if you have any quests just ask.
PS- I know there is no easy way to add content to the cells right now but you only asked for disappearing rows.

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