Updating table in JavaScript - javascript

I have a 2d array, cells that I am using to store some cellData values. I have a displayTable function in JavaScript that I call to display this array as a table. The problem I am running into is that, after I click on a cell it's cellType should change to "start" and then, when the table is immediately redrawn, its color should change to the color specified in its style, which isn't happening. The cell's color stays the same.
function CellData(cellType) {
this.cellType = cellType;
this.crowFlyDistance = Number.Infinity;
this.backtrack = null;
}
function displayTable() {
var table = document.createElement('table');
var tableBody = document.createElement('tbody');
for(var row = 0; row < rowAndColumnSize; row++) {
var rowToAppend = document.createElement('tr');
for(var column = 0; column < rowAndColumnSize; column++) {
var cellToAppend = document.createElement('td');
cellToAppend.className = "baseCell";
cellToAppend.className += " " + cells[row][column].cellType;
cellToAppend.addEventListener("click", clicked);
rowToAppend.appendChild(cellToAppend);
}
tableBody.appendChild(rowToAppend);
}
table.appendChild(tableBody);
document.getElementById("space").appendChild(table);
}
I have the clicked function assigned to selectStart when the page first loads. After the user selects a start location on the table the clicked function should clear the table with the clearTable function then make a call to displayTable to update the table's values.
function selectStart() {
var row = this.parentNode.rowIndex;
var column = this.cellIndex;
if(cells[row][column].cellType == "empty") {
cells[row][column].cellType = "start";
clearTable();
displayTable();
clicked = selectEnd();
alert("Select an end location.");
}
else alert("Select a cell that is empty.");
}
function clearTable() {
var tableDiv = document.getElementById("space");
while(div.firstChild){
div.removeChild(div.firstChild);
}
}
I have different styles defined for each cell's class, so the cell that is clicked on should change color after it is clicked. The problem is that the color of the cell stays the same, and if I set the text for each cell to contain the cellType for that cell it stays as empty. But if I click on the same cell twice in a row, the alert to "Select an empty cell." pops up, meaning that the cells.cellType value for that cell was updated. Am I doing something wrong with the way I update my table?
JSFiddle: http://jsfiddle.net/r4zxb11o/

The JSFiddle you posted has bugs, every time the method clearTable was called it crashed. To remove all the elements inside space you can use space.innerHTML = ''
function clearTable() {
space.innerHTML = '';
}

Related

Why can't I total up these values?

I am building a gradebook web app. I wanted the app to have the ability to calculate grades upon pushing the Final button. However, it's not working for some reason:
var myTable = document.getElementById("myTable");
var r = 0;//how many rows; row index
var c = 1;//how many columns
//make a table
//table must be able to add rows
//table cells should be editable
//save changes?
//
//make a table head row
//all table columns must have a table head
//**
// var firstRow= myTable.insertRow(0);
function addRow(){
//make a new row
var row = myTable.insertRow(r);
//use a while loop to keep creating row cells until you reach last column
var i = 0;
while(i<c){
var cell = row.insertCell(i);
cell.innerHTML ="Students[i]";
i++;
}
r++;
}
function addColumn(){
//make new column
//increment column
var tHead = document.createElement("th");
var allRows= document.getElementsByTagName("tr");//get all rows
//put tHead in first row
allRows[0].append(tHead);
var dateTable = document.createElement("input");
dateTable.type = "date";
tHead.appendChild(dateTable);
//tHead.innerHTML = (c*2);
//add a new cell for each row
var j =1;
while(j<allRows.length){
var row2 = allRows[j];
var cell2 = row2.insertCell(c);
cell2.innerHTML = j;
j++;
}
c++;
f++;
//if there already id a final row, delete it
}
function unEdit(){
//go through every cell
//save input value to a variable
//remove the input cell
var valArray =[];
document.querySelectorAll("td>input").forEach(input => {
var num = parseInt(input.value);
valArray.push(num);
input.remove();
});
//put input value into innerhtml of td
var i = 0;
document.querySelectorAll("td").forEach(td =>{
td.innerHTML=valArray[i];
i++;
});
}
function editTable(){
var allCells = document.getElementsByTagName("td");
for(var k=0; k<allCells.length; k++){
var oldText= allCells[k];
var input = document.createElement("input");
input.type ="number";
input.max = 100;
input.min = 0;
//before making all cells input, save previous innerhtml to var,
//make it into a num instead of a string, and put that value into input
var prev = allCells[k].innerHTML;
prev = parseInt(prev);
input.value = prev;
allCells[k].innerHTML = "";
allCells[k].appendChild(input);
input.onblur;
}
}
function deleteRow(){
document.getElementById("myTable").deleteRow(1);
r--;
}
function deleteColumn(){
//go through each row
//delete cell at each index
var everyRow = document.getElementsByTagName("tr");
for(var p=0; p<everyRow.length; p++){
everyRow[p].deleteCell(-1);
}
c--;
var finalButton = document.getElementById("final");
finalButton.enabled = true;
}
//final grade column
function finalRow(){
//make a <thead>
//make a new cell going down
var finalHead = document.createElement("th");
finalHead.innerHTML= "Final Grade";
var theseRows = document.getElementsByTagName("tr");
theseRows[0].append(finalHead);
for(var t =1; t<theseRows.length; t++){
//go through every cell in the row
//total up the numbers and put it in the final cell
var finalTotal=0;
for(var e =1; e< theseRows[t].length; e++){
var numero = theseRows[t][e].value;
numero = parseInt(numero);
console.log(numero);
finalTotal += numero;
}
//add up the innerhtmls and put it in finalCell
var finalCell = theseRows[t].insertCell(-1);
finalCell.innerHTML = finalTotal;
}
c++;
//disable final button
var finalButton = document.getElementById("final");
finalButton.disabled = true;
var days = document.getElementById("days");
days.disabled = true;
}
addRow();
addColumn();
//make a table head row at the top
//maybe add a print button?
//add a final grade column
//make it so that final row stays final when add new students and days
//do final funtion inside of unEdit() at the end?????
table,td,th{
border: 1px solid black;
border-collapse: collapse;
}
<table id = "myTable"></table>
</script>
<button onclick ="addRow()">Students</button>
<button onclick ="addColumn()" id ="days">Days</button>
<button onclick="editTable()">Edit</button>
<button onclick="unEdit()">Unedit</button>
<button onclick="deleteRow()">Delete Row</button>
<button onclick="deleteColumn()">Delete Column</button>
<button onclick ="finalRow()" id ="final">Final</button>
<button>Print</button>
In the finalRow() function, I can't figure out why the total I keep getting is always 0. Why doesn't it add up the value of the cells? I wanted it to go through every row, get the number values from each cell and total it up. It seems like the issue is with the "numero" variable, but I'm not sure what the issue is.
the first error is because you forgot to declare the variable f, you declared only the variables r and c above.
the second is in the function DeleteRow() there is an indexing error because it finds a negative value when deleting the last row. If you don't even want him to delete the last row, I suggest using a Try-Catch to deal with this error.

