Как получить 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 шт):
В конец функции render попробуйте добавить:
list.querySelectorAll('tr .btn.remove').forEach((elem,index) => {
elem.addEventListener('click', function(){
arr.splice(index,1)
render()
})
})
И если Вы хотите отрисовывать таблицу заново, возможно лучше очищать содержимое, а потом аппендить.
list.innerHTML = ''
Две функции (для рендеринга элементов данных, и для делегированной обработки кликов по кнопкам), с их использованием:
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, но в данном случае это никак не используется - сделано только чтобы ответить на одну из частей заданного вопроса (чтобы показать как это в принципе можно реализовать).
// Вообще, тут разумнее было бы применить ООП, сделав эти функции методами объекта данных (или разделив на объекты модели и контроллера), но раз вопрос с отдельными самостоятельными функциями, то и ответ соответственно с такими же.