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:
Ajax Form submit with preventDefault (jQuery)
preventDefault() on $.ajax complete (jQuery, and its pilgrimage of dup dups)
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 form
–submit
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 form
–submit
button after the AJAX response JS-alters the div
containing that form
with the same id
.
Workflow:
- HTML ->
<form id="same">
(success) <form id="same">
-> AJAX (success, first AJAX call)- AJAX ->
<form id="same">
(success, first AJAX response) <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 form
–submit
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 form
–submit
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 ); ... }