Add links to all cells in column of html table

I would like to loop through the cells in the second column of an html table, adding a link to the text in each cell. I have a generic base URL, with a hash that should be defined by an integer in the corresponding cell in the first column of the table.
For example, the text in first cell of the second column should link to:
http://test.example.com/foo.html#1
Where the #1 is defined by the integer in the first cell of the first column (1). Then repeat for each row, where the integer in each cell of the first column should be used for the hash.
Pure js or jquery would work. I have found this snippet of jquery, which seems like a good start for iterating through each cell in the second column:
$('#table1 td:nth-child(2)').each(function(elem) {
//do something with elem
});
Is this jquery method appropriate, and if so, how can I apply the links as described?
As a possible alternative, could I modify the function I use to create the table?:
function createTable(tableData) {
var table = document.getElementById('table1');
var tableBody = document.createElement('tbody');
tableData.forEach(function(rowData) {
var row = document.createElement('tr');
rowData.forEach(function(cellData) {
var cell = document.createElement('td');
cell.appendChild(document.createTextNode(cellData));
row.appendChild(cell);
});
tableBody.appendChild(row);
});
table.appendChild(tableBody);
document.getElementById('wrapper').appendChild(table);
}
createTable(table_input);
Since you are the one creating the table at the first place it is best to modify the code and render it correctly (as oppose to updating it later on client-side).
Based on your comments and clarification here is the updated code, storing the integer value of first column of each row to use it as url on second column:
function createTable(tableData) {
var table = document.getElementById('table1');
var tableBody = document.createElement('tbody');
var theUrl = 'http://test.example.com/foo.html#'; //replace with real URL
tableData.forEach(function(rowData) {
var row = document.createElement('tr');
var i = 1;
var numValue = '';
var content = '';
rowData.forEach(function(cellData) {
var cell = document.createElement('td');
numValue = (i==1) ? cellData : numValue ;
if(i==2){
content = document.createElement('a');
content.setAttribute('href', theUrl + numValue);
content.innerText = cellData ;
}
else{
content = document.createTextNode(cellData);
}
cell.appendChild(content);
row.appendChild(cell);
i++;
});
tableBody.appendChild(row);
});
table.appendChild(tableBody);
document.getElementById('wrapper').appendChild(table);
}
createTable(table_input);
You may change the following code:
rowData.forEach(function(cellData) {
var cell = document.createElement('td');
cell.appendChild(document.createTextNode(cellData));
row.appendChild(cell);
});
to
for (var i=0; i<rowData.length;i++)
{
var cell = document.createElement('td');
if (i==1)
{
var link=document.createElement("a");
link.href="http://test.example.com/foo.html#"+(i+1);
link.text=rowData[i];
cell.appendChild(link);
}
else
cell.textContent=rowData[i];
row.appendChild(cell);
}

