Если не думать о производительности, то примерно так. С вашим кодом очень сложно работать. Разделите логику на вывод шаблона и функцию, которая будет прослушивать события (некий контроллер) и запускать нужные функции ( удаление, редактирование и т.д. ). Я не выводил таблицу целиком при первом рендеринге, можно добавить это. Обычно такие задачи решают добавив шаблонизатор (JSX к примеру) и описывая всё через ООП. Либо сразу взять удобный фреймворк для фронта, если на проекте много таких задач.
<!DOCTYPE html>
<html lang="ru">
<head>
<meta charset="UTF-8">
<title>Document</title>
</head>
<body>
<table id="some-table">
<thead>
<tr>
<th>Наименование</th>
<th>Описание</th>
<th>Количество</th>
<th>Действия</th>
</tr>
</thead>
<tbody>
</tbody>
</table>
</body>
</html>
window.onload = () => {
const table = document.getElementById('some-table')
const tableBody = table.querySelector('tbody')
let serverData = [
{
id: parseInt( Math.random() * 1000 ),
name: 'name 1',
description: 'description 1',
qnt: '5'
},
{
id: parseInt( Math.random() * 1000 ),
name: 'name 2',
description: 'description 2',
qnt: '2'
},
{
id: parseInt( Math.random() * 1000 ),
name: 'name 2',
description: 'description 2',
qnt: '8'
}
]
const render = html => tableBody.innerHTML = html
const template = data => {
return data.map( item => {
return `
<tr>
<td>${ item.name }</td>
<td>${ item.description }</td>
<td>${ item.qnt }</td>
<td><button data-id="${ item.id }" data-action="remove">Удалить</button></td>
</tr>
`
}).join('')
}
const rmv = id => {
serverData = serverData.filter( item => item.id !== parseInt( id ) )
}
const tableHandler = e => {
const $el = e.target
switch( $el.dataset.action ) {
case 'remove':
rmv( $el.dataset.id )
break
}
render( template( serverData ) )
}
const run = () => {
render( template( serverData ) )
table.addEventListener( 'click', tableHandler )
}
run()
}