Adding elements to the DOM with JavaScript and inheriting event handlers

In my web application, I have set up a form with a textarea and a button. Upon submission, I prevent the default action using e.preventDefault() and submit the form via AJAX. The returned query is then prepended as information inside a div at the top of a list.

Additionally, each item has the ability to be deleted instantly on the client side and also through an AJAX call for complete removal. While this functionality is mostly working, there are some issues.

Current Functionality:

  • Creating and deleting a single item works fine
  • Adding two items, then deleting the newest item works fine, but trying to delete the oldest item results in an error. It attempts to delete itself along with the newest item, leaving the second item alone.

What Needs to Be Done:

  • Ensure that newly prepended elements inherit the event handler
  • Delete only the selected item without affecting others

Code Explanation:

When the page loads, items from the database are queried and displayed on the screen.

In the provided JavaScript code snippet, you can find the function delPostFunc which handles deletion of posts. This function is immediately called to assign click event handlers to existing items upon initial load.

For submitting a new item, the code attaches event listeners to handle the submission process via AJAX. The returned data (newest post) is prepended to the list, and the event handler is reassigned to include the newly added item.

Javascript Code Snippet:

// Specific functions related to post deletion and submission
// These functions utilize promises and AJAX requests

// GET Request Function
const get = (url) => {
  // Promise implementation for HTTP GET request
}

// Delete Post Promise Function
const deletePostPromise = (url, postId) => {
  // Promise implementation for post deletion via AJAX
}

// Submit Post Promise Function
const submitPost = (url, userId, userName, content) => {
  // Promise implementation for submitting a new post via AJAX
}

// Return Newest Post Promise Function
const returnNewestPost = (url) => {
  // Promise implementation for retrieving the newest blog post via AJAX
}

Answer №1

To simplify this issue, the best course of action would be to refactor the script using the concept of Event Delegation.

With event delegation, a single event listener is attached to a parent element, triggering for all descendants matching a selector, regardless of whether those descendants currently exist or are added later on.

Take a look at the original script and compare it with the rewritten version provided below. The updated script boasts reduced code complexity, fewer loops, fewer variables, and overall offers better readability and maintainability.

If you want to delve into the specifics, the event delegation begins at the line containing

if (displayPostWrapper && submitPostBtn) {

Revised JavaScript Code

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

// Perform GET REQUEST TO FETCH ALL POSTS
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 A 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);
  });
};

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

// INITIATE DELETION OF A 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

Similar questions

If you have not found the answer to your question or you are interested in this topic, then look at other similar questions below or use the search

Centralizing npm Modules in Node.js: A Comprehensive Guide

I've encountered difficulties while trying to install node-canvas using npm on my Windows system. I've had to run multiple tests, tweaking environment variables, parameters, and more... What's frustrating is that whenever I run 'npm in ...

Employing a lexicon of hexadecimal color code values to harmonize CSS styles

I have a unique requirement where I need to utilize a dictionary of HTML color codes and then apply those colors as styles. It's an interesting challenge! Here is an example of how my color dictionary looks like: const colorCodes = { red: ...

Array index exceeded boundary in C#

When trying to add items to a ListBox using ListBox.Items.Add(), I am encountering an error indicating that the index is out of bounds of the array. Surprisingly, if I utilize MessageBox.Show() instead of ListBox.Items.Add(), the error does not occur. str ...

What are the steps for creating interactive web applications using Ajax in the Scala programming language

Seeking a web framework that is object oriented for developing Ajax applications in Scala, without having to mix Java and Scala code like GWT. An example of how Scala can create a vertical layout with a label and a list: val label = new Label("nothing se ...

How can I customize a default button in HTML to hide the selected option from the dropdown menu?

Hey there! I'm currently working on a website that needs to be bilingual, with Spanish as the default language. I want to include a dropdown button that allows users to translate the content into English. Here's what I've tried so far: ...

Jasmine is having trouble scrolling the window using executeScript

I usually use the following command: browser.driver.executeScript('window.scrollTo(0,1600);'); However, this command is no longer working. No errors are showing in the console, making it difficult to troubleshoot. Interestingly, the same scri ...

Move files into the designated folder and bundle them together before publishing

Is there a way to transfer the files listed in package.json (under the File field) to a specific folder in order to bundle them together with npm publish? Here is the structure of my repository: . ├── package.json └── folder0 ├── fil ...

Heroku Node.js - Cross-Origin Resource Sharing (CORS) is functioning correctly for all API requests except for when using the image upload

Our web application contains numerous APIs that retrieve data from a Node.js server deployed on Heroku. Most of the APIs function correctly, with the exception of one that allows users to upload images to the server. While this API worked flawlessly in o ...

Top method for transferring data between React components post retrieval from Axios Call

I am currently utilizing React JS in an application structured like the following diagram: This particular application fetches data from a Rest API (Node express) using Axios. The challenge I am facing is determining the most effective method for storing ...

React is encountering a problem with loading the image source and it is

<img src="play.jpg" alt="play"></img> This code works fine in standard HTML, but I'm having trouble getting it to work in React. Is adding it as a background the only solution? ...

Is my front-end JavaScript fetch request mistakenly being sent as a GET instead of a POST?

On clicking the submit button, a fetch request is triggered. Strangely, in the developer tools, it shows up as a GET request. I tested the request using Insomnia and it returned the handlebars site to me without any of my console logs appearing on either ...

A guide to importing a Vue component in a JavaScript file

I am looking to achieve a specific behavior in my project: depending on a condition stored in my database, I want to load a particular vue.component instead of another. For example, within a file named discover.js, there is the following code: Vue.compone ...

Comparing the features of ASP.NET webforms and ASP.NET Ajax to ASP.NET MVC with the flexibility of Ajax frameworks

Imagine you have the opportunity to choose your path - which one would you go for? ASP.NET Webforms + ASP.NET AJAX or ASP.NET MVC + Pick Your Own JavaScript Framework Are there any specific restrictions or shortcomings when comparing ASP.NET Webfor ...

Utilizing RequireJS with Laravel 4

I am trying to use require js and laravel to manage the assets directory. I have placed the require.js file in the assets/js directory, along with main.js. The main.js file contains: require.config({ baseURL: '', paths: { userPre ...

Creating a Scrollable React Form Group

Struggling to display a large amount of data that is not scrollable, how can I make this form group scrollable to easily view all the data? render() { return ( <div style={{width: '50rem', margin: '1rem&ap ...

What is the best way to transfer item information from Flatlist within a React Native application?

I am a newcomer to react. In my App.js file, I have the main code, and in another folder, there is a lists.js file that contains the list data. I successfully import the list from lists.js into my App.js file. Now, I want to add an onPress action to the ...

Does anyone have tips on how to upload images to MongoDB using React?

Currently, I am working on a project that requires an image upload feature for users. These images need to be stored in MongoDB so that they can be viewed by the user later on. Can anyone offer assistance with this? I have successfully configured my datab ...

Execute a parent action within a Controller

I'm currently working on customizing the update() and create() actions within a specific controller in my Sails.js application. My goal is to upload a file first, store the file path (which is saved on s3 using skipper) in the request body, and then ...

What is the best way to set a CSS background using vue-cli 3?

What is the process for setting a CSS background in vue-cli 3? I have set my vue.config.js like this. Is publicPath properly configured? JavaScript const path = require("path"); module.exports = { devServer: { port: 8081, overlay: { warni ...

Determining the selected number option with jquery

In my HTML page, I have a set of select menus as shown below: <label>Product Details</label> <select id="PD"> <xsl:for-each select="PRODUCT_DETAILS"> <option value="{DATA0}"><xsl:value-of selec ...