Could you please help me to highlight the selected HTML table row created dynamically through java script

Below is the JavaScript functionalities addRow() I have used to add the rows dynamically and now am trying to highlight the selected row with red color using rowhighlight() function.
/Function to addRows dynamically to the HTML table/
function addRow(msg)
{
var table = document.getElementById("NotesFinancialSummary");
var finSumArr1 = msg.split("^");
var length = finSumArr1.length-1;
alert("length"+ length);
for(var i=1; i<finSumArr1.length; i++)
{
var rowValues1 = finSumArr1[i].split("|");
tb=document.createElement("tbody");
var tbody=document.createElement("tbody");
table.appendChild(tbody);
var tr=document.createElement("tr");
tbody.appendChild(tr);
for(var k=0;k<=10;k++)//adding data to table dynamically
{
var td=document.createElement("td");
tr.appendChild(td);
var element1=rowValues1[k];
td.innerHTML =element1;
tr.onclick=function(){
rowhighlight(this);//calling the rowhighlight function
}
}
}
}
function rowhighlight(x)
{
var index = x.rowIndex;
document.getElementById("NotesFinancialSummary").rows [index].style.backgroundColor = "red";
}
One approach is to first loop through the other rows and remove the styling (really should be a class) then apply the styling (again, class) to the selected row.
Here's one way of doing it:
function rowHighlight() {
var selectedRows = document.getElementsByClassName('selected');
for (var n = 0; n < selectedRows.length; n++) {
selectedRows[n].className = '';
}
this.className = 'selected'
}
And here's a working example of it, though very simple: fiddle time!

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.

Swap canvas element with another table cells value

I have a 3x3 HTML table with each element storing a separate <canvas> element
There is one cell in the table containing text instead of a <canvas>
When a cell containing an image is clicked and the cell to its right contains text, I'm trying to swap that cell's canvas with the text stored in the neighbouring cell.
With the following code every time I click a cell, the canvas element disappears and only swaps the text, leaving the cell on the right no longer containing text or a <canvas>
I tried accessing the canvas using ("#blankCell").html() but it does not work .. anybody know how to access this content for swapping of values?
I created a simplified jsFiddle for this: http://jsfiddle.net/bobbyrne01/dbMnM/1/
$(function () {
var $tbl = $('<table border="1">').attr('id', 'grid');
var $tbody = $('<tbody>').attr('id', 'tableBody');
var tileCount = 0;
var rowCount = $("#numOfPieces").val();
for (var i = 0; i < rowCount; i++) {
var trow = $("<tr>").attr('id', 'row' + i); // New row
for (var j = 0; j < rowCount; j++) {
$cell.append(canvasArray[tileCount]); // Each data cell contains separate canvas element
$cell.appendTo(trow);
tileCount++;
}
trow.appendTo($tbody);
}
$tbl.append($tbody);
$('table').remove(); // removes previous table if it existed
$('body').append($tbl);
});
$('#grid tr:nth-child(2) td:last').prev().text("empty");
$('#grid tr:nth-child(2) td:last').prev().attr('id', 'blankCell');
Listens for clicks ..
$('body').on('click', '#grid td', function(e) {
var $this = $(this);
if ($(this).closest('td').next("#blankCell").length){
blank = $(this).closest('td').next("#blankCell");
if (blank.length) {
//alert('blank to the right');
$this.before(blank);
}
$(this).attr('id', 'blankCell');
$(this).closest('td').next("#blankCell").attr('id', 'piece');
}
Appreciate any help!
Instead of using canvas elements for swapping, it now only has text.
The goal is if a user selects a cell beside the blankCell I would like to swap that cell with the blankCell, if the user selects a cell not beside blankCell I would like nothing to happen.
Only concerned with left and right detection for now.
Make it simple and just exchange the whole table cells, not their contents or content strings and attributes:
$('#grid').on('click', 'td', function(e) {
var $this = $(this),
blank = $this.next("#blankCell");
if (blank.length) {
//alert('blank to the right');
$this.before(blank);
}
});
OK, some more complicated code for checking the position included:
var empty = $("#empty").get(0);
$('#grid').on('click', 'td', function(e) {
if (!empty || this == empty) return; // abort, abort!
var currow = this.parentNode,
emptyrow = empty.parentNode;
var cx = this.cellIndex,
cy = currow.rowIndex,
ex = empty.cellIndex,
ey = emptyrow.rowIndex;
if (cx==ex && Math.abs(cy-ey)==1 || cy==ey && Math.abs(cx-ex)==1) {
// empty and this are next to each other in the grid
var afterempty = empty.nextSibling,
afterthis = this.nextSibling;
currow.insertBefore(empty, afterthis);
emptyrow.insertBefore(this, afterempty);
}
});
Demo at jsfiddle.net

Categories