JavaScript: Prepend element to DOM and inherit event handler

寵の児 提交于 2019-12-24 20:22:03

问题


I have a <textarea> and a <button> inside of a <form>. When submitted, I call e.preventDefault() and submit the form via AJAX. From there I return the query and PREPEND at information inside of a <div> at the top of this list.

Also, I have given each item the ability to be deleted, which is instant on the client side but also submits a form via AJAX to be completely removed. This is sort of working.

I am able to:

  • Have a blank screen (no items added), create one and delete it no problems
  • Have blank screen, add two items, delete the NEWEST item with no problems but deleting the second item (which was the first, or oldest item) returns an error. It's trying to delete itself and the NEWEST item. So if I have three, it will delete itself and the newest, leaving item #2 all alone. This just gets worse the more items that are added.

What I need to do

  • Have newly Prepended element inherit the event handler
  • Only remove the item that is selected

Code Explanation

When the user loads the page, items that are stored in the database are immediately queried and added to the screen.

Go ahead and find const delPostFunc in the first code example. This is an anonymous function that is called immediately, to ensure that any items that are initially added to the screen are assigned the click event handler.

When a user submits a new item via submitPostBtn.addEventListener('click', e => {, at the bottom of the first example, two calls are made. One to const submitPost, AJAX in the second example, and const returnNewestPost, AJAX in the second example. This returnNewestPost call returns some DATA from the database, which just so happens to be the newest item inserted, and then it PREPENDS this item to the top of the list, displayPostWrapper.prepend(newPostDiv); and finally calls the delPostFunc(); function in an attempt to reassign the event handler to newly inserted items. This is because innerHTML removes any event handlers that are supposed to be on an element, or that is what I am lead to believe.

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

These are the promises for the AJAX just incase

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

回答1:


The simplest answer to this question is to rewrite the script using Event Delegation.

Event delegation allows us to attach a single event listener, to a parent element, that will fire for all descendants matching a selector, whether those descendants exist now or are added in the future.

Compare the script from the OP and compare this one. The rewritten script has less code, less loops, less variables and is a lot easier to maintain and read through.

If you would like to compare specifics, event delegation starts on the line with if (displayPostWrapper && submitPostBtn) {

Re-written 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


来源:https://stackoverflow.com/questions/48468289/javascript-prepend-element-to-dom-and-inherit-event-handler

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!