Как получить NodeList созданных элементов через JS?

Нужно доступиться до конкретного элемента в NodeList-e, и при нажатии на него удалить из таблицы строку (а также удалить из массива сам объект), но не могу получить доступ до кнопки из списка.

у меня есть :

<tbody id="user-list"></tbody>

let list = document.getElementById('user-list'); // доступ

function AddUser() { // create user
    let obj = {};
    obj.name = loginField.value; // собирает из инпутов value и формирует объект
    obj.pass = passField.value; // собирает из инпутов value и формирует объект
    obj.mail = mailField.value; // собирает из инпутов value и формирует объект
    arr.push(obj); пушит в массив
    loginField.value = '';
    passField.value = '';
    mailField.value = '';
}

Этот объект нужно "рендерить" в html -

function render() {

    const row = document.createElement('tr');
    for (i = 0; i < arr.length; i++) {
        row.innerHTML = `
        <td>${i+1}</td>
        <td>${arr[i].name}</td>
        <td>${arr[i].pass}</td>
        <td>${arr[i].mail}</td>
        <td><button type="button" class="btn btn-warning edit">Edit</button></td> // бутстрап кнопка
        <td><button type="button" class="btn btn-danger delete">Delete</button></td> // бутстрап кнопка
        `;
    }
    list.append(row);
}

Как мне получить доступ ко всем созданным кнопкам, чтобы потом удалить из массива этот объект по индексу? NodeList всегда пустой, он не видит кнопок и даже строк или ячеек в таблице (через querySelectorAll())


Ответы (2 шт):

Автор решения: Pavel Nazarian

В конец функции render попробуйте добавить:

list.querySelectorAll('tr .btn.remove').forEach((elem,index) => {
    elem.addEventListener('click', function(){                  
        arr.splice(index,1)
        render()
    })      
})

И если Вы хотите отрисовывать таблицу заново, возможно лучше очищать содержимое, а потом аппендить.

list.innerHTML = ''
→ Ссылка
Автор решения: yar85

Две функции (для рендеринга элементов данных, и для делегированной обработки кликов по кнопкам), с их использованием:

function render(items, tBody, fields = []) {
  const buf = document.createElement('tbody');
  for (const item of items) {
    const row = buf.insertRow();
    fields.forEach(fld => row.insertCell().textContent = item[fld]);
    // на самом деле, кнопки было бы лучше рендерить тоже из объекта-конфига действий (операций над элементом данных), но для простоты пусть разметкой
    row.insertAdjacentHTML('beforeend', `
      <td><button type="button" class="btn btn-warning edit">Edit</button></td>
      <td><button type="button" class="btn btn-danger delete">Delete</button></td>
    `);
    row._item = item;  // сохраняем в DOM-объекте ссылку на соотв. объект данных
  }
  tBody.replaceChildren(...buf.children);
  return tBody.children;  // если и правда нужен NodeList строк таблицы...
}

function clickHandler(items, renderFunc, evt) {
  const btn = evt.target.closest('.btn');
  if (!btn) return;
  const item = btn.closest('tr')._item;
  if (btn.classList.contains('edit')) {
    console.log('edit item', item);  // тут предполагается заполнение формы значениями из `item`
  } else if (btn.classList.contains('delete')) {
    const itemIdx = items.indexOf(item);
    if (itemIdx > -1) {
      items.splice(itemIdx, 1);
      console.log('item deleted\n', items);
      renderFunc();
    }
  }
}

const users = [
  { name: 'Василий', passw: 'dgh384fgy8', email: '[email protected]' },
  { name: 'Иван', passw: 'танки7777', email: '[email protected]' },
  { name: 'Екатерина', passw: '1234', email: '[email protected]' },
  { name: 'Петр', passw: 'j56fg646t3', email: '[email protected]' },
  { name: 'Анастасия', passw: '04101995', email: '[email protected]' },
];
const tBody = document.querySelector('table tbody'),
      userFields = ['name', 'passw', 'email'],
      renderUsers = render.bind(this, users, tBody, userFields),
      usersClickHandler = clickHandler.bind(tBody, users, renderUsers);
tBody.addEventListener('click', usersClickHandler);  // общий обработчик кликов по элементам таблицы - тут используется только для кнопок
renderUsers();
table { border-collapse: collapse; }
td { padding: 0.2em 0.3em; border: 1px solid #ccc; }
<table>
  <tbody></tbody>
</table>

Рекомендую после запуска примера нажать "На всю страницу", чтобы консольный вывод не мешал видеть таблицу.


Функция render в примере возвращает NodeList состоящий из элементов tr, но в данном случае это никак не используется - сделано только чтобы ответить на одну из частей заданного вопроса (чтобы показать как это в принципе можно реализовать).

// Вообще, тут разумнее было бы применить ООП, сделав эти функции методами объекта данных (или разделив на объекты модели и контроллера), но раз вопрос с отдельными самостоятельными функциями, то и ответ соответственно с такими же.

→ Ссылка