【问题标题】:JavaScript: Prepend element to DOM and inherit event handlerJavaScript:将元素添加到 DOM 并继承事件处理程序
【发布时间】:2018-01-26 19:30:43
【问题描述】:

我在<form> 中有一个<textarea> 和一个<button>。提交后,我致电e.preventDefault() 并通过AJAX 提交表单。从那里我返回查询和 PREPEND 在此列表顶部的 <div> 内的信息。

另外,我赋予了每个项目被删除的能力,这在客户端是即时的,但也通过AJAX 提交表单以被完全删除。这有点工作。

我能够:

  • 有一个空白屏幕(没有添加任何项目),创建一个并删除它没有问题
  • 有空白屏幕,添加两个项目,删除最新的项目没有问题,但删除第二个项目(这是第一个或最旧的项目)会返回错误。它试图删除自己和最新的项目。所以如果我有三个,它会删除自己和最新的,只留下第 2 项。添加的项目越多,情况就越糟糕。

我需要做什么

  • 让新的 Prepended 元素继承事件处理程序
  • 仅删除选中的项目

代码说明

当用户加载页面时,存储在数据库中的项目会立即被查询并添加到屏幕上。

继续在第一个代码示例中找到const delPostFunc。这是一个立即调用的匿名函数,以确保为最初添加到屏幕的任何项目分配click 事件处理程序。

