Unable to reference a dynamically created table using document.getElementById() - javascript

I am trying to reference the table from a function outside createHTMLTableFromTableObject() using document.getElementById(). I have created a button that prints to the console the reference to the position in the table but all I'm getting is an error. I am obviously not referencing the table correctly but everything ive found online says this is how its done. when i do the exact same console statement inside the createHTMLTableFromTableObject(), it is referenced perfectly. What am i doing wrong and is there any way to reference the table correctly?. Note that this is part of a wider bit of code that randomly generates info for the table, i have cut this out and replaced it with "-" in each cell.
<!DOCTYPE html>
<html>
<style>
p,
input {
font: 14px Verdana;
}
.code {
margin-left: auto;
margin-right: auto;
width: 50%;
}
tr:nth-child(even) {background: #FFF}
tr:nth-child(odd) {background: rgb(228, 228, 228)}
.htmlTable {
border: solid 2px #000;
border-collapse: collapse;
margin-left: auto;
margin-right: auto;
}
.thCell {
border: solid 1px #000;
border-collapse: collapse;
padding: 8px 8px;
background-color:rgb(136, 136, 136);
font: 14px Verdana;
color: black;
font-weight: bold;
}
.thAlign {
text-align: center;
}
.tdCell {
border: solid 1px #000;
border-collapse: collapse;
padding: 8px 8px;
font: 14px Verdana;
color: black;
}
.tdTxtAlign {
text-align: left;
}
.tdNumAlign {
text-align: right;
}
</style>
<head>
<meta http-equiv="Content-Type" content="text/html;charset=UTF-8" />
<title>Convert JavaScript Object Data to HTML Table</title>
</head>
<body>
<!-- View showing Table containing randonly generated data -->
<!-- And a Button to generate another random table-->
<div>
<div style="padding: 20px; text-align: center;"><input type="button" onclick="createHTMLTableFromTableObject()"
value="Create Table From JavaScript Object" /></div>
<table style="padding: 20px;" id="showTableData"></table>
<div style="padding: 20px; text-align: center;"><input type="button" onclick="testRef()"
value="Test Reference to Table" /></div>
</div>
<!-- Function to generate a JS Table Object, and a function to convert to HTML Table -->
<script>
//
// The table object - it is the data model!
//
var myTable = [];
//
// generate random data to populate the table model (populates a JavaScript object)
//
function genNewTable(table, numRows) {
for (i = 0; i < numRows; i++) {
// generate table row with random data
table.push({
"Student Name": "-",
"Student ID": "-",
"Assignment 1": "-",
"Assignment 2": "-",
"Assignment 3": "-",
"Assignment 4": "-",
"Assignment 5": "-",
"Average (%)": null
});
// calculate the average grade for the last generated record inserted into table
numAssignments = -3; averageGrade = 0;
studentRecord = table[table.length - 1];
// get the keys for any column matching "assignment"
// var assignmentKeys = Object.keys(studentRecord).filter((assignment) => /Assignment/.test(assignment));
// get total grades (not averaged) just to see what you can do with filter
// Object.keys(studentRecord).filter((assignment) => averageGrade += (/Assignment/.test(assignment))?studentRecord[assignment]:0);
for (var column in studentRecord) {
if (/^Assignment/.test(column))
averageGrade += studentRecord[column];
numAssignments++;
}
averageGrade = Math.round(averageGrade / numAssignments);
// set the final column to the average grade for the randomly generated grades
studentRecord["Average (%)"] = averageGrade +"%";
}
}
//
// generate a table from the JavaScript (JSON) object
//
function createHTMLTableFromTableObject() {
// generate a new table using random data
myTable = []; genNewTable(myTable, 10);
// extract the column headers from the current data model (using first row of data model)
var colHeaders = Object.keys(myTable[0]);
// create the <table> object for inserting into DOM
var table = document.createElement("table");
table.setAttribute("class", "htmlTable");
// create the <table> header row first using extracted headers
var tr = table.insertRow(-1); // create the row (at end of table)
for (var i = 0; i < colHeaders.length; i++) {
var th = document.createElement("th"); // and the headers
th.setAttribute("class", "thCell thAlign"); // add the styles
th.innerHTML = colHeaders[i];
tr.appendChild(th);
}
// add the data from the table object as rows
for (var i = 0; i < myTable.length; i++) {
tr = table.insertRow(-1); // insert row at end of table
for (var j = 0; j < colHeaders.length; j++) {
var tabCell = tr.insertCell(-1); // insert at end of row
if (j<2) {
tabCell.setAttribute("class", "tdCell tdTxtAlign");
} else {
tabCell.setAttribute("class", "tdCell tdNumAlign");
tabCell.contentEditable = true;
}
tabCell.innerHTML = myTable[i][colHeaders[j]];
//console.log(table.rows[i+1].cells[j]); <---------- PERFECT REFERENCE
}
}
// now update the table view (container)
var viewContainer = document.getElementById("showTableData");
viewContainer.innerHTML = ""; // reset
viewContainer.appendChild(table); // add the table
}
function testRef()
{
var table = document.getElementById("showTableData");
console.log(table.rows[0].cells[1]);
}
</script>
</body>
</html>

This is because you are placing that generated table into the already existing table (with id: showTableData) and then you are calling that table.
it would return something like <table id="showTableData"><table class="htmlTable"></table></table>
and you want to address the second one or better yet don't place a table inside the other table :)
function testRef()
{
var table = document.querySelector(".htmlTable");
console.log(table.rows[0].cells[1]);
}
this is a quick fix (proof) but you should spawn that generated table into the container directly.
Edit 1: answering further question
in javascript change
table.setAttribute("class", "htmlTable");
to
table.setAttribute("id", "showTableData");
you can also keep both if you need that class name.
Next this line that currently addresses the first table
var viewContainer = document.getElementById("showTableData");
change to
var viewContainer = document.getElementById("showTableData_container");
and lastly in HTML
<table style="padding: 20px;" id="showTableData"></table>
to this
<div style="padding: 20px;" id="showTableData_container"></div>
you can now use the original test function
function testRef()
{
var table = document.getElementById("showTableData");
console.log(table.rows[0].cells[1]);
}

In your code yo wrote:
// create the <table> object for inserting into DOM
var table = document.createElement("table");
table.setAttribute("class", "htmlTable");
And this table is not the same to the one with id="showTableData"
To get the content of the first row of the table that you create with the function createHTMLTableFromTableObject:
function testRef(){
var table = document.querySelector(".htmlTable");
console.log(table.rows[0].cells[1]);
}

Related

Cell in table wont color on click (CodeNewbie)

I am new to JavaScript and I am trying to create a pixel generator, I think it is almost there but when I click the cells in the table they do not change color to the selected color that was chosen. Please can you help?
I tried to add the following in JavaScript but it did not work:
table.addEventListener("click", function (event) {
if (event.target.tagName === "td") {
event.target.style.backgroundColor = colorGrid;
}
}
I also added to the above suggested code by stating the table as a variable as I thought the table variable declared already may have been constrained to local scope, but that also did not work.
Thanks
// Select color input
var colorGrid = document.getElementById("colorPicker");
// Select size input
var sizeHeight = document.getElementById("inputHeight").value;
var sizeWidth = document.getElementById("inputWidth").value;
// When size is submitted by the user, call makeGrid()
const form = document.getElementById("sizePicker");
form.addEventListener("submit", makeGrid);
function makeGrid(event) {
// stop automatic reset of form
event.preventDefault();
// body reference - get first <body></body>
var body = document.getElementsByTagName("body")[0];
// Your code goes here!
var table = document.getElementById("pixelCanvas");
var tableBody = document.createElement("tbody");
// cells creation
for (let i = 1; i <= sizeHeight; i++) {
// create table row
var tableRow = document.createElement("tr");
for (let i = 1; i <= sizeWidth; i++) {
// create table cells (cols) within each row - loop within loop
var tableCell = document.createElement("td");
// append cell to the row
tableRow.appendChild(tableCell);
}
// add row to the table
table.appendChild(tableRow);
}
// append the table body inside the table
table.appendChild(tableBody);
}
body {
text-align: center;
}
h1 {
font-family: Monoton;
font-size: 70px;
margin: 0.2em;
}
h2 {
margin: 1em 0 0.25em;
}
h2:first-of-type {
margin-top: 0.5em;
}
table,
tr,
td {
border: 1px solid black;
}
table {
border-collapse: collapse;
margin: 0 auto;
}
tr {
height: 20px;
}
td {
width: 20px;
}
input[type=number] {
width: 6em;
}
<!DOCTYPE html>
<html>
<head>
<title>Pixel Art Maker!</title>
<link rel="stylesheet" href="https://fonts.googleapis.com/css?family=Monoton">
<link rel="stylesheet" href="styles.css">
</head>
<body>
<h1>Pixel Art Maker</h1>
<h2>Choose Grid Size</h2>
<form id="sizePicker">
Grid Height:
<input type="number" id="inputHeight" name="height" min="1" value="1">
Grid Width:
<input type="number" id="inputWidth" name="width" min="1" value="1">
<input type="submit">
</form>
<h2>Pick A Color</h2>
<input type="color" id="colorPicker">
<h2>Design Canvas</h2>
<table id="pixelCanvas"></table>
<script src="designs.js"></script>
</body>
</html>
There are a couple of issues with your code that I'll address first.
When the script first loads it's assigning the value of those inputs to a variable. At that point the value is an empty string so that won't work properly when you come to the iteration part of the script. You should change
var sizeHeight = document.getElementById("inputHeight").value;
to
var sizeHeight = document.getElementById("inputHeight");
and then get the value when you loop because at that point the inputs will be complete:
for (let i = 1; i <= sizeHeight.value; i++) {
Second, you create a tableBody element but you don't use it - you append the rows directly to the table element instead.
Finally, you should probably clear the table's HTML content on each submission so you remove the existing grid before adding a new one.
In terms of how you change the colour of the grid boxes there are a couple of routes you can take. 1) Add a listener to each box which calls a function when it is fired 2) Add one listener to the table to catch events from its child elements as they "bubble up" the DOM. This is known as event delegation and is what this example uses. When the listener catches an event it checks that the element that fired the event is a table cell, and then adjusts the cell's background colour.
const colorGrid = document.getElementById('colorPicker');
// Just assign the elements to the variables,
// not the values
const sizeHeight = document.getElementById('inputHeight');
const sizeWidth = document.getElementById('inputWidth');
const form = document.getElementById('sizePicker');
form.addEventListener('submit', makeGrid);
function makeGrid(event) {
event.preventDefault();
const table = document.getElementById('pixelCanvas');
const tableBody = document.createElement('tbody');
// Reset the table HTML
table.innerHTML = '';
// Add a listener to the table element which calls
// `handleClick`
table.addEventListener('click', handleClick);
// `handleClick` checks to see if the element that
// fired the event is a table cell, and then it
// sets its background color to the value of your
// `colorGrid` input
function handleClick(e) {
if (e.target.matches('td')) {
e.target.style.backgroundColor = colorGrid.value;
}
}
// Using the value of `sizeHeight`
for (let i = 1; i <= sizeHeight.value; i++) {
const tableRow = document.createElement('tr');
// Using the value of `sizeWidth`
for (let i = 1; i <= sizeWidth.value; i++) {
const tableCell = document.createElement('td');
tableRow.appendChild(tableCell);
}
// Appending to the tableBody element rather than
// the table element
tableBody.appendChild(tableRow);
}
table.appendChild(tableBody);
}
body{text-align:center}h1{font-family:Monoton;font-size:70px;margin:.2em}h2{margin:1em 0 .25em}h2:first-of-type{margin-top:.5em}table,td,tr{border:1px solid #000}table{border-collapse:collapse;margin:0 auto}tr{height:20px}td{width:20px}input[type=number]{width:6em}td:hover{cursor:pointer}
<form id="sizePicker">
Grid Height:
<input type="number" id="inputHeight" name="height" min="1" value="1"> Grid Width:
<input type="number" id="inputWidth" name="width" min="1" value="1">
<input type="submit">
</form>
<h2>Pick A Color</h2>
<input type="color" id="colorPicker">
<h2>Design Canvas</h2>
<table id="pixelCanvas"></table>
In your event listener, you are setting the background color to colorGrid, which is actually an element (input). You want the value of that input instead.
event.target.style.backgroundColor = colorGrid.value;
Add a onclick before appending the child:
for (let i = 1; i <= sizeWidth; i++) {
// create table cells (cols) within each row - loop within loop
var tableCell = document.createElement("td");
// add onclick
tableCell.onclick = (e) => {
e.target.style.background = colorGrid.value;
}
// append cell to the row
tableRow.appendChild(tableCell);
}
Updated demo:
// Select color input
var colorGrid = document.getElementById("colorPicker");
// Select size input
var sizeHeight = document.getElementById("inputHeight").value;
var sizeWidth = document.getElementById("inputWidth").value;
// When size is submitted by the user, call makeGrid()
const form = document.getElementById("sizePicker");
form.addEventListener("submit", makeGrid);
function makeGrid(event) {
// stop automatic reset of form
event.preventDefault();
// body reference - get first <body></body>
var body = document.getElementsByTagName("body")[0];
// Your code goes here!
var table = document.getElementById("pixelCanvas");
var tableBody = document.createElement("tbody");
// cells creation
for (let i = 1; i <= sizeHeight; i++) {
// create table row
var tableRow = document.createElement("tr");
for (let i = 1; i <= sizeWidth; i++) {
// create table cells (cols) within each row - loop within loop
var tableCell = document.createElement("td");
// add onclick
tableCell.onclick = (e) => {
e.target.style.background = colorGrid.value;
}
// append cell to the row
tableRow.appendChild(tableCell);
}
// add row to the table
table.appendChild(tableRow);
}
// append the table body inside the table
table.appendChild(tableBody);
}
body {
text-align: center;
}
h1 {
font-family: Monoton;
font-size: 70px;
margin: 0.2em;
}
h2 {
margin: 1em 0 0.25em;
}
h2:first-of-type {
margin-top: 0.5em;
}
table,
tr,
td {
border: 1px solid black;
}
table {
border-collapse: collapse;
margin: 0 auto;
}
tr {
height: 20px;
}
td {
width: 20px;
}
input[type=number] {
width: 6em;
}
<!DOCTYPE html>
<html>
<head>
<title>Pixel Art Maker!</title>
<link rel="stylesheet" href="https://fonts.googleapis.com/css?family=Monoton">
<link rel="stylesheet" href="styles.css">
</head>
<body>
<h1>Pixel Art Maker</h1>
<h2>Choose Grid Size</h2>
<form id="sizePicker">
Grid Height:
<input type="number" id="inputHeight" name="height" min="1" value="1">
Grid Width:
<input type="number" id="inputWidth" name="width" min="1" value="1">
<input type="submit">
</form>
<h2>Pick A Color</h2>
<input type="color" id="colorPicker">
<h2>Design Canvas</h2>
<table id="pixelCanvas"></table>
<script src="designs.js"></script>
</body>
</html>

Create table rows with Javascript

I'm new to Javascript and learn about querySelector(), createElement(), setAttribute and loop.
I try to create a table with a row, which contains 5 cells in it.
Imagine I create in HTML file. I use following code to execute my idea:
// 1. select id = pixelCanvas and store in var table.
const table = document.querySelector('#pixelCanvas');
// 2. create table row element <tr></tr> and append to var table.
table.appendChild(document.createElement('tr'));
// 3. create element table data and store in var data.
const data = document.createElement('td');
// 4. set html attribute with class="cell", width: 20px & height: 20px for table data
data.setAttribute('style', 'width: 20px; height: 20px:');
// 5. use for loop and appendChild() to append 5 <td></td> to <tr></tr>
for (i = 0; i < 5; i++) {
let tblRow = document.querySelector('tr');
tblRow.appendChild(data);
};
Here is the result in HTML page.
Obviously my idea doesn't work as expected. Appreciate your advice: where goes wrong?
prefer to use css to style attributes.
there is also js methods for tables :
HTMLTableElement.insertRow()
HTMLTableRowElement.insertCell()
const myTable = document.querySelector('table#pixelCanvas')
for (let r=0;r<5;++r) {
let row = myTable.insertRow()
for (c=0;c<5;++c) {
row.insertCell().textContent = `${r}.${c}`
}
}
table#pixelCanvas {
border-collapse: collapse;
font-size: 9px;
}
table#pixelCanvas td {
padding: 5px;
width: 20px;
height: 20px;
border: 1px solid black;
}
<table id="pixelCanvas"></table>
Your code is very close to working - you just need to create a seperate td element for each iteration of the for loop, and setting the style attribute is much easier when done via the .style property of the element.
Also instead of trying to query for the row you have just created, you can use the return value of the createElement function - it will give you the variable containing the new element.
See this snippet:
const table = document.querySelector('#pixelCanvas');
const colors = ["red", "orange", "green", "blue", "purple"];
const row = document.createElement('tr');
table.appendChild(row);
for (i = 0; i < 5; i++) {
const cell = document.createElement('td');
cell.style.width = "20px";
cell.style.height = "20px";
cell.style.backgroundColor = colors[i];
row.appendChild(cell);
};
<table id="pixelCanvas"></table>
You mean this
Move the row outside the loop and if you need the cells to be the same, clone the one you made - you also had a typo in it (colon instead of semicolon)
I would also target the tbody since a table should have such a thing
const table = document.querySelector('#pixelCanvas tbody');
const data = document.createElement('td');
data.setAttribute('style', 'width: 20px; height: 20px;');
let tblRow = document.createElement('tr');
for (let i = 0; i < 5; i++) {
let cell = data.cloneNode(true)
cell.textContent = i;
tblRow.appendChild(cell);
}
table.appendChild(tblRow);
<table id="pixelCanvas">
<thead>
</thead>
<tbody>
</tbody>
</table>
Result
<table id="pixelCanvas">
<thead>
</thead>
<tbody>
<tr>
<td style="width: 20px; height: 20px;">0</td>
<td style="width: 20px; height: 20px;">1</td>
<td style="width: 20px; height: 20px;">2</td>
<td style="width: 20px; height: 20px;">3</td>
<td style="width: 20px; height: 20px;">4</td>
</tr>
</tbody>
</table>

