Skip to content
Advertisement

AJAX infinite preventDefault() on AJAX-responded form (vanilla JS)

I have reviewed several great Questions along the way, so many that I earned my badge for 40 votes in one day. But, nearly all Questions use a non-Vanilla dependency like jQuery or don’t address my specific need. Here are two examples of wonderful, yet to-my-sorrow unrelated, Questions:

I’m surprised this Question has not been asked because I don’t regard myself to be so imaginative. If this Question is already out there, I want to please open a Meta discussion on how to update that Question so searches can find it.

The situation:

This is Vanilla JS, no dependencies.

The script uses preventDefault() to interrupt a formsubmit button identified by id and call AJAX instead. AJAX works and changes content correctly. Then, the problem begins…

This AJAX responds with a replaced div containing the same form that preventDefault() interrupts. But, AJAX doesn’t recognize that updated formsubmit button after the AJAX response JS-alters the div containing that form with the same id.

Workflow:

  1. HTML -> <form id="same"> (success)
  2. <form id="same"> -> AJAX (success, first AJAX call)
  3. AJAX -> <form id="same"> (success, first AJAX response)
  4. <form id="same"> -> AJAX (broken, second AJAX call)

It works the first time only; I need it to work infinitely.

Note, I need to process this specific form. Other elements, including possible other forms, may be included in this div that AJAX changes.

The code:

index.php:

(AJAX adapted from MDN – Sending forms through JavaScript: Using FormData bound to a form element)

  <script>
  window.addEventListener( "load", function () {
    function sendData() {
      const AJAX = new XMLHttpRequest(); // AJAX handler
      const FD = new FormData( form ); // Bind to-send data to form element

      AJAX.addEventListener( "load", function(event) {
        document.getElementById("ajax_changes").innerHTML = event.target.responseText;
      } ); // Change HTML on successful response
      AJAX.addEventListener( "error", function( event ) {
        document.getElementById("ajax_changes").innerHTML =  'Oops! Something went wrong.';
      } );
      AJAX.open( "POST", "ajax_responder.php" ); // Send data, ajax_responder.php can be any file or URL
      AJAX.send( FD ); // Data sent is from the form

    } // sendData() function

    const form = document.getElementById( "ajaxForm" ); // Access <form id="ajaxForm">, id="ajaxForm" can be anything
    form.addEventListener( "submit", function ( event ) { // Takeover <input type="submit">
      event.preventDefault();
      sendData();
    } );

  } );
  </script>

  <div id="ajax_changes">Replace me with AJAX<br>
    <form id="ajaxForm">
      <input type="text" value="AJAX" name="foo">
      <input type="text" value="5" name="bar">
      <input type="submit" value="Form AJAX!">
    </form>
  <!-- Possible other HTML elements and/or forms I do not want to process with that same AJAX -->
  </div>

ajax_responder.php :

if ($_SERVER['REQUEST_METHOD'] === 'POST') {
  $foo = $_POST['foo'];
  $bar = $_POST['bar'];

echo '
'.$foo.'
<br>
'.$bar.'
<br>
<form id="ajaxForm">
  <input type="text" value="'.$foo.'" name="foo">
  <input type="text" value="'.$bar.'" name="bar">
  <input type="submit" value="Form AJAX!">
</form>
<!-- Possible other HTML elements and/or forms -->
';
}

The question

What is the “right” way to get AJAX to keep acting on the same response-contained formsubmit element?

I have considered ways that do not respond with the same form:

  • Having AJAX only respond and change specific, small elements
  • Relocating the form outside of the AJAX-changed content

But both of those seem complicated.

There must be some way to tell AJAX to preventDefault() on a formsubmit button after that button has been updated by AJAX. I just don’t know what that way is.

Or, is there another solution that still involves Vanilla JS?

Advertisement

Answer

You are binding the eventListener to the <form> ajaxForm. Then you replace it (= remove <form> from DOM and add new <form> to the DOM). In that case the eventListener is also removed.

To have an eventListener on the new form, you can either:

• Replace only the content of the <form>. In that case the DOM element isn’t removed and so the eventListener keeps intact (don’t forget to remove the <form> tag in the php response). This is imho the easiest way.

document.getElementById("ajaxForm").innerHTML=
                             event.target.responseText;

• Bind a new eventListener everytime the <form> is changed:

AJAX.addEventListener( "load", function(event) {
        document.getElementById("ajax_changes").innerHTML =
                             event.target.responseText;
//=====> bind new eventListener here <======
      } );

You can create a function that can be called on load as well in the ajax response

function addListenerToForm(){
    document.getElementById( "ajaxForm" ).addEventListener( "submit", function ( event ) {
       event.preventDefault();
       sendData();
    });
  }

• bind the eventListener once to the container <div>

document.getElementById( "ajax_changes" ).addEventListener( "submit", function ( event ) {
  event.preventDefault();
  sendData();
});

function sendData(){
   const AJAX = new XMLHttpRequest(); // AJAX handler
   const form = document.getElementById( "ajaxForm");               
   const FD = new FormData( form );
   ...
   }
User contributions licensed under: CC BY-SA
6 People found this is helpful
Advertisement