当用户通过submitPostBtn.addEventListener('click', e => { 提交新项目时,在第一个示例的底部,会进行两次调用。在第二个示例中为const submitPostAJAX,在第二个示例中为const returnNewestPostAJAX。这个returnNewestPost调用从数据库返回一些DATA,恰好是最新插入的项目,然后它PREPENDS这个项目到列表的顶部,displayPostWrapper.prepend(newPostDiv);最后调用delPostFunc();函数试图将事件处理程序重新分配给新插入的项目。这是因为innerHTML 删除了任何应该在元素上的事件处理程序,或者这就是我所相信的。

JavaScript

// DELETE POST VARIABLES
let deletePostBtn      = document.querySelectorAll('button[name="delete_post"]');
const displayPostWrapper = document.querySelector('.col-8.pt-4');
let displayPostSection = document.querySelectorAll('.col-8.pt-4 .row');
let postID             = document.querySelectorAll('#delete-post-id');

// SUBMIT POST VARIABLES
const submitPostBtn = document.querySelector('#submit-post-button');
const submitPostID  = document.querySelector('#submit-post-id');
const submitPostContent  = document.querySelector('#submit-post-content');
const submitPostName  = document.querySelector('#submit-post-name');

// MAKING THE CALL TO DELETE THE POST
const delPostFunc = () => {
  console.log(deletePostBtn);
  deletePostBtn = document.querySelectorAll('button[name="delete_post"]');
  console.log(deletePostBtn);

  if (deletePostBtn) {  
    for (let i = 0; i < deletePostBtn.length; i++) {
      deletePostBtn[i].addEventListener('click', e => {
        e.preventDefault();
        postID = document.querySelectorAll('#delete-post-id');
        displayPostSection = document.querySelectorAll('.col-8.pt-4 .row');

        console.log(postID[i].value);


        // ${postID[i]} comes from `const postID` at the top
        deletePostPromise('http://localhost/mouthblog/ajax/delete_post.ajax.php', `id=${postID[i].value}`);

        console.log(deletePostBtn);
        displayPostSection[i].remove();
        console.log(deletePostBtn);
      });
    }
  }
}
// CALL `delPostFunc()` FOR THE INITIAL `deletePostBtn` ON SCREEN
delPostFunc();

// MAKING CALL TO SUBMIT NEW POST
if (submitPostBtn) {
  submitPostBtn.addEventListener('click', e => {
    e.preventDefault();

    // SUBMIT POST
    submitPost('http://localhost/mouthblog/ajax/submit_post.ajax.php',
      `id=${submitPostID.value}&name=${submitPostName.value}&content=${submitPostContent.value}`)
      .then(() => {
        // RETURN THAT SAME POST
        returnNewestPost('http://localhost/mouthblog/api/newest_post.php')
          .then(data => {
            // INSERT POST INTO DOM
            const newPostDiv = document.createElement('div');
            newPostDiv.setAttribute('class', 'row');
            newPostDiv.innerHTML = `
                                    <article class="col-10 offset-1">
                                      <h2 class="h2">${data.user_name}</h2>
                                      <small>${data.date_created}</small>
                                      &nbsp;
                                      &nbsp;
                                      <form action="//localhost/mouthblog/blog.php" method="POST">
                                        <button class="btn btn-danger" name="delete_post" type="submit">DELETE</button>
                                        <input id="delete-post-id" name="post_id" type="hidden" value="${data.id}">
                                      </form>
                                      <hr>
                                      <p class="lead">${data.content}</p>
                                    </article>
            `;
            console.log(`INSERTING ${data.id}`);

            displayPostWrapper.prepend(newPostDiv);

            console.log(`INSERT ${data.id} COMPLETE`);

            // GIVE THE `newPostDiv`'s `delete button` THE CLICK EVENT HANDLER
            console.log(`RUNNING delPostFunc()`);

            delPostFunc(); // BOOM!

            console.log(`delPostFunc() COMPLETE`);
          });
      });
  });
}

这些只是 AJAX 的承诺

// GET REQUEST TO RETRIEVE EVERY POST
const get = (url) => {
  return new Promise((resolve, reject) => {
    const xhttp = new XMLHttpRequest();

    xhttp.open('GET', url, true);

    xhttp.onload = () => {
      if (xhttp.status == 200) {
        resolve(JSON.parse(xhttp.response));
      } else {
        reject(xhttp.statusText);
      }
    };

    xhttp.onerror = () => {
      reject(xhttp.statusText);
    };

    xhttp.send();
  });
}

// DELETE SPECIFIC POST
const deletePostPromise = (url, postID) => {
  return new Promise((resolve, reject) => {
    const xhr = new XMLHttpRequest();

    xhr.open('POST', url, true);

    xhr.onload = () => {
      if (xhr.status == 200) {
        console.log('if (xhr.status == 200)');
        resolve();
      } else {
        reject(xhr.statusText);
      }
    };

    xhr.onerror = () => {
      reject(xhr.statusText);
    };

    xhr.setRequestHeader('Content-type', 'application/x-www-form-urlencoded');
    xhr.send(postID);
  });
}

// SUBMIT A NEW POST
const submitPost = (url, user_id, user_name, content) => {
  return new Promise((resolve, reject) => {
    const xhr = new XMLHttpRequest();

    xhr.open('POST', url, true);

    xhr.onload = () => {
      if (xhr.status == 200) {
        console.log('resolving');
        resolve();
      } else {
        reject(xhr.statusText);
      }
    };

    xhr.onerror = () => {
      reject(xhr.statusText);
    };

    xhr.setRequestHeader('Content-type', 'application/x-www-form-urlencoded');
    xhr.send(user_id, user_name, content);
  });
};

// RETURN THE NEWEST BLOG POST
const returnNewestPost = (url) => {
  return new Promise((resolve, reject) => {
    const xhr = new XMLHttpRequest();

    xhr.open('GET', url, true);

    xhr.onload = () => {
      if (xhr.status == 200) {
        console.log('resolving');
        resolve(JSON.parse(xhr.response));
      } else {
        reject(xhr.statusText);
      }
    };

    xhr.onerror = () => {
      reject(xhr.statusText);
    };

    xhr.send();
  });
}

【问题讨论】:

    标签: javascript arrays ajax event-handling dom-events


    【解决方案1】:

    这个问题最简单的答案是使用Event Delegation重写脚本。

    事件委托允许我们将单个事件侦听器附加到父元素,该事件侦听器将为匹配选择器的所有后代触发,无论这些后代现在存在还是将来添加。

    比较来自 OP 的脚本并比较这个。重写后的脚本代码更少、循环更少、变量更少,并且更易于维护和阅读。

    如果你想比较细节,事件委托从if (displayPostWrapper &amp;&amp; submitPostBtn) {开始

    重写的JS

    const submitPostBtn = document.querySelector('#submit-post-button');
    const submitPostID  = document.querySelector('#submit-post-id');
    const submitPostContent  = document.querySelector('#submit-post-content');
    const submitPostName  = document.querySelector('#submit-post-name');
    
    const displayPostWrapper = document.querySelector('.col-8.pt-4');
    
    // GET REQUEST TO RETRIEVE EVERY POST
    const get = (url) => {
      return new Promise((resolve, reject) => {
        const xhttp = new XMLHttpRequest();
    
        xhttp.open('GET', url, true);
    
        xhttp.onload = () => {
          if (xhttp.status == 200) {
            resolve(JSON.parse(xhttp.response));
          } else {
            reject(xhttp.statusText);
          }
        };
    
        xhttp.onerror = () => {
          reject(xhttp.statusText);
        };
    
        xhttp.send();
      });
    }
    
    // DELETE SPECIFIC POST
    const deletePostPromise = (url, postID) => {
      return new Promise((resolve, reject) => {
        const xhr = new XMLHttpRequest();
    
        xhr.open('POST', url, true);
    
        xhr.onload = () => {
          if (xhr.status == 200) {
            console.log('if (xhr.status == 200)');
            resolve();
          } else {
            reject(xhr.statusText);
          }
        };
    
        xhr.onerror = () => {
          reject(xhr.statusText);
        };
    
        xhr.setRequestHeader('Content-type', 'application/x-www-form-urlencoded');
        xhr.send(postID);
      });
    }
    
    // SUBMIT A NEW POST
    const submitPost = (url, user_id, user_name, content) => {
      return new Promise((resolve, reject) => {
        const xhr = new XMLHttpRequest();
    
        xhr.open('POST', url, true);
    
        xhr.onload = () => {
          if (xhr.status == 200) {
            console.log('resolving');
            resolve();
          } else {
            reject(xhr.statusText);
          }
        };
    
        xhr.onerror = () => {
          reject(xhr.statusText);
        };
    
        xhr.setRequestHeader('Content-type', 'application/x-www-form-urlencoded');
        xhr.send(user_id, user_name, content);
      });
    };
    
    // RETURN THE NEWEST BLOG POST
    const returnNewestPost = (url) => {
      return new Promise((resolve, reject) => {
        const xhr = new XMLHttpRequest();
    
        xhr.open('GET', url, true);
    
        xhr.onload = () => {
          if (xhr.status == 200) {
            console.log('resolving');
            resolve(JSON.parse(xhr.response));
          } else {
            reject(xhr.statusText);
          }
        };
    
        xhr.onerror = () => {
          reject(xhr.statusText);
        };
    
        xhr.send();
      });
    }
    
    // MAKING THE CALL TO DELETE THE POST
    if (displayPostWrapper && submitPostBtn) {
      displayPostWrapper.addEventListener('click', e => {
        if (e.target && e.target.nodeName == 'BUTTON') {
          e.preventDefault();
    
          const row    = e.target.parentElement.parentElement.parentElement;
          const form   = e.target.parentElement;
          const postID = e.target.parentElement.childNodes[3].value;
    
          deletePostPromise('http://localhost/mouthblog/ajax/delete_post.ajax.php', `id=${postID}`);
    
          row.remove();
        } // if
      }); // click event
    
      submitPostBtn.addEventListener('click', e => {
        e.preventDefault();
    
        submitPost('http://localhost/mouthblog/ajax/submit_post.ajax.php',
          `id=${submitPostID.value}&name=${submitPostName.value}&content=${submitPostContent.value}`)
        .then(() => {
          returnNewestPost('http://localhost/mouthblog/api/newest_post.php')
            .then(data => {
              console.log(data);
    
              const newPost = document.createElement('div');
    
              newPost.setAttribute('class', 'row');
              newPost.innerHTML = `
                                <article class="col-10 offset-1">
                                  <h2 class="h2">${data.user_name}</h2>
                                  <small>${data.date_created}</small>
                                  &nbsp;
                                  &nbsp;
                                  <form action="//localhost/mouthblog/blog.php" method="POST">
                                    <button class="btn btn-danger" name="delete_post" type="submit">DELETE</button>
                                    <input id="delete-post-id" name="post_id" type="hidden" value="${data.id}">
                                  </form>
                                  <hr>
                                  <p class="lead">${data.content}</p>
                                </article>
              `;
    
              displayPostWrapper.prepend(newPost);
            }) // then
        }) // then
      }); // click event
    } // if
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2010-11-11
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2011-08-13
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多