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;
}