简体   繁体   English

如何在此for循环中将EventListener添加到每个innerHTML + =?

[英]How can I addEventListener to each innerHTML += in this for loop?

I'm trying to add an event listener on some repeating innerHTML . 我正在尝试在一些重复的innerHTML上添加事件侦听器。 IE for every lot of HTML added by innerHTML, I'll also need to add a click event onto it. 对于由innerHTML添加的所有HTML,IE也需要在其上添加click事件。

To complicate things I'm also importing a data set from another JS file imported under the name data. 为了使事情复杂化,我还要从名称数据下导入的另一个JS文件中导入数据集。 As you can see in the code I need the data inside the event listener to be specific to the for loop iteration of the innerHTML so that when I fire the event listener I can see the correct, corresponding data. 如您在代码中所看到的,我需要事件侦听器内部的数据特定于innerHTML的for循环迭代,以便当我触发事件侦听器时,可以看到正确的对应数据。

This is my attempt: 这是我的尝试:

JS: 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;
};

At the moment ONLY the first iteration of innerHTML += has an event listener attached and when I click on it I see the data that should be corresponding the last iteration. 目前,仅innerHTML +=的第一次迭代附加了事件侦听器,当我单击它时,我看到的数据应该与上次迭代相对应。

What am I doing wrong here? 我在这里做错了什么?

You need to move the code that sets up the event handlers so that it is outside of your for loop and runs after that loop is finished. 您需要移动设置事件处理程序的代码,以使其位于for循环之外,并在该循环结束后运行。 Then, instead of .querySelector() , which only returns the first matching element, you need .querySelectorAll() to return all matching elements. 然后,而不是.querySelector()只返回第一个匹配的元素,你需要.querySelectorAll()返回所有匹配的元素。 After that, you'll loop through all those elements and set up the handler. 之后,您将遍历所有这些元素并设置处理程序。

You'll also need to change how your obj variable is declared so that it will be in scope outside of the for loop. 您还需要更改obj变量的声明方式,使其位于for循环之外。 Do this by declaring it just before the loop, but assigning it inside the loop: 为此,只需在循环之前声明它,但在循环内部分配它:

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]);

And, put the following just after the for loop finishes: 并且,在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);
  });
});

With all this said, your approach here is really not very good. 综上所述,您在这里的方法确实不是很好。 There is almost always another option than to use eval() for anything and using .innerHTML is usually something to avoid due to its security and performance implications. 除了使用eval()进行任何操作外,几乎总是存在另一种选择,并且由于其安全性和性能影响,通常避免使用.innerHTML Using it in a loop is almost always a bad idea. 循环使用它几乎总是一个坏主意。 You really should be using the DOM API to create new elements, configure them and inject them into the DOM. 您确实应该使用DOM API来创建新元素,对其进行配置并将它们注入DOM。 If you must use .innerHTML , then build up a string in your loop and after the loop, inject the string into the DOM via .innerHTML , just once. 如果必须使用.innerHTML ,则在循环中构建一个字符串,然后在循环之后,通过.innerHTML将字符串注入DOM一次。

One options is to look at event delegation/bubbling . 一种选择是查看事件委托/冒泡 The basic principle here is you add the event handler to a parent object, in this case <div id="lists"></div> . 这里的基本原理是将事件处理程序添加到父对象,在本例中为<div id="lists"></div> Then when the event is fired you query the target of that event to see if it matches your element. 然后,在触发事件时,您查询该事件的目标以查看其是否与您的元素匹配。

Using this technique you don't have to re-bind event handlers when new items are added, particularly useful if the items are added by user interaction. 使用此技术,在添加新项目时不必重新绑定事件处理程序,特别是在通过用户交互添加项目时特别有用。

In your case it would look something like: 在您的情况下,它将类似于:

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    
});

NOTE Scots comments re eval and innerHTML apply equally to this answer. 注意 苏格兰语评论evalinnerHTML同样适用于此答案。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM