简体   繁体   English

难以将唯一的事件侦听器添加到已创建的<div>通过 for 循环的元素</div><div id="text_translate"><p>我正在做一个项目来学习一些 JavaScript,目标是在单页 web 应用程序中动态提供电子邮件。 应用程序的HTML已经使用createElement JS方法创建。 到目前为止,我已经成功地显示了包含所有电子邮件的“收件箱”页面,如下图所示:</p><p> <a href="https://i.stack.imgur.com/V8cZq.png" rel="nofollow noreferrer"><img src="https://i.stack.imgur.com/V8cZq.png" alt=""></a></p><p> 我目前正在尝试通过使用 addEventListener 使这些单独的电子邮件中的每一个都可以点击。 我遇到的问题是,每当我单击任何一封电子邮件时,都会呈现收件箱中的第一个 email(id:1,主题:测试电子邮件),并且无法查看任何其他电子邮件。</p><p> <a href="https://i.stack.imgur.com/BBjQj.png" rel="nofollow noreferrer"><img src="https://i.stack.imgur.com/BBjQj.png" alt=""></a></p><p> 我可以看到相同的 email.id 被应用到所有创建的 div 的事件侦听器,尽管所有 div 都被正确创建,并包含来自相应电子邮件的所有信息。</p><p> 这是加载邮箱 JS,它呈现收件箱中的所有电子邮件:</p><pre> function load_mailbox(mailbox) { // Show the mailbox and hide other views document.querySelector('#emails-view').style.display = 'block'; document.querySelector('#compose-view').style.display = 'none'; document.querySelector('#single-view').style.display = 'none'; // Show the mailbox name document.querySelector('#emails-view').innerHTML = `<h3>${mailbox.charAt(0).toUpperCase() + mailbox.slice(1)}</h3>`; // GET request fetch(`/emails/${mailbox}`).then(response => response.json()).then(emails => { // Print emails to console console.log(emails); // Iterate through each email for(var i = 0; i < emails.length; i++) { var email = emails[i]; // Create div and append email content to HTML var emaildiv = document.createElement('div'); emaildiv.style.borderStyle = 'solid'; emaildiv.style.borderColor = 'black'; emaildiv.style.borderWidth = '0.1rem'; emaildiv.style.borderRadius = '0'; emaildiv.style.marginBottom = '0.2rem'; emaildiv.style.padding = '0.3rem'; emaildiv.innerHTML = `<b>${email.sender}</b> --- ${email.subject}<br>${email.timestamp}`; // If individual email selected then view email emaildiv.addEventListener('click', () => view_email(email)); // Populate div HTML with emails document.querySelector('#emails-view').append(emaildiv); console.log(email.read); // Colour backgrounds based on whether emails have been read if (email.read == true) { emaildiv.style.backgroundColor = 'lightgrey'; } console.log(email); } });}</pre><p> 这是视图 email JS,它应该渲染个人 email 的 HTML:</p><pre> // View email function view_email(email) { console.log(email.id); // Show the mailbox and hide other views document.querySelector('#emails-view').style.display = 'none'; document.querySelector('#compose-view').style.display = 'none'; document.querySelector('#single-view').style.display = 'block'; // GET request fetch(`/emails/${email["id"]}`).then(response => response.json()).then(email => { // Create div, set class, and append email content to HTML var reademail = document.createElement('div'); reademail.innerHTML = ''; reademail.style.borderStyle = 'solid'; reademail.style.borderColor = 'black'; reademail.style.borderWidth = '0.1rem'; reademail.style.borderRadius = '0'; reademail.style.marginBottom = '0.2rem'; reademail.style.padding = '0.3rem'; reademail.innerHTML = ` <b>From:</b> ${email.sender}<br> <b>To:</b> ${email.recipients}<br> <b>Subject:</b> ${email.subject}<br> <b>Timestamp:</b> ${email.timestamp}<br> <button class="btn btn-sm btn-outline-primary" id="Reply">Reply</button> <hr> ${email.body}`; // Populate div HTML with emails document.querySelector('#single-view').append(reademail); // Mark unread emails as read if (email.read === 'false') { fetch(`/emails/${email}`, { method: 'PUT', body: JSON.stringify({ read: true }) }) } }); }</pre><p> 这是存储在 email GET 响应中的示例(虚拟数据):</p><pre> { "id": 1, "sender": "user@example.com", "recipients": ["user@example.com"], "subject": "Hello,": "body", "Hello, world:", "timestamp": "Oct 24 2020, 12:00 AM", "read": false, "archived": false }</pre><p> 我已经尝试将其他每封电子邮件的 ID 硬编码到视图 email JS 中,并且可以看到该功能按要求工作(显示电子邮件)。</p><p> 因此,我知道问题与上面的加载邮箱 JS 有关,并且可能与事件侦听器如何在 for 循环中应用有关。 如果有人可以阐明如何将唯一的事件侦听器应用于每个单独的 div,将不胜感激。</p><p> 谢谢!</p></div>

[英]Difficulty adding unique event listener to created <div> elements via a for loop

I'm working on a project to learn some JavaScript, the goal is to serve up emails dynamically in a single page web application.我正在做一个项目来学习一些 JavaScript,目标是在单页 web 应用程序中动态提供电子邮件。 The HTML of the application has been created using the createElement JS method.应用程序的HTML已经使用createElement JS方法创建。 So far I have managed to display the "inbox" page with all of the emails in it, as illustrated below:到目前为止,我已经成功地显示了包含所有电子邮件的“收件箱”页面,如下图所示:

I'm currently attempting to make each of these individual emails clickable through the use of an addEventListener.我目前正在尝试通过使用 addEventListener 使这些单独的电子邮件中的每一个都可以点击。 The issue that I'm having is that whenever I click any of the emails, the first email in the inbox (id:1, subject:Test Email) is rendered and it is not possible to view any of the others.我遇到的问题是,每当我单击任何一封电子邮件时,都会呈现收件箱中的第一个 email(id:1,主题:测试电子邮件),并且无法查看任何其他电子邮件。

I can see that the same email.id is being applied to the event listener for all of the created divs, despite all of the divs being created correctly, with all of the information from the corresponding emails.我可以看到相同的 email.id 被应用到所有创建的 div 的事件侦听器,尽管所有 div 都被正确创建,并包含来自相应电子邮件的所有信息。

Here is the load mailbox JS, which renders all of the emails within the inbox:这是加载邮箱 JS,它呈现收件箱中的所有电子邮件:

function load_mailbox(mailbox) {
  
  // Show the mailbox and hide other views
  document.querySelector('#emails-view').style.display = 'block';
  document.querySelector('#compose-view').style.display = 'none';
  document.querySelector('#single-view').style.display = 'none';

  // Show the mailbox name
  document.querySelector('#emails-view').innerHTML = `<h3>${mailbox.charAt(0).toUpperCase() + mailbox.slice(1)}</h3>`;

  // GET request
  fetch(`/emails/${mailbox}`)
  .then(response => response.json())
  .then(emails => {

    // Print emails to console
    console.log(emails);

    // Iterate through each email
    for(var i = 0; i < emails.length; i++) {
      var email = emails[i];
        
      // Create div and append email content to HTML
      var emaildiv = document.createElement('div');
      emaildiv.style.borderStyle = 'solid';
      emaildiv.style.borderColor = 'black';
      emaildiv.style.borderWidth = '0.1rem';
      emaildiv.style.borderRadius = '0';
      emaildiv.style.marginBottom = '0.2rem';
      emaildiv.style.padding = '0.3rem';
      emaildiv.innerHTML = `<b>${email.sender}</b> --- ${email.subject}<br>${email.timestamp}`;
      
      // If individual email selected then view email
      emaildiv.addEventListener('click', () => view_email(email));

      // Populate div HTML with emails
      document.querySelector('#emails-view').append(emaildiv); 
      console.log(email.read);

      // Colour backgrounds based on whether emails have been read
      if (email.read == true) {
        emaildiv.style.backgroundColor = 'lightgrey';
      }

      console.log(email);
  }   
});}

Here is the view email JS, which is supposed to render the HTML of the individual email:这是视图 email JS,它应该渲染个人 email 的 HTML:

// View email
function view_email(email) {
  console.log(email.id);

  // Show the mailbox and hide other views
  document.querySelector('#emails-view').style.display = 'none';
  document.querySelector('#compose-view').style.display = 'none';
  document.querySelector('#single-view').style.display = 'block';

  // GET request
  fetch(`/emails/${email["id"]}`)
  .then(response => response.json())
  .then(email => {
    
    // Create div, set class, and append email content to HTML
    var reademail = document.createElement('div');
    reademail.innerHTML = '';
    reademail.style.borderStyle = 'solid';
    reademail.style.borderColor = 'black';
    reademail.style.borderWidth = '0.1rem';
    reademail.style.borderRadius = '0';
    reademail.style.marginBottom = '0.2rem';
    reademail.style.padding = '0.3rem';
    reademail.innerHTML = `
    <b>From:</b> ${email.sender}<br>
    <b>To:</b> ${email.recipients}<br>
    <b>Subject:</b> ${email.subject}<br>
    <b>Timestamp:</b> ${email.timestamp}<br>
    <button class="btn btn-sm btn-outline-primary" id="Reply">Reply</button>
    <hr>
    ${email.body}`;

    // Populate div HTML with emails
    document.querySelector('#single-view').append(reademail); 

    // Mark unread emails as read
    if (email.read === 'false') {
      fetch(`/emails/${email}`, {
        method: 'PUT',
        body: JSON.stringify({
            read: true
        })
      })
    }      
});
}

Here is an example (dummy data) what is stored in the email GET response:这是存储在 email GET 响应中的示例(虚拟数据):

{
        "id": 1,
        "sender": "user@example.com",
        "recipients": ["user@example.com"],
        "subject": "Hello!",
        "body": "Hello, world!",
        "timestamp": "Oct 24 2020, 12:00 AM",
        "read": false,
        "archived": false
}

I have tried hard-coding in the id for each of the other emails into the view email JS and can see that the functionality works as required (displays the email).我已经尝试将其他每封电子邮件的 ID 硬编码到视图 email JS 中,并且可以看到该功能按要求工作(显示电子邮件)。

Consequently, I know that the issue is to do with the load mailbox JS above, and likely how the event listener is being applied within the for loop.因此,我知道问题与上面的加载邮箱 JS 有关,并且可能与事件侦听器如何在 for 循环中应用有关。 If anybody could shine some light on how to get a unique event listener applied to each individual div, that would be highly appreciated.如果有人可以阐明如何将唯一的事件侦听器应用于每个单独的 div,将不胜感激。

Thank you!谢谢!

Your problem is with the way your declaring the email within the loop:您的问题在于您在循环中声明 email 的方式:

var email = emails[i];

Because of scoping, you create a single closure around email and all iterations of the loop use one value.由于作用域,您在email周围创建了一个闭包,并且循环的所有迭代都使用一个值。 Change it to let for block level scope so that each loop iteration gets its own value to work with:将其更改为let for block level scope 以便每个循环迭代都有自己的值来使用:

let email = emails[i];

Other:其他:

To make your code simpler to read and maintain, don't set up inline styles. Instead create CSS Classes and just apply/remove the classes as needed.为了使您的代码更易于阅读和维护,请不要设置内联 styles。而是创建 CSS 类并根据需要应用/删除类。 So instead of:所以不是:

document.querySelector('#emails-view').style.display = 'none';
document.querySelector('#compose-view').style.display = 'none';
document.querySelector('#single-view').style.display = 'block';

Just make a CSS class called, say .hidden , like this:只需调用 CSS class ,比如.hidden ,就像这样:

.hidden { display:none; }

And, when an element needs that class or needs it removed do:而且,当一个元素需要 class 或需要删除它时:

someElement.classList.add("hidden");
someElement.classList.remove("hidden");

Similarly, instead of this:同样,而不是这个:

// Create div, set class, and append email content to HTML
var reademail = document.createElement('div');
reademail.innerHTML = '';
reademail.style.borderStyle = 'solid';
reademail.style.borderColor = 'black';
reademail.style.borderWidth = '0.1rem';
reademail.style.borderRadius = '0';
reademail.style.marginBottom = '0.2rem';
reademail.style.padding = '0.3rem';

Make a CSS Class, like this:制作一个 CSS Class,像这样:

.readEmail {
  border: 0.1rem solid black;
  border-radius: 0;
  margin-bottom: 0.2rem;
  padding: 0.3rem;
}

And then just add it to the new element (also, don't use .innerHTML when your string doesn't contain any HTML because .innerHTML has security and performance implications):然后将它添加到新元素(另外,当您的字符串不包含任何 HTML 时不要使用.innerHTML ,因为.innerHTML具有安全性和性能影响):

var reademail = document.createElement('div');
reademail.textContent = '';
reademail.classList.add("readEmail");

In the emaildiv.addEventListener('click', () => view_email(email));emaildiv.addEventListener('click', () => view_email(email)); into the load_mailbox function you are assigning the reference of email object that change in every loop.load_mailbox function 中,您正在分配email object 的引用,该引用在每个循环中都会发生变化。 Because in the view_email function you need only the email's id, I suggest you to pass only email.id param to this function, like this:因为在view_email function 中你只需要电子邮件的 ID,我建议你只将email.id参数传递给这个 function,如下所示:

emaildiv.addEventListener('click', () => view_email(email.id));

email.id is a primitive value (a Number) and Javascript pass as value and not as reference. email.id是原始值(一个数字),Javascript 作为值而不是引用传递。 So, the view_email function became:所以, view_email function 变成了:

// View email
function view_email(emailId) {
  console.log(emailId);

  ...

  // GET request
  fetch(`/emails/${emailId}`)
  .then(response => response.json())
  .then(email => {
    
   ...

   });
}

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

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