How to properly use XMLHttpRequest with Bootstrap 5 (client) Validation via Javascript (in Laravel package)
I'm working on a Captcha package for Laravel and it's working fine with server-side validation. In addition I want also to implement the client direct validation via Bootstrap (Javascript) without submitting if it is invalid.
My targets:
- I want to use plain javascript (no jQuery) for the package
- The BS client-side validation should be extended with the xhr call and validating before submitting the form
- The messages should come from the backend (Laravel) to be injectable to the page to support multiple languages
On my internet research I found a similar question, with the idea for adding the xhr call in the else statement of the starter example of Bootstrap's javascript validation code. But this is not working properly, because the form is submitted before the captcha was validated by the client - only 'required' statement is being processed. (See screenshots of the workflow down below).
Similar question >>> https://stackoverflow.com/questions/48769374/bootstrap-4-client-validation-before-ajax-submit
What I did so far, I used the starter example from Bootstrap website and added a XHR call to give it a shot.
JS-Code:
// Example starter JavaScript for disabling form submissions if there are invalid fields
(function () {
'use strict'
// Fetch all the forms we want to apply custom Bootstrap validation styles to
var forms = document.querySelectorAll('.needs-validation')
// Loop over them and prevent submission
Array.prototype.slice.call(forms)
.forEach(function (form) {
form.addEventListener('submit', function (event) {
if (!form.checkValidity()) {
event.preventDefault()
event.stopPropagation()
} else {
// XMLHttpRequest ---------------------------------------------- START
try {
var xhr = new XMLHttpRequest();
let result = new Promise((resolve, reject) => {
xhr.onreadystatechange = (e) => {
if (xhr.readyState !== 4) {
return;
}
if (xhr.status === 200) {
console.log('RESPONSE:', xhr.responseText);
let result = JSON.parse(xhr.responseText);
console.log(result);
if (result['valid'] === 'false') {
alert('FALSE');
event.preventDefault()
event.stopPropagation()
} else {
alert('TRUE >> SUBMITTING form...');
}
} else {
alert('request_error');
}
};
let codeInput = document.getElementById("captcha-code");
const url = '/captcha-validate/' + codeInput.value;
xhr.open('GET', url);
xhr.send();
});
} catch (e) {
//handle error
alert(e);
event.preventDefault()
event.stopPropagation()
}
// XMLHttpRequest ---------------------------------------------- END
}
form.classList.add('was-validated')
}, false)
})
})()
Source: Bootstrap 5 docs >>> https://getbootstrap.com/docs/5.1/forms/validation/
Images of workflow:
Loaded form:

Pressed 'Submit' once, no postback occurred, BS updated the form with validation - all looking good:

After typing in dummy text and the first char of the code, it got "valid". Also no postback occurred, since now.

Here I want to have the xhr call is being automatically done in the background and the fetched result from the server should be represented there -> that the code is indeed invalid! On a last click on submit, the form should then be posted to the server finally (to possibly send a contact mail or similar).
When 'Submit' was clicked, the server returned the result properly and the above JS code is alerting "FALSE" as expected. After closing the alert, the form is being reloaded without any validation an looks like the first image, like a fresh pageload...
Result from server: The returned result is in JSON format and will look like this:
{
"code" : "9", // the sent code
"valid" : "false", // the result (is valid or not)
"message" : "Incorrect code" // returned error message from server
}
Probably I need to inject a custom rule to Bootstrap in any way, but how?
Another way of doing this could be storing the word in a hidden field and check it in any way, but imho this would be not effective for intelligent spiders and spam mailers protection, which it is designed for...
What is the proper way of doing this? I want to provide a clear solution with this package.
So, any hint really appreciated. Thank you guys. :)
Please or to participate in this conversation.