Why do my HTML table columns resize when I dynamically add a row containing a div?

I am trying to add a table row containing a div dynamically using JavaScript. Everything is working fine except, when it is added, my columns move slightly. Weirdly, it seems to only happen when the div contains text longer than a certain length.
In the stripped down version below, you can see the problem clearly. After trying it, go to line 24 of the JavaScript, remove the "i" at the end of the string, and it will no longer move my columns.
JSFiddle
setEventListeners();
function setEventListeners() {
var hideMe = document.getElementById('hide-me');
var table = document.getElementById('table');
hideMe.addEventListener('mouseenter', showHoverMenu);
table.addEventListener('mouseleave', deleteOtherMenus);
}
function showHoverMenu(e) {
e.preventDefault();
deleteOtherMenus();
var tr = document.createElement('tr');
tr.setAttribute('class', 'row-menu-parent')
var td = document.createElement('td');
td.colSpan = 4;
var rowMenu = document.createElement('div');
rowMenu.classList.add('row-menu');
var div = document.createElement('div');
// Delete the "i" at the end of the string and try hovering again
div.innerHTML = 'abcdefghi';
rowMenu.appendChild(div);
td.appendChild(rowMenu);
tr.appendChild(td);
var target = e.currentTarget;
target.parentNode.insertBefore(tr, target.nextSibling);
}
function deleteOtherMenus() {
var rowMenu = document.getElementsByClassName('row-menu-parent');
if (rowMenu.length > 0) {
rowMenu[0].parentNode.removeChild(rowMenu[0]);
}
}
* {
border: 1px solid red;
}
table {
width: 100%;
}
.row-menu div {
background-color: lightGrey;
}
<table id="table">
<tr id="hide-me">
<td>A</td>
<td>B</td>
<td>C</td>
<td>D</td>
</tr>
</table>
What is causing my columns to move, and how do I fix it?
EDIT: The columns need to be able to automatically resize, so a fixed table layout will not work.
You can set your table to table-layout: fixed; if you don't want it resizing.
table {
table-layout: fixed; /* Add this */
width: 100%;
}
setEventListeners();
function setEventListeners() {
var hideMe = document.getElementById('hide-me');
var table = document.getElementById('table');
hideMe.addEventListener('mouseenter', showHoverMenu);
table.addEventListener('mouseleave', deleteOtherMenus);
}
function showHoverMenu(e) {
e.preventDefault();
deleteOtherMenus();
var tr = document.createElement('tr');
tr.setAttribute('class', 'row-menu-parent')
var td = document.createElement('td');
td.colSpan = 4;
var rowMenu = document.createElement('div');
rowMenu.classList.add('row-menu');
var div = document.createElement('div');
// Delete the "i" at the end of the string and try hovering again
div.innerHTML = 'abcdefghi';
rowMenu.appendChild(div);
td.appendChild(rowMenu);
tr.appendChild(td);
var target = e.currentTarget;
target.parentNode.insertBefore(tr, target.nextSibling);
}
function deleteOtherMenus() {
var rowMenu = document.getElementsByClassName('row-menu-parent');
if (rowMenu.length > 0) {
rowMenu[0].parentNode.removeChild(rowMenu[0]);
}
}
* {
border: 1px solid red;
}
table {
table-layout: fixed;
width: 100%;
}
.row-menu div {
background-color: lightGrey;
}
<table id="table">
<tr id="hide-me">
<td>A</td>
<td>B</td>
<td>C</td>
<td>D</td>
</tr>
</table>
Try setting box-sizing: border-box in CSS for the table and/or wrap the .row-menu div in a div that does not have any styling.
If you look closely, the inner div's border is expanding the size of the parent TD which is causing the table to re-adjust.

