Search code examples
javascriptphpajaxdatabaseforms

How to submit a form that is the result of an XMLHttpRequest inside an empty div, without page refresh?


I'm trying to make a chatroom like website. But I ran into a big problem relating to AJAX.

I have an empty div in the main page inside which I load all the messages that are found in the database using an XMLHttpRequest. It's all fine so far as I managed to make it work well. But then I wanted to take things to the next level and create an Admin account that would be able to delete any posts in case they contain an inappropriate content. I have added an extra row inside the table that contains a form with a hidden text input which contains the id of the selected row. And I have a button on which I would click, and I would call a file that has the role of deleting the respective entry from the database,

I have tried to submit the form using a regular POST method inside the form without AJAX just to check if it works well on the database. And it works perfectly, so there are no problems in my mysql procedures file. But it refreshes the webpage and I want to avoid that, so AJAX is my only hope, but when I try to use AJAX it just won't work. I believe this is mainly because the form that I use to delete the posts is in another file than the main body of my website.

I tried some different things but nothing seems to work. I have noticed that there aren't many resources regarding the issue I am facing, and I get it, what I want is a bit uncommon as AJAX is most often used to submit text/textarea inputs that are filled by the user and are in a single file. And I already did that myself, in the main page I have a textarea form that I can submit with AJAX without refreshing the page so I kind of understand how it works. But regarding the problem I am facing right now, no resource on the web has proved useful. This thread is the closest to what I am looking for: Submiting a form that is inside a div with AJAX but it doesn't provide any solutions.

So I'll post my code here: The main body of the website is in the file Chatroom.php, here I have the XHR request:

<!DOCTYPE html>
<html>
<body>

function table2(){
    const xhttp = new XMLHttpRequest();
    xhttp.onload = function(){
      document.getElementById("scrollchat").innerHTML = this.responseText;                        
    }
    xhttp.open("GET", "load_chat.php");
    xhttp.send();
  }

  setInterval(function(){
    table2();
  }, 500);

<div id="scrollchat">

</div>

Which is nothing fancy just the classical code which works perfectly fine, the div that I am filling is: <div id="scrollchat"> \</div>

And then in the load_chat.php where I read the contents from the database I have a form that contains the ID of each message which is the following:

<?php
session_start();
//echo '12345';
$con = mysqli_connect('localhost', 'root', '');

mysqli_select_db($con, 'chatroom');
//echo $_SESSION['username1'];
$onlinevalue = 1;

$count = "SELECT * FROM mainchat";

$rows = mysqli_query($con, $count);
$number = mysqli_num_rows($rows);
?>

<table>
<?php 
foreach($rows as $row) : ?>

<td>
$currentuser = $row["Username"];
echo $currentuser;
</td>
<td> 
 <form action="" id="deletepostform">
 <input type="hidden" name="MessagedeleteID" value="<?php echo $row["ID"]; ?>" />
 <button type="submit"> <b><span style="color:red">X</span></b></button> 
 </form>
 </td>
<?php endforeach; ?>
</table>

What would be the best AJAX function that would allow me to submit a form that is returned by this function?

Back in my main page Chatroom.php I tried a lot of types of AJAX calls. What I tried last time is the following code:

document.getElementById('deletepostform').addEventListener('submit', function(event) { event.preventDefault(); // Prevent the default form submission
    let xhr = new XMLHttpRequest();
    let url = 'deletepost.php'; // Replace with your server endpoint
    xhr.open('POST', url, true);
    xhr.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded');
    
    // Collect form data
    let formData = new FormData(this);
    let params = new URLSearchParams(formData).toString();
    
    xhr.onreadystatechange = function() {
        if (xhr.readyState === 4 && xhr.status === 200) {
            console.log(xhr.responseText); // Handle the response
        }
    };
    
    xhr.send(params); // Send the request with the form data
    });

And with this code I get the error

TypeError: Cannot read properties of null (reading 'addEventListener')

in the console. And I think this may happen because it doesn't find the form in the main page.

Anything I try can't submit it because it does not find the actual form in the page itself as it is loaded from somewhere else


Solution

  • There are a couple of issues in your attempt to handle the form submission:

    1. As you correctly worked out, the JavaScript can't find your form in order to bind the submit event to it. This is because when the JS code runs, your form doesn't exist yet - it hasn't been loaded from the AJAX request.

    2. Even if it could detect it, you're using a loop to potentially generate multiple forms (one for each row). Therefore using an id to identify the form doesn't make sense, because an ID must identify exactly one element (just like an ID in any walk of life must uniquely identify something). So your JS script would only ever find the first instance of the form which had that ID.

    The solutions to those are as follows:

    1. Use event delegation to create the "submit" handler. This technically attaches the event handler to something further up the DOM hierarchy - you can even just use the HTML document's root - which will always be present in the page. Then write code inside the handler to only process the event if the real item which was clicked on (in this case one of your forms) matches a selector you've used to identify it.

    2. Use a class rather than an ID to identify the form. A class can legitimately be attached to as many elements as you like, and it's just as easy to use a class selector in your code as an ID selector.

    Here's a demonstration:

    document.addEventListener('submit', function(event) {
      if (event.target.matches('.deletepostform')) {
        event.preventDefault(); //prevent normal postback
        let formData = new FormData(event.target);
        let params = new URLSearchParams(formData).toString();
        console.log(params); //just to demonstrate it gets the value correctly
        //the rest of your ajax code goes here
    
      }
    });
    <table>
      <tr>
        <td>
          <form action="" class="deletepostform">
            <input type="hidden" name="MessagedeleteID" value="1" />
            <button type="submit"> <b><span style="color:red">X</span></b></button>
          </form>
        </td>
      </tr>
      <tr>
        <td>
          <form action="" class="deletepostform">
            <input type="hidden" name="MessagedeleteID" value="2" />
            <button type="submit"> <b><span style="color:red">X</span></b></button>
          </form>
        </td>
      </tr>
      <tr>
        <td>
          <form action="" class="deletepostform">
            <input type="hidden" name="MessagedeleteID" value="3" />
            <button type="submit"> <b><span style="color:red">X</span></b></button>
          </form>
        </td>
      </tr>
      <tr>
        <td>
          <form action="" class="deletepostform">
            <input type="hidden" name="MessagedeleteID" value="4" />
            <button type="submit"> <b><span style="color:red">X</span></b></button>
          </form>
        </td>
      </tr>
    </table>

    Because the if (event.target.matches('.deletepostform')) code doesn't run until something was actually clicked on, it means it will detect the form even if it was added to the page by AJAX later in the page's lifecycle. It doesn't need to exist when the "submit" handler was first created.