I'm working on creating a table of cells that will change color when each cell is clicked on.
I've created the table and have unique Ids for 'td' element - how do I return the Id of the cell that was clicked so that I can change its background color?
Is it possible to do this with pure JS?
const grid = function makeGrid() {
var body = document.getElementById('pixelCanvas');
var tbl = document.createElement('table');
var tblBody = document.createElement('tbody');
for (let i = 0; i < 5; i++) {
let row = document.createElement('tr');
for (j = 0; j < 5; j++) {
var cell = document.createElement('td')
row.appendChild(cell)
cell.setAttribute('id', 'makeId' + i + j);
}
tblBody.appendChild(row);
}
tbl.appendChild(tblBody);
body.appendChild(tbl)
}
grid();
Rather than set event listeners on every single td element, use event delegation.
Set a listener on the table body. It will hear the clicks on the td elements. Use the event object's target property to determine which td was clicked. That is the one the change:
document.querySelector('tbody').addEventListener('click', (e)=>{
e.target.style.backgroundColor = 'pink';
}
You need to bind the click event to the created cell
EDITED: using Event delegation
tblBody.addEventListener('click', (e)=>{
if (e.target.nodeName.toUpperCase() === 'TD') {
console.log(e.target.id)
e.target.classList.add('bg');
}
});
const grid = function makeGrid() {
var body = document.getElementById('pixelCanvas');
var tbl = document.createElement('table')
var tblBody = document.createElement('tbody')
tblBody.addEventListener('click', (e)=>{
if (e.target.nodeName.toUpperCase() === 'TD') {
console.log(e.target.id)
e.target.classList.add('bg');
}
});
for (let i = 0; i < 5; i++) {
let row = document.createElement('tr')
for (let j = 0; j < 5; j++) {
var cell = document.createElement('td')
cell.innerHTML = 'makeId' + i + j;
row.appendChild(cell)
cell.setAttribute('id', 'makeId' + i + j);
}
tblBody.appendChild(row);
}
tbl.appendChild(tblBody);
body.appendChild(tbl)
}
grid();
.bg {
background-color: lightgreen;
}
<div id="pixelCanvas">
</div>
As suggested by Randy, rather than put a listener on every TD you can use event delegation and put the listener on a suitable ancestor, either a parent table section (tbody, thead, tfoot) or the table itself. Then use the event object passed to the listener to get the target of the event. If it's not a TD, go up the parent nodes until you reach a TD.
There is now Element.closest implemented in most browsers in use, but not IE. So you might want to use a polyfill or simple "upTo" function for those users.
Also, you can take advantage of the insertRow and insertCell methods of table and table section elements rather than the more long winded createElement. Lastly, you can combine creating the element and appending it in one go.
Anyhow, like this:
// Create a table with rows rows and cells columns
function genTable(rows, cells) {
var table = document.createElement('table');
// Create tbody and append in one go
var tbody = table.appendChild(document.createElement('tbody'));
tbody.addEventListener('click',highlightCell);
var row, cell;
// Use insert methods for less code
for (var i=0; i<rows; i++) {
row = tbody.insertRow();
for (var j=0; j<cells; j++) {
cell = row.insertCell();
cell.textContent = 'row ' + i + ' cell ' + j;
}
}
// Add entire table to document in one go, easier on host
document.body.appendChild(table);
}
// Highlight cell that was clicked on
function highlightCell(e){
// Remove highlight from all cells that have it
document.querySelectorAll('.highlight').forEach(
node => node.classList.remove('highlight')
);
// Add highlight to cell of td in which event occurred
var cell = e.target.closest('td');
cell.classList.add('highlight');
}
window.onload = function(){genTable(3, 3);};
table {
border-collapse: collapse;
border-left: 1px solid #bbbbbb;
border-top: 1px solid #bbbbbb;
}
td {
border-right: 1px solid #bbbbbb;
border-bottom: 1px solid #bbbbbb;
}
.highlight {
background-color: red;
}
Related
I'm trying to make an interactable and configurable grid using divs as their cells.
To first give a bit of context on this part of the code, I'm basically just repeating td's in HTML output then appending a specific amount of divs into the td's/rows (cellID is just a method for decorating the div names);
var table, row, div;
table = document.createElement('table');
for (var i=0; i < rows; i++)
{
row = document.createElement('td');
row.id = "row-" + i;
for (var j=0; j < cols; j++)
{
div = document.createElement('div');
div.id = cellID(i, j);
row.append(div);
}
table.append(row);
}
Let's say that:
-rows = 4 and -cols = 2 | The output result on the user's end would be this :
Now my current problem that I'm trying to figure out is how to make every repeated div be given the onClick() event so that an event occurs when you click on the boxes, here is what I tried to make this work :
div.addEventListener("click", null); //This part didn't work for me, though to be honest, I don't really know what to replace the null with
div.getElementById(div.id).onclick = function() { OnClick() }; (OnClick is a test method to see if it works, however this one just rejects an error, saying that "div.getElementById" is not a function, so I don't know what's up with that.)
These things I tried were all things that had been recommended throughout, but I don't know what else could make it work.
What do you think the problem may be there?
-
div.addEventListener() should work.
But you need to create valid DOM structure. Rows are tr, cells are td. You can put the div in the td, or just use the td directly.
let rows = 2,
cols = 4;
var table, row, div;
table = document.createElement('table');
for (var i = 0; i < rows; i++) {
row = document.createElement('tr');
row.id = "row-" + i;
for (var j = 0; j < cols; j++) {
let cell = document.createElement("td");
div = document.createElement('div');
div.id = cellID(i, j);
div.addEventListener("click", function() {
console.log('Clicked on', this.id);
});
div.innerText = div.id;
cell.append(div);
row.append(cell);
}
table.append(row);
}
document.body.appendChild(table);
function cellID(i, j) {
return `cell-${i}-${j}`;
}
How can I adapt this code so that when the row is selected the color changes but also resets anything else that has been clicked back to the original color. Obviously right now it changes the rows colour every click and all past selected rows colours stay the same.
var tr = document.getElementById("r_row" + r);
for (var c = 0; c < columns; c++) {
var td = document.createElement("td");
td.innerText = cellText[c];
tr.appendChild(td);
//Makes the rows selectable
tr.onclick = function(event) {
selectedItem = this;
selectedItem.style.background = "#828891";
}
}
An alternative is to add a class, i.e selected and check for a selected TR using a collection of the selectable TRs.
var trs = Array.from(document.querySelectorAll('[id^="r_row"]'));
trs.forEach(function(elem) {
elem.addEventListener('click', function() {
trs.forEach(function(tr) {
if (tr !== elem) tr.classList.remove('selected');
});
this.classList.add('selected');
});
});
function addTRs(ids) {
var columns = 3;
ids.forEach(function(r) {
var tr = document.getElementById("r_row" + r);
for (var c = 0; c < columns; c++) {
var td = document.createElement("td");
td.innerText = `This is a test - ${r}`;
tr.appendChild(td);
}
});
}
addTRs([1, 2]);
.selected {
background-color: #828891;
}
tr {
cursor: pointer
}
<table>
<tbody>
<tr id='r_row1'></tr>
<tr id='r_row2'></tr>
<tr>
<td colSpan='3'>Not selectable!</td>
</tr>
</tbody>
</table>
I created a table and populated values from an array, so I have 5x5 table, where each td will be filled with a word. The word come from array memo and all the code below works fine.
var myTableDiv = document.getElementById("results")
var table = document.createElement('TABLE')
var tableBody = document.createElement('TBODY')
table.border = '1'
table.appendChild(tableBody);
//TABLE ROWS
for (i = 0; i < this.memo.length; i++) {
var tr = document.createElement('TR');
for (j = 0; j < this.memo[i].length; j++) {
var td = document.createElement('TD');
td.onclick = function () {
check();
}
td.appendChild(document.createTextNode(this.memo[i][j]));
tr.appendChild(td)
}
tableBody.appendChild(tr);
}
myTableDiv.appendChild(table);
I have one question : I would like to click on the cell and get the word, which belongs to the cell.
For this purpose I tried onclick as I created td element
td.onclick = function () {
check();
}
The function check should print the innerHTML of the cell, which was clicked
function check() {
var a = td.innerHTML;
console.log(a);
}
But it gives me always wrong text - the last one in the array, which was populated.
How could I solve it?..
You always get the last td in the array because the last value that was set to td was of the last cell. You need to add the a parameter, say event, to onclick's callback function, and then your clicked element will be referenced in event.target. Then you would be able to get it's innerHTML.
Here's why it's always giving you the first element: after the for (j = 0; ... loop is finished, the variable td will hold the value of the last element in the list. Then, when check is called, it accesses that same td variable pointing to the last element.
To solve this, you can add an argument to the function to accept a specific element and log that.
td.onclick = function () {
check(td);
};
// later...
function check(element) {
var html = element.innerHTML;
console.log(html);
}
I would pass the innerHTML in the click itself - please see working example below, with some mock data for memo.
var myTableDiv = document.getElementById("results")
var table = document.createElement('TABLE')
var tableBody = document.createElement('TBODY')
var memo = [
['joe', 'tom', 'pete'],
['sara','lily', 'julia'],
['cody','timmy', 'john']
]
table.border = '1'
table.appendChild(tableBody);
//TABLE ROWS
for (i = 0; i < this.memo.length; i++) {
var tr = document.createElement('TR');
for (j = 0; j < this.memo[i].length; j++) {
var td = document.createElement('TD');
td.onclick = function () {
check(this.innerHTML);
}
td.appendChild(document.createTextNode(this.memo[i][j]));
tr.appendChild(td)
}
tableBody.appendChild(tr);
}
myTableDiv.appendChild(table);
function check(a) {
console.log(a);
}
<div id="results">
</div>
you can try..
td.onclick = function () {
check();
}
to
td.onclick = function (evt) {
var html = evt.target.innerHTML;
console.log(html);
check(html); //to do something..
}
The individual table rows are giving me a problem. I have created what I want using divs but I need to use a table instead of divs. My table has 220 cells, 10 rows, and 22 columns. Each cell has to have the value of i inside the innerHTML. Here is similar to what i want using Divs ( although the cell height and width does not have to be set ):
<!DOCTYPE html>
<html>
<head>
<style>
#container{
width:682px; height:310px;
background-color:#555; font-size:85%;
}
.cell {
width:30px; height:30px;
background-color:#333; color:#ccc;
float:left; margin-right:1px;
margin-bottom:1px;
}
</style>
</head>
<body>
<div id="container">
<script>
for( var i = 1; i <= 220; i++ ){
document.getElementById( 'container' ).innerHTML +=
'<div class="cell">' + i + '</div>'
}
</script>
</div>
</body>
</html>
http://jsfiddle.net/8r6619wL/
This is my starting attempt using a table:
<script>
for( var i = 0; i <= 10; i++ )
{
document.getElementById( 'table' ).innerHTML +=
'<tr id = "row' + i + '"><td>...</td></tr>';
}
</script>
But that code somehow dynamically creates a bunch of tbody elements. Thanks for help as I newb
You can do this with nested loops - one to add cells to each row and one to add rows to the table. JSFiddle
var table = document.createElement('table'), tr, td, row, cell;
for (row = 0; row < 10; row++) {
tr = document.createElement('tr');
for (cell = 0; cell < 22; cell++) {
td = document.createElement('td');
tr.appendChild(td);
td.innerHTML = row * 22 + cell + 1;
}
table.appendChild(tr);
}
document.getElementById('container').appendChild(table);
Alternatively, you can create an empty row of 22 cells, clone it 10 times, and then add the numbers to the cells.
var table = document.createElement('table'),
tr = document.createElement('tr'),
cells, i;
for (i = 0; i < 22; i++) { // Create an empty row
tr.appendChild(document.createElement('td'));
}
for (i = 0; i < 10; i++) { // Add 10 copies of it to the table
table.appendChild(tr.cloneNode(true));
}
cells = table.getElementsByTagName('td'); // get all of the cells
for (i = 0; i < 220; i++) { // number them
cells[i].innerHTML = i + 1;
}
document.getElementById('container').appendChild(table);
And a third option: add the cells in a single loop, making a new row every 22 cells.
var table = document.createElement('table'), tr, td, i;
for (i = 0; i < 220; i++) {
if (i % 22 == 0) { // every 22nd cell (including the first)
tr = table.appendChild(document.createElement('tr')); // add a new row
}
td = tr.appendChild(document.createElement('td'));
td.innerHTML = i + 1;
}
document.getElementById('container').appendChild(table);
Edit - how I would do this nowadays (2021)... with a helper function of some kind to build dom elements, and using map.
function make(tag, content) {
const el = document.createElement(tag);
content.forEach(c => el.appendChild(c));
return el;
}
document.getElementById("container").appendChild(make(
"table", [...Array(10).keys()].map(row => make(
"tr", [...Array(22).keys()].map(column => make(
"td", [document.createTextNode(row * 22 + column + 1)]
))
))
));
There are a lot of ways to do this, but one I've found to be helpful is to create a fragment then append everything into it. It's fast and limits DOM re-paints/re-flows from a loop.
Take a look at this jsbin example.
Here's the modified code:
function newNode(node, text, styles) {
node.innerHTML = text;
node.className = styles;
return node;
}
var fragment = document.createDocumentFragment(),
container = document.getElementById("container");
for(var i = 1; i <= 10; i++) {
var tr = document.createElement("tr");
var td = newNode(document.createElement("td"), i, "cell");
tr.appendChild(td);
fragment.appendChild(tr);
}
container.appendChild(fragment);
You can modify whatever you want inside the loop, but this should get you started.
That's because the DOM magically wraps a <tbody> element around stray table rows in your table, as it is designed to do. Fortunately, you can rewrite your loop in a way that will add all of those table rows at once, rather than one at a time.
The simplest solution to achieve this would be to store a string variable, and concatenate your rows onto that. Then, after you've concatenated your rows together into one string, you can set the innerHTML of your table element to that one string like so:
<script>
(function() {
var rows = '';
for( var i = 0; i <= 10; i++ )
{
rows += '<tr id = "row' + i + '"><td>...</td></tr>';
}
document.getElementById( 'table' ).innerHTML = rows;
}());
</script>
Here's a JSFiddle that demonstrates what I've just written. If you inspect the HTML using your browser's developer tools, you'll notice that one (and only one) tbody wraps around all of your table rows.
Also, if you're wondering, the odd-looking function which wraps around that code is simply a fancy way of keeping the variables you've created from becoming global (because they don't need to be). See this blog post for more details on how that works.
please check this out.
This is a very simple way to create a table using js and HTML
<body>
<table cellspacing="5" >
<thead>
<tr>
<td>Particulate count</td>
<td>Temperature</td>
<td>Humidity</td>
</tr>
</thead>
<tbody id="xxx">
</tbody>
</table>
<script>
for (var a=0; a < 2; a++) {
var table1 = document.getElementById('xxx');
var rowrow = document.createElement('tr');
for ( i=0; i <1; i++) {
var cell1 = document.createElement('td');
var text1 = document.createTextNode('test1'+a);
var cell2 = document.createElement('td');
var text2 = document.createTextNode('test2'+a);
var cell3 = document.createElement('td');
var text3 = document.createTextNode('test3'+a);
cell1.appendChild(text1);
rowrow.appendChild(cell1);
cell2.appendChild(text2);
rowrow.appendChild(cell2);
cell3.appendChild(text3);
rowrow.appendChild(cell3);
}
table1.appendChild(rowrow);
}
</script>
</body>
</html>
I need to add rows dynamically to a table on a button click event using JavaScript. In addition, the table cells need to contain textboxes.
How can I do this?
Here's a sample code taken from this source. Suggest you read more about DOM, specifically about DOM tables for this question.
function start() {
// get the reference for the body
var body = document.getElementsByTagName("body")[0];
// creates a <table> element and a <tbody> element
var tbl = document.createElement("table");
var tblBody = document.createElement("tbody");
// creating all cells
for (var j = 0; j < 2; j++) {
// creates a table row
var row = document.createElement("tr");
for (var i = 0; i < 2; i++) {
// Create a <td> element and a text node, make the text
// node the contents of the <td>, and put the <td> at
// the end of the table row
var cell = document.createElement("td");
var cellText = document.createTextNode("cell is row "+j+", column "+i);
cell.appendChild(cellText);
row.appendChild(cell);
}
// add the row to the end of the table body
tblBody.appendChild(row);
}
// put the <tbody> in the <table>
tbl.appendChild(tblBody);
// appends <table> into <body>
body.appendChild(tbl);
// sets the border attribute of tbl to 2;
tbl.setAttribute("border", "2");
}