Electron's localstorage not saving my on-click events on dynamically created elements

I am creating a desktop application using Electron and I'm running into an issue regarding the local storage of on click events.
In my application, the user enters 5 groups of information (publication name, publisher name, status, submission date, response date)
the information is then placed into an HTML table through the D.O.M, where it is separated into rows by entry and columns by information category with Remove and Edit buttons on the far right columns.
e.g.
|name1|publisher1|awaiting|noDate|NoDate2|[RemoveButton][EditButton]|
|name2|publisher2|denied|noDate3|NoDate4|[RemoveButton]|[EditButton]|
|name3|publisher3|accepted|noDate5|NoDate6|[RemoveButton]|[EditButton]|
and so on, as the user enters more entries, the more rows are appended to the table. Each row that is appended, however, always has the 2 remove and edit buttons on the right. I have assigned a function to these 2 buttons like this:
The Remove Button:
editButton.addEventListener("click", function() {
if (confirm("Are you sure you want to delete this item?")) {
var td = event.target.parentNode;
var tr = td.parentNode;
tr.parentNode.removeChild(tr);
localStorage.setItem("TableStorage", table.innerHTML);
} else {}
};
The Edit Button:
editButton.addEventListener("click", function() {
if (confirm("Are you sure you want to edit this item?")) {
var publicationName = document.getElementById("publicationNameTextField");
var publisherName = document.getElementById("publisherNameTextField");
var publicationStatus = document.getElementById(
"publicationStatusTextField"
);
var publicationDateSubmitted = document.getElementById(
"publicationDateSubmittedTextField"
);
var publicationDateResponse = document.getElementById(
"publicationDateResponseTextField"
);
var table = document.getElementById("myTable");
var td = event.target.parentNode;
var tr = td.parentNode;
rowIndex = tr.rowIndex;
var cell1 = table.rows[rowIndex].cells[0].innerHTML;
var cell2 = table.rows[rowIndex].cells[1].innerHTML;
var cell3 = table.rows[rowIndex].cells[2].innerHTML;
var cell4 = table.rows[rowIndex].cells[3].innerHTML;
var cell5 = table.rows[rowIndex].cells[4].innerHTML;
publicationName.value = cell1;
publisherName.value = cell2;
publicationStatus.value = cell3;
publicationDateSubmitted.value = cell4;
publicationDateResponse.value = cell5;
publicationName.focus();
localStorage.setItem("TableStorage", table.innerHTML);
tr.parentNode.removeChild(tr);
} else {}
};
(To elaborate further on the edit button, It deletes the row that the button is in and re enters all the column entries publication name, status, etc. back into the textfields to be revised and resubmitted.)
(Formatting of code may not be exact, as I am not used to formatting it on here)
These buttons do their job during an active session (when the submissions are added as the application is running) but when I close the application and reopen it and/or force reload it, the buttons seem to lose their functionality. I believe this to be some sort of issue with how I'm storing my local storage.
What I do find strange, however, I have a "clear all button" that essentially clears all the content in the table. This button is formatted in the same way the other buttons are but it is not dynamically generated like them. Because of this, it leads me to believe there is a problem in how my functions are being declared.
var clearBtn = document.getElementById("clearAllButton");
clearBtn.addEventListener("click", function() {
if (confirm("Are you sure you want to clear your Publications?")) {
for (var i = 1; i < table.rows.length; ) {
table.deleteRow(i);
localStorage.setItem("TableStorage", table.innerHTML);
}
} else {
}
});
Here is the full code snippet:
// ALL javascript code that interacts with the page is used here.
// This code is required in the HTML by a require('renderer.js) and thus runs from the page.
// The DOM can be accessed from this file.
// Declare variables from document
//var publicationName = document.getElementById('publicationNameTextField');
//var publisherName = document.getElementById('publisherNameTextField');
//var publicationStatus = document.getElementById('publicationStatusTextField');
//var publicationDateSubmitted = document.getElementById('publicationDateSubmittedTextField');
//var publicationDateResponse = document.getElementById('publicationDateResponseTextField');
"use strict";
// Save Data on close
window.onclose = function() {
localStorage.setItem("TableStorage", table.innerHTML);
};
// Save data failsafe in event of crash
window.onabort = function() {
localStorage.setItem("TableStorage", table.innerHTML);
};
// Declare submit button
var submitBtn = document.getElementById("submitButton");
// Declare table data will be displayed and saved to
var table = document.getElementById("myTable");
// Get value of textfields from DOM
var publicationName = document.getElementById("publicationNameTextField").value;
var publisherName = document.getElementById("publisherNameTextField").value;
var publicationStatus = document.getElementById("publicationStatusTextField")
.value;
var publicationDateSubmitted = document.getElementById(
"publicationDateSubmittedTextField"
).value;
var publicationDateResponse = document.getElementById(
"publicationDateResponseTextField"
).value;
// Create a event listener for when the add button is pressed.
submitBtn.addEventListener("click", function(o) {
// Re declare variables for scope
var table = document.getElementById("myTable");
var publicationName = document.getElementById("publicationNameTextField")
.value;
var publisherName = document.getElementById("publisherNameTextField").value;
var publicationStatus = document.getElementById("publicationStatusTextField")
.value;
var publicationDateSubmitted = document.getElementById(
"publicationDateSubmittedTextField"
).value;
var publicationDateResponse = document.getElementById(
"publicationDateResponseTextField"
).value;
// Save values entered as possible option entries for later submissions.
var publicationNameDataList = document.getElementById(
"publicationNameDataList"
);
var publicationNameOption = document.createElement("option");
publicationNameOption.innerHTML = publicationName;
publicationNameDataList.appendChild(publicationNameOption);
var publisherNameDataList = document.getElementById("publisherNameDataList");
var publisherNameOption = document.createElement("option");
publisherNameOption.innerHTML = publisherName;
publisherNameDataList.appendChild(publisherNameOption);
var publicationDateSubmittedDataList = document.getElementById(
"publicationDateSubmittedDataList"
);
var publicationDateSubmittedOption = document.createElement("option");
publicationDateSubmittedOption.innerHTML = publicationDateSubmitted;
publicationDateSubmittedDataList.appendChild(publicationDateSubmittedOption);
var publicationDateResponseDataList = document.getElementById(
"publicationDateResponseDataList"
);
var publicationDateResponseOption = document.createElement("option");
publicationDateResponseOption.innerHTML = publicationDateResponse;
publicationDateResponseDataList.appendChild(publicationDateResponseOption);
// Add a button to remove the particular row on each row
var deleteButton = document.createElement("img");
deleteButton.className = "deleteButton";
deleteButton.innerHTML = "Remove";
deleteButton.addEventListener("click", function() {
if (confirm("Are you sure you want to delete this item?")) {
var td = event.target.parentNode;
var tr = td.parentNode;
tr.parentNode.removeChild(tr);
localStorage.setItem("TableStorage", table.innerHTML);
} else {}
});
deleteButton.src = "assets/img/icons8-cancel-48.png";
var editButton = document.createElement("img");
editButton.className = "editButton";
editButton.innerHTML = "Edit";
editButton.addEventListener("click", function() {
if (confirm("Are you sure you want to edit this item?")) {
var publicationName = document.getElementById("publicationNameTextField");
var publisherName = document.getElementById("publisherNameTextField");
var publicationStatus = document.getElementById(
"publicationStatusTextField"
);
var publicationDateSubmitted = document.getElementById(
"publicationDateSubmittedTextField"
);
var publicationDateResponse = document.getElementById(
"publicationDateResponseTextField"
);
var table = document.getElementById("myTable");
var td = event.target.parentNode;
var tr = td.parentNode;
rowIndex = tr.rowIndex;
var cell1 = table.rows[rowIndex].cells[0].innerHTML;
var cell2 = table.rows[rowIndex].cells[1].innerHTML;
var cell3 = table.rows[rowIndex].cells[2].innerHTML;
var cell4 = table.rows[rowIndex].cells[3].innerHTML;
var cell5 = table.rows[rowIndex].cells[4].innerHTML;
publicationName.value = cell1;
publisherName.value = cell2;
publicationStatus.value = cell3;
publicationDateSubmitted.value = cell4;
publicationDateResponse.value = cell5;
publicationName.focus();
localStorage.setItem("TableStorage", table.innerHTML);
tr.parentNode.removeChild(tr);
} else {}
});
editButton.src = "assets/img/icons8-edit-file-40.png";
// Create cells for each row
var row = table.insertRow(1);
var cell1 = row.insertCell(0);
var cell2 = row.insertCell(1);
var cell3 = row.insertCell(2);
var cell4 = row.insertCell(3);
var cell5 = row.insertCell(4);
var cell6 = row.insertCell(5);
var cell7 = row.insertCell(6);
// Give each cell of the row content from the textfields
cell1.innerHTML = publicationName;
cell2.innerHTML = publisherName;
cell3.innerHTML = publicationStatus;
cell4.innerHTML = publicationDateSubmitted;
cell5.innerHTML = publicationDateResponse;
cell6.appendChild(deleteButton);
cell7.appendChild(editButton);
// Save data to localstorage
localStorage.setItem("TableStorage", table.innerHTML);
// Clear text fields for next entry
document.getElementById("publicationNameTextField").value = "";
document.getElementById("publisherNameTextField").value = "";
document.getElementById("publicationStatusTextField").value = "";
document.getElementById("publicationDateSubmittedTextField").value = "";
document.getElementById("publicationDateResponseTextField").value = "";
});
// Load previous data upon application loading
window.onload = function() {
var saved = localStorage.getItem("TableStorage");
// If there are any saved items, update our list
if (saved) {
table.innerHTML = saved;
}
};
// Clear Button to clear all items of the table.
var clearBtn = document.getElementById("clearAllButton");
clearBtn.addEventListener("click", function() {
if (confirm("Are you sure you want to clear your Publications?")) {
for (var i = 1; i < table.rows.length; ) {
table.deleteRow(i);
localStorage.setItem("TableStorage", table.innerHTML);
}
} else {
}
});
body {
font-family: Arial, Helvetica, sans-serif;
}
* {
box-sizing: border-box;
}
#form {
text-align: center;
}
.input-container {
display: -ms-flexbox;
/* IE10 */
display: flex;
width: 100%;
margin-bottom: 15px;
text-align: left;
}
.blueB {
padding: 10px;
background: dodgerblue;
color: white;
min-width: 50px;
text-align: center;
}
.input-field {
width: 100%;
padding: 10px;
outline: none;
}
.input-field:focus {
border: 2px solid dodgerblue;
}
/* Set a style for the submit button */
.btn {
background-color: dodgerblue;
color: white;
padding: 15px 20px;
border: none;
cursor: pointer;
width: 50%;
opacity: 0.9;
}
.btn:hover {
opacity: 1;
}
img:hover {
transform: scale(1.1);
cursor: pointer;
}
/* Style the main content */
.main {
/* Same as the width of the sidenav */
padding: 0px 10px;
}
/* Add media queries for small screens (when the height of the screen is less than 450px, add a smaller padding and font-size) */
#media screen and (max-height: 450px) {
.sidebar {
padding-top: 15px;
}
.sidebar a {
font-size: 18px;
}
}
p {
color: white;
}
h1 {
color: #3500D3;
}
h2 {
color: white;
}
#myInput {
background-image: url('../assets/img/icons8-search-32.png');
/* Add a search icon to input */
background-position: 10px 10px;
/* Position the search icon */
background-repeat: no-repeat;
/* Do not repeat the icon image */
width: 100%;
/* Full-width */
font-size: 16px;
/* Increase font-size */
padding: 12px 20px 12px 40px;
/* Add some padding */
border: 1px solid #3500D3;
/* Add a grey border */
margin-bottom: 12px;
/* Add some space below the input */
}
.PublicationShow {
height: 50%;
width: 100%;
overflow: auto;
}
#myTable {
border-collapse: collapse;
/* Collapse borders */
width: 100%;
/* Full-width */
border: 1px solid #3500D3;
/* Add a grey border */
font-size: 18px;
height: 50%;
overflow-y: auto;
/* Increase font-size */
text-align: center;
}
#myTable th,
#myTable td {
text-align: center;
/* Left-align text */
padding: 12px;
/* Add padding */
}
#myTable tr {
/* Add a bottom border to all table rows */
border-bottom: 1px solid #ddd;
}
#myTable tr.header,
#myTable tr:hover {
/* Add a grey background color to the table header and on hover */
background-color: #3500D3;
}
td {
color: white;
}
#clearItemText {
display: inline;
text-align: center;
top: 50%;
}
.deleteButton {
border-radius: 50%;
}
.editButton {
border-radius: 50%;
}
.trashHover {
background-color: none !important;
color: white;
font-size: 35px;
}
.trashHover:hover {
transform: scale(1.1);
cursor: pointer;
color: #fb0253;
}
<!DOCTYPE html>
<html style="width: 100%; height: 90%; background-color: #190061">
<head>
<meta charset="UTF-8" />
<title>Easy Track | Publication Manager</title>
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0/css/bootstrap.min.css" integrity="sha384-Gn5384xqQ1aoWXA+058RXPxPg6fy4IWvTNh0E263XmFcJlSAwiGgFAW/dAiS6JXm"
crossorigin="anonymous" />
<link rel="stylesheet" href="https://use.fontawesome.com/releases/v5.5.0/css/all.css" integrity="sha384-B4dIYHKNBt8Bc12p+WXckhzcICo0wtJAoU8YZTY5qE0Id1GSseTk6S+L3BlXeVIU"
crossorigin="anonymous" />
<link rel="stylesheet" href="css/index.css" />
</head>
<body style="width: 100%; height: 90%; background-color: #190061">
<!-- All of the Node.js APIs are available in this renderer process. -->
<div class="main">
<h1>Easy Track | Publication Manager</h1>
<br />
<p>Manage your publications and submissions with ease!</p>
<br />
<div id="form" style="margin:auto">
<h2>Add Publication</h2>
<div class="input-container">
<i class="blueB"></i>
<input id="publicationNameTextField" class="input-field" type="text" placeholder="Publication Name" name="publicationName"
list="publicationNameDataList" />
<datalist id="publicationNameDataList"> </datalist>
</div>
<div class="input-container">
<i class="blueB"></i>
<input id="publisherNameTextField" class="input-field" type="text" placeholder="Publisher Name" name="publisherName"
list="publisherNameDataList" />
<datalist id="publisherNameDataList"> </datalist>
</div>
<div class="input-container">
<i class="blueB"></i>
<input id="publicationStatusTextField" class="input-field" type="text" placeholder="Status" name="publicationStatus"
list="statusList" />
<datalist id="statusList">
<option>Awaiting</option>
<option>Denied</option>
<option>Accepted</option>
</datalist>
</div>
<div class="input-container">
<i class="blueB"></i>
<input id="publicationDateSubmittedTextField" class="input-field" type="text" placeholder="Date Submitted" name="publicationDateSubmitted"
list="publicationDateSubmittedDataList" />
<datalist id="publicationDateSubmittedDataList"> </datalist>
</div>
<div class="input-container">
<i class="blueB"></i>
<input id="publicationDateResponseTextField" class="input-field" type="text" placeholder="Date of Response"
list="publicationDateResponseDataList" name="publicationDateResponse" />
<datalist id="publicationDateResponseDataList"> </datalist>
</div>
<button id="submitButton" class="btn">Submit</button> <br />
</div>
<br />
<br />
<div class="PublicationsShow">
<input type="text" id="myInput" onkeyup="searchBar()" placeholder="Search" />
<br />
<i id="clearAllButton" class="fas fa-trash-alt trashHover"></i>
<p id="clearItemText">Clear All Items</p>
<table id="myTable">
<tr class=" header">
<th style="width:15%; color: white !important;">
Publication name
</th>
<th style="width:15%; color: white !important;">Publisher Name</th>
<th style="width:15%; color: white !important;">Status</th>
<th style="width:15%; color: white !important;">Date Submitted</th>
<th style="width:15%; color: white !important;">
Date of Response
</th>
<th style="width:15%; color: white !important;">Change</th>
</tr>
</table>
</div>
</div>
<script>
// You can also require other files to run in this process
require("./js/renderer.js");
function filterTable(event) {
var filter = event.target.value.toUpperCase();
var rows = document.querySelector("#myTable tbody").rows;
for (var i = 1; i < rows.length; i++) {
var firstCol = rows[i].cells[0].textContent.toUpperCase();
var secondCol = rows[i].cells[1].textContent.toUpperCase();
var thirdCol = rows[i].cells[2].textContent.toUpperCase();
var fourthCol = rows[i].cells[3].textContent.toUpperCase();
var fifthCol = rows[i].cells[4].textContent.toUpperCase();
if (
firstCol.indexOf(filter) > -1 ||
secondCol.indexOf(filter) > -1 ||
thirdCol.indexOf(filter) > -1 ||
fourthCol.indexOf(filter) > -1 ||
fifthCol.indexOf(filter) > -1
) {
rows[i].style.display = "";
} else {
rows[i].style.display = "none";
}
}
}
document
.querySelector("#myInput")
.addEventListener("keyup", filterTable, false);
</script>
</body>
</html>
The snippet is not entirely accurate as my application is run through Node and Electron.
If I haven't made things clear enough, feel free to ask me questions to clear up confusion.
If you have any solutions to this, or tips for my bad code and practices I appreciate any feedback.
Thanks.
After further examination, I have found the problem. The on click events on the buttons were being assigned in the session but the local storage could not store the events because they weren't strings.
To solve this:
Leave your on click events on your add button so that they function during the session, but add a for loop on the page load for the buttons you wish to use. Make sure you assign a class for your element during the button click to reference it on the page load.
e.g.
window.onload = function() {
var savedInner = localStorage.getItem("listStorageInner");
var savedOuter = localStorage.getItem("listStorageOuter");
// If there are any saved items, update our list
if (savedInner) {
let targetList = document.getElementById("projectList");
targetList.innerHTML = savedInner;
}
if (savedOuter) {
let targetList = document.getElementById("projectList");
targetList.outerHTML = savedOuter;
}
var removeButtons = document.getElementsByClassName("removeMeBtn");
for (let i = 0; i < removeButtons.length; i++) {
removeButtons[i].addEventListener(
"click",
(function() {
return function() {
let parent = this.parentElement;
parent.parentElement.removeChild(parent);
let targetList = document.getElementById("projectList");
localStorage.removeItem("listStorageInner", targetList.innerHTML);
localStorage.removeItem("listStorageOuter", targetList.outerHTML);
localStorage.setItem("listStorageInner", targetList.innerHTML);
localStorage.setItem("listStorageOuter", targetList.outerHTML);
};
})(i),
false
);
}
var editBtns = document.getElementsByClassName("editMeBtn");
for (let j = 0; j < editBtns.length; j++) {
editBtns[j].addEventListener(
"click",
(function() {
return function() {
let parent = this.parentElement;
document.getElementById("entryInput").value = parent.textContent;
parent.focus();
parent.parentElement.removeChild(parent);
let targetList = document.getElementById("projectList");
localStorage.removeItem("listStorageInner", targetList.innerHTML);
localStorage.removeItem("listStorageOuter", targetList.outerHTML);
localStorage.setItem("listStorageInner", targetList.innerHTML);
localStorage.setItem("listStorageOuter", targetList.outerHTML);
};
})(j),
false
);
}
};
The for loop goes through all elements that have the class names of the buttons, then assigns the onclick event on the page load. That way the button click events always work and save properly through local storage. Also, make sure to watch the scope when it comes to using local storage.
Hope this helps if someone else encounters this issue.

