Вместо добавления в
#elem
нового контента следует
полностью перезаписывать его содержимое:
document.querySelector('#elem').innerHTML = `
<table>${Array.from({ length: 2 }, (_, iRow) => `
<tr>${Array.from({ length: columns }, (_, iCol) => `
<td>${iRow + 1}.${iCol + 1}</td>`).join('')}
</tr>`).join('')}
</table>
`;
Или, можно заранее создать таблицу с пустыми строками:
const table = document.createElement('table');
table.insertRow();
table.insertRow();
document.querySelector('#elem').append(table);
И добавлять недостающее или удалять лишнее:
for (const n of table.rows) {
while (n.cells.length < columns) {
n.insertCell().textContent = `${-~n.rowIndex}.${n.cells.length}`;
}
while (n.cells.length > columns) {
n.lastElementChild.remove();
}
}