Я пытаюсь добавить прослушиватель событий для некоторых повторяющихся innerHTML
. То есть для каждой партии HTML, добавленной с помощью innerHTML, мне также нужно будет добавить к ней событие клика.
Чтобы усложнить ситуацию, я также импортирую набор данных из другого файла JS, импортированного под именем data. Как вы можете видеть в коде, мне нужны данные внутри прослушивателя событий, относящиеся к итерации цикла for внутреннего HTML, чтобы при запуске прослушивателя событий я мог видеть правильные, соответствующие данные.
Это моя попытка:
JS:
import data from './data.js';
import img from './images.js';
export const lists = () => {
const main = document.getElementById('main');
main.innerHTML = `
<div class = "main-container">
<div class = "flex-between row border-bottom">
<div class = "flex new-list">
<img class = "create-img img-radius" src = "${img.symbols[0]}" alt = "Delete Bin">
<h3>New List</h3>
</div>
<div class = "flex-between sections">
<h3 class = "text-width flex-c">Items:</h3>
<h3 class = "text-width flex-c">Reminders:</h3>
<h3 class = "text-width flex-end">Created:</h3>
</div>
</div>
<div id = "lists"></div>
</div>
`;
const lists = document.getElementById('lists');
for (let i = 0; i < data.lists.length; i++) {
let obj = eval(data.lists[i]);
let totalReminders = getTotalReminders(obj);
lists.innerHTML += `
<div class = "flex-between row list">
<h4>${obj.name}</h4>
<div class = "flex-between sections">
<h4 class = "number-width flex-c">${obj.items.length}</h4>
<h4 class = "number-width flex-c">${totalReminders}</h4>
<div class = "text-width flex-end">
<h4 class = "date">${obj.created}</h4>
<img class = "img-radius" src = "${img.symbols[3]}" alt = "Delete Bin">
</div>
</div>
</div>
`;
const list = document.querySelector('.list');
list.addEventListener('click', () => { // click event
listNav.listNav(obj.name);
listSidebarL.listSidebarL();
listSidebarR.listSidebarR();
listMain.listMain(obj.items);
});
};
};
const getTotalReminders = passed => { // find total reminders
let total = 0;
for (let i = 0; i < passed.items.length; i++) {
total += passed.items[i].reminders;
};
return total;
};
На данный момент ТОЛЬКО первая итерация innerHTML +=
имеет прикрепленный прослушиватель событий, и когда я нажимаю на него, я вижу данные, которые должны соответствовать последней итерации.
Что я здесь делаю неправильно?
Вам нужно переместить код, устанавливающий обработчики событий, так, чтобы он находился за пределами вашего цикла for и запускался после завершения этого цикла. Затем вместо .querySelector()
, которая возвращает только первый совпадающий элемент, вам нужно .querySelectorAll()
, чтобы вернуть все совпадающие элементы. После этого вы пройдете по всем этим элементам и настроите обработчик.
Вам также нужно изменить способ объявления вашей переменной obj
, чтобы она находилась за пределами цикла for
. Сделайте это, объявив его непосредственно перед циклом, но назначив его внутри цикла:
let obj = null; // Now, obj is scoped so it can be accessed outside of the loop
for (let i = 0; i < data.lists.length; i++) {
obj = eval(data.lists[i]);
И поместите следующее сразу после завершения цикла for
:
// Get all the .list elements into an Array
const list = Array.prototype.slice.call(document.querySelectorAll('.list'));
// Loop over the array and assign an event handler to each array item:
list.forEach(function(item){
item.addEventListener('click', () => {
listNav.listNav(obj.name);
listSidebarL.listSidebarL();
listSidebarR.listSidebarR();
listMain.listMain(obj.items);
});
});
С учетом всего сказанного, ваш подход здесь действительно не очень хорош. Почти всегда есть другой вариант, чем использовать eval()
для чего-либо, и использование .innerHTML
обычно является чем-то, чего следует избегать из-за его последствий для безопасности и производительности. Использование его в цикле почти всегда является плохой идеей. Вам действительно следует использовать DOM API для создания новых элементов, их настройки и внедрения в DOM. Если вы должны использовать .innerHTML
, создайте строку в своем цикле и после цикла вставьте строку в DOM через .innerHTML
, только один раз.
Не надо Array.prototype.slice.call
. Все вечнозеленые браузеры реализовали и выставили .forEach
непосредственно в итерируемом querySelectorAll
NodeList
— вы можете это сделать NodeList.forEach()
stackoverflow.com/a/32664363/383904
@RokoC.Buljan Я понятия не имею, что такое «вечнозеленые браузеры», но использование Array.prototype.slice.call()
рекомендуется для совместимости с браузерами, которые не поддерживают .forEach()
в списках узлов, таких как все версии IE.
Один из вариантов — посмотреть событие делегирование/бульканье. Основной принцип здесь заключается в том, что вы добавляете обработчик события к родительскому объекту, в данном случае <div id = "lists"></div>
. Затем, когда событие запускается, вы запрашиваете цель этого события, чтобы увидеть, соответствует ли она вашему элементу.
Используя этот метод, вам не нужно повторно связывать обработчики событий при добавлении новых элементов, что особенно полезно, если элементы добавляются в результате взаимодействия с пользователем.
В вашем случае это будет выглядеть примерно так:
export const lists = () => {
const main = document.getElementById('main');
main.innerHTML = `
<div class = "main-container">
<div class = "flex-between row border-bottom">
<div class = "flex new-list">
<img class = "create-img img-radius" src = "${img.symbols[0]}" alt = "Delete Bin">
<h3>New List</h3>
</div>
<div class = "flex-between sections">
<h3 class = "text-width flex-c">Items:</h3>
<h3 class = "text-width flex-c">Reminders:</h3>
<h3 class = "text-width flex-end">Created:</h3>
</div>
</div>
<div id = "lists"></div>
</div>
`;
const lists = document.getElementById('lists');
//Now that the parent element is added to the DOM
//Add the event handler
lists.addEventListener("click",function(e) {
// e.target was the clicked element
if (e.target && e.target.matches(".list")) {
listNav.listNav(obj.name);
listSidebarL.listSidebarL();
listSidebarR.listSidebarR();
listMain.listMain(obj.items);
}
//Add Items etc
});
Комментарии ПРИМЕЧАНИЕшотландцы относительно eval
и innerHTML
в равной степени относятся к этому ответу.
Почему вы используете eval()???