Как я могу добавитьEventListener к каждому innerHTML += в этом цикле for?

Я пытаюсь добавить прослушиватель событий для некоторых повторяющихся 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 += имеет прикрепленный прослушиватель событий, и когда я нажимаю на него, я вижу данные, которые должны соответствовать последней итерации.

Что я здесь делаю неправильно?

Почему вы используете eval()???

epascarello 10.04.2019 03:00
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Улучшение производительности загрузки с помощью Google Tag Manager и атрибута Defer
Улучшение производительности загрузки с помощью Google Tag Manager и атрибута Defer
В настоящее время производительность загрузки веб-сайта имеет решающее значение не только для удобства пользователей, но и для ранжирования в...
Безумие обратных вызовов в javascript [JS]
Безумие обратных вызовов в javascript [JS]
Здравствуйте! Юный падаван 🚀. Присоединяйся ко мне, чтобы разобраться в одной из самых запутанных концепций, когда вы начинаете изучать мир...
Система управления парковками с использованием HTML, CSS и JavaScript
Система управления парковками с использованием HTML, CSS и JavaScript
Веб-сайт по управлению парковками был создан с использованием HTML, CSS и JavaScript. Это простой сайт, ничего вычурного. Основная цель -...
JavaScript Вопросы с множественным выбором и ответы
JavaScript Вопросы с множественным выбором и ответы
Если вы ищете платформу, которая предоставляет вам бесплатный тест JavaScript MCQ (Multiple Choice Questions With Answers) для оценки ваших знаний,...
0
1
2 211
2
Перейти к ответу Данный вопрос помечен как решенный

Ответы 2

Ответ принят как подходящий

Вам нужно переместить код, устанавливающий обработчики событий, так, чтобы он находился за пределами вашего цикла 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 непосредственно в итерируемом querySelectorAllNodeList — вы можете это сделать NodeList.forEach()stackoverflow.com/a/32664363/383904

Roko C. Buljan 10.04.2019 02:31

@RokoC.Buljan Я понятия не имею, что такое «вечнозеленые браузеры», но использование Array.prototype.slice.call() рекомендуется для совместимости с браузерами, которые не поддерживают .forEach() в списках узлов, таких как все версии IE.

Scott Marcus 10.04.2019 02:33

Один из вариантов — посмотреть событие делегирование/бульканье. Основной принцип здесь заключается в том, что вы добавляете обработчик события к родительскому объекту, в данном случае <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 в равной степени относятся к этому ответу.

Другие вопросы по теме