Clicking individual Cells and adding color to HTML table using Javascript

I have created a HTML table (with user inputted cols and rows) and also have a dynamic way of selecting color.
Now I want to be able to click on individual cells in the table and color them with chosen color. I have this code so far.
My final goal is to be able to reset the colors when I hit "submit" again. Flow would be:
Choose table size
Choose color
Color the cells in the table
Reset the table upon hitting "submit" again
function makeGrid(ev) {
ev.preventDefault();
var heights = document.getElementById("inputHeight").value;
var widths = document.getElementById("inputWidth").value;
var body = document.getElementById("pixelCanvas");
var table = document.createElement('TABLE')
var tblB = document.createElement('TBODY');
table.appendChild(tblB);
for (var i=0; i<heights; i++){
var tr = document.createElement('TR');
table.appendChild(tr);
for (var j=0; j<widths; j++){
var td = document.createElement('TD')
document.getElementById("pixelCanvas").onclick = function(){
td = document.getElementById("colorPicker").value;
alert(td);
}
table.appendChild(td);
}
}
body.append(table);
body.addEventListener('click', function(){
var coloor = document.getElementById("colorPicker").value;
body.style.backgroundColor = coloor;
})
}
body {
text-align: center;
}
h1 {
font-family: Monoton;
font-size: 70px;
margin: 0.2em;
}
h2 {
margin: 1em 0 0.25em;
}
h2:first-of-type {
margin-top: 0.5em;
}
table,
tr,
td {
border: 1px solid black;
padding: 25px;
}
table {
border-collapse: collapse;
margin: 0 auto;
}
input[type=number] {
width: 6em;
}
<!DOCTYPE html>
<html>
<head>
<title>Pixel Art Maker!</title>
<link rel="stylesheet" href="https://fonts.googleapis.com/css?family=Monoton">
<link rel="stylesheet" href="styles.css">
</head>
<body>
<h1>Pixel Art Maker</h1>
<h2>Choose Grid Size</h2>
<form id="sizePicker" onsubmit="makeGrid(event)">
Grid Height:
<input type="number" id="inputHeight" name="height" min="1" value="1">
Grid Width:
<input type="number" id="inputWidth" name="width" min="1" value="1">
<input type="submit" value= "submit">
</form>
<h2>Pick A Color</h2>
<input type="color" id="colorPicker">
<h2>Design Canvas</h2>
<table id="pixelCanvas"></table>
<script src="designs.js"></script>
</body>
</html>
Almost, only a few changes:
click events on the incorrect elements; only tds require event.
appending td to the wrong element. (tds should only be apart of trs.)
the color-picker's value should be assigned to the element's style attribute via HTMLElement.prototype.style (note: the css property name is normalized [camel-cased]).
We should not append a table to a table; consider making pixelCanvas a div.
Notice this.style... is not td.style...; In the event handler, this refers to the target element. You could have used td.style..., if you used let keyword to declare td, but you used the keyword var: learn more about scope here.
Clearing the table
Clearing the table is straight-forward: delete the elements in pixelCanvas (reset pixelCanvas to its original state). This is done in two lines:
//reset pixelCanvas
while (body.firstChild)
body.removeChild(body.firstChild);
If you will not add more children to pixelCanvas, you can change while to if.
All together:
function makeGrid(ev) {
ev.preventDefault();
//keep like-statements together
var rows = document.getElementById("inputHeight").value;
var cols = document.getElementById("inputWidth").value;
var table = document.createElement('TABLE');
var body = document.getElementById("pixelCanvas");
//reset pixelCanvas
while (body.firstChild)
body.removeChild(body.firstChild);
for (var i=0; i<rows; i++){
var tr = document.createElement('TR');
for (var j=0; j<cols; j++) {
var td = document.createElement('td');
td.onclick = function() {
this.style.backgroundColor = document.getElementById("colorPicker").value;
};
tr.appendChild(td);
}
table.appendChild(tr);
}
body.append(table);
}
body {
text-align: center;
}
h1 {
font-family: Monoton;
font-size: 70px;
margin: 0.2em;
}
h2 {
margin: 1em 0 0.25em;
}
h2:first-of-type {
margin-top: 0.5em;
}
table,
tr,
td {
border: 1px solid black;
padding: 25px;
}
table {
border-collapse: collapse;
margin: 0 auto;
}
input[type=number] {
width: 6em;
}
<!DOCTYPE html>
<html>
<head>
<title>Pixel Art Maker!</title>
<link rel="stylesheet" href="https://fonts.googleapis.com/css?family=Monoton">
<link rel="stylesheet" href="styles.css">
</head>
<body>
<h1>Pixel Art Maker</h1>
<h2>Choose Grid Size</h2>
<form id="sizePicker" onsubmit="makeGrid(event)">
Grid Height:
<input type="number" id="inputHeight" name="height" min="1" value="1">
Grid Width:
<input type="number" id="inputWidth" name="width" min="1" value="1">
<input type="submit" value= "submit">
</form>
<h2>Pick A Color</h2>
<input type="color" id="colorPicker">
<h2>Design Canvas</h2>
<table id="pixelCanvas"></table>
<script src="designs.js"></script>
</body>
</html>
Edit
Added the reset process. Also replaced the inline attribute event (onsubmit) on the form with an event listener.
Don't waste resources on assigning event listeners to a ton of <td>s, use Event Delegation to use the <table> to listen for all of the <td> instead. Details on implementing the pattern is commented in demo as well as alternative methods to use that are more specialized, less verbose, and more efficient.
Demo
Details commented in demo
/*
Register the first (and only) form to submit event
Look for a node with class .destroy and if present remove it from
DOM.
Call makeGrid()
*/
document.forms[0].addEventListener('submit', function(e) {
const destroy = document.querySelector('.destroy');
if (destroy) {
destroy.parentNode.removeChild(destroy);
}
makeGrid(e);
});
function makeGrid(ev) {
ev.preventDefault();
/*
Since there's a form with multiple form controls we are using
HTMLFormControlsCollection API.
Just reference the forms HTMLCollection once...
*/
var ui = document.forms[0].elements;
/*
...and then use that reference any and every form control
nested within that referenced form. (colorPicker was moved into
the form)
*/
var rowsQty = ui.inputHeight.value;
var cellsQty = ui.inputWidth.value;
var cellColor = ui.colorPicker.value;
var body = document.getElementById("pixelCanvas");
var table = document.createElement('TABLE');
/*
There's 2 loops:
1. first loop: the insertRow() method is used once on each loop.
insertRow() advantage is that it creates and appends with one
call.
*/
for (let r = 0; r < rowsQty; r++) {
var rowObj = table.insertRow();
/*
2. second loop: the insertCell() method is used as many times
as the submited number (cellsQty). insertCell() also
creates and appends in one call as well.
*/
for (let c = 0; c < cellsQty; c++) {
var cellObj = rowObj.insertCell();
}
}
/*
We will use Event Delegation so that we only need to register
the parent node (table) to listen for an event not only for
itself but for all of the nodes nested within it. BTW, this
works perfectly for dynamically created nodes when the number
of nodes is unknown....
*/
// Here we are registering table to listen for clicks...
table.addEventListener('click', function(e) {
// Reference the origin of event (clicked td)
var tgt = e.target;
// Reference the node registered to the event (table)
var cur = e.currentTarget;
// if the clicked node IS NOT the table...
if (tgt !== cur) {
// ...change its background to whatever value colorPicker has
tgt.style.background = cellColor;
}
});
// Mark table for reset
table.classList.add('destroy');
// Add completed table to DOM
body.appendChild(table);
}
body {
text-align: center;
}
h1 {
font-family: Monoton;
font-size: 70px;
margin: 0.2em;
}
h2 {
margin: 1em 0 0.25em;
}
h2:first-of-type {
margin-top: 0.5em;
}
table,
tr,
td {
border: 1px solid black;
padding: 25px;
}
table {
border-collapse: collapse;
margin: 0 auto;
}
input[type=number] {
width: 6em;
}
<!DOCTYPE html>
<html>
<head>
<title>Pixel Art Maker!</title>
<link rel="stylesheet" href="https://fonts.googleapis.com/css?family=Monoton">
<link rel="stylesheet" href="styles.css">
</head>
<body>
<h1>Pixel Art Maker</h1>
<h2>Choose Grid Size</h2>
<form id="sizePicker">
Grid Height:
<input type="number" id="inputHeight" name="height" min="1" value="1"> Grid Width:
<input type="number" id="inputWidth" name="width" min="1" value="1">
<input type="submit" value="submit">
<h2>Pick A Color</h2>
<input type="color" id="colorPicker">
</form>
<h2>Design Canvas</h2>
<table id="pixelCanvas"></table>
<script src="designs.js"></script>
</body>
</html>

Categories

Resources