Why does my form only submit on the second click? - javascript

Why isn't the form submitted the first time?
What have I done wrong?
If I click submit, the console will display the output (Event check # 0. Event check # 1.)
If I click on the submit button again, the console will display the output (Event check # 0. Event check # 1. Event check # 2.)
And after that the message will be sent to the mail.
".button-f" and "# send-btn-form" are the same button!
//#1grn --- Submitting the form without reloading the page
let sendBtn = document.querySelector(".button-f");
sendBtn.onclick = function(e) {
e.preventDefault(); // Cancels the default browser action
console.log("Event check №0.");
let formName = document.querySelector('#main-form'); // Find our shape
let formData = new FormData(formName); // Create a new FormData-object. We use all fields with the attribute name
let request = new XMLHttpRequest(); // Create a new XMLHttpRequest-object.
request.open('POST', "php/send.php"); // We configure it: POST-request for URL php/send.php
//#1grn --- Filtering characters such as: &<>"'
['name', 'socialname', 'numberfo', 'email', 'text'].forEach(name => {
let input = document.querySelector(`[name='${name}']`);
input.value = escape(input.value);
console.log("Event check №1.");
});
//#1grn --- Check ("real" event or generated by code)
let sendBttn = document.querySelector("#send-btn-form");
sendBttn.addEventListener('click', e => { // We accept the event object into our callback-function, call it "e".
if (e.isTrusted) { // CHECKING, if a e.isTrusted === true - then this is not a machine click.
request.send(formData); // We send HTTP request to the server and get a response.
console.log("Event check №2.");
} else {
console.log('blocked!');
}
});
// This code will work after we receive the server response
request.onload = function() {
if (request.status != 200) { // analyze HTTP-response status, if the status is not 200, then an error has occurred
} else { // if everything went smoothly, clear the form and display the result
formName.reset(formData); // Clearing the form
let on = document.querySelector(".message-good");
on.classList.add('ms-on');
}
};
};

If ".button-f" and "#send-btn-form" are the same button, you are adding an event listener every time you click the button. In that case, you are not executing the code inside that listener, it will be executed the next time you click it. So you could replace this
let sendBttn = document.querySelector("#send-btn-form");
sendBttn.addEventListener('click', e => {
if (e.isTrusted) {
request.send(formData);
} else {
console.log('blocked!');
}
});
with just this
if (e.isTrusted) {
request.send(formData);
} else {
console.log('blocked!');
}

Related

How do I incorporate an AJAX form submission into Bouncer.js?

I need my contact form to validate and have an AJAX submission.
So, I created this great PHP and Vanilla JS AJAX form, finally got it working and then decided to add some validation.
For said validation, I first looked to Validate.js but then found out it was deprecated and a new plugin, Bouncer.js(https://github.com/cferdinandi/bouncer) was to be used.
Now, that being said, Bouncer.js and my AJAX function aren't playing well together. According to the documentation, there's a place to put my AJAX function, I just don't know how to translate it so that the validation and the request both work together.
The documentation:
// Detect a successful form validation
document.addEventListener('bouncerFormValid', function (event) {
// The successfully validated form
var form = event.target;
// If `disableSubmit` is true, you might use this to submit the form with Ajax
}, false);
My AJAX function:
const _ = id => document.getElementById(id);
const submit = _('form1_contact__submit')
const status = _('status')
window.addEventListener("load", function () {
function sendData() {
const XHR = new XMLHttpRequest();
// Bind the FormData object and the form element
const FD = new FormData(form);
status.innerHTML = 'Please wait...'
// Define what happens on successful data submission
XHR.addEventListener("load", function(event) {
// if(event.target.responseText == 'Success');
if(XHR.readyState == 4 && XHR.status == 200) {
contactForm.innerHTML = '<div class="success"><h2>Thanks for getting in touch. Your message has been sent.</h2><p>This page will refresh in <span id="countdown"></span></p>';
contactForm.className += ' flex';
(function countdown(remaining) {
if(remaining === 0)
location.reload(true);
document.getElementById('countdown').innerHTML = remaining;
setTimeout(function(){ countdown(remaining - 1); }, 1000);
})(5);
} else {
status.innerHTML = XHR.responseText;
submit.disabled = false;
}
});
// Set up our request
XHR.open("POST", "/contact");
// The data sent is what the user provided in the form
XHR.send(FD);
}
// Access the form element...
const form = contactForm
// ...and take over its submit event.
form.addEventListener("submit", function (event) {
event.preventDefault();
sendData();
});
});
How do I translate this to work with Bouncer.js? No jQuery. I'm working and really need to learn and understand Javascript. Please and thank you in advance.
//------------------------------// UPDATE //----------------------------//
So, here's my code revisited with some changes to rely on the bouncerFormValid callback. This could be horrendously wrong, so please forgive me for butchering this, but here goes:
const _ = id => document.getElementById(id);
var bouncer = new Bouncer('[data-validate]');
document.addEventListener('bouncerFormValid', function() {
function sendData() {
const submit = _('form1_contact__submit')
const status = _('status')
const XHR = new XMLHttpRequest();
const FD = new FormData(form);
status.innerHTML = 'Please wait...'
XHR.addEventListener('bouncerFormValid', function(event) {
if (event.target.responseText == 'Success');
if (XHR.readyState == 4 && XHR.status == 200) {
contactForm.innerHTML = '<div class="success"><h2>Thanks for getting in touch. Your message has been sent.</h2><p>This page will refresh in <span id="countdown"></span></p>';
contactForm.className += ' flex';
(function countdown(remaining) {
if (remaining === 0)
location.reload(true);
document.getElementById('countdown').innerHTML = remaining;
setTimeout(function() {
countdown(remaining - 1);
}, 1000);
})(5);
} else {
status.innerHTML = XHR.responseText;
submit.disabled = false;
}
});
XHR.open("POST", "/contact");
XHR.send(FD);
}
const form = contactForm
// ...and take over its submit event.
form.addEventListener('submit', function(event) {
event.preventDefault();
sendData();
});
}, false);
The form registers errors and submits, but instead of the desired JS action, I get a page refresh with the PHP success message.
I'm not exactly sure how this is wrong, but I'm feeling it has something to do with my submit event.
Your ajax call shouldn't be on load. It should go in the bouncerFormValid callback.

How do I prevent multiple duplicate events from firing on a dynamic form?

I have a dynamic form that allows you to add more child records as needed. I'm using the cocoon ruby gem. I got auto-save working on a form using the following code:
<script>
$(document).on('turbolinks:load', function() {
var $form = $('form.autosave');
var saving = false;
function autoSaveForm(actionPath) {
var data = $form.serialize();
console.log("POST: " + actionPath);
var request = new XMLHttpRequest();
request.open('POST', actionPath, true);
request.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded; charset=UTF-8');
request.send(data);
request.onreadystatechange = function () {
if(request.readyState === XMLHttpRequest.DONE && request.status === 200) {
saving = false;
}
};
}
$form.on('blur', 'input', function() {
if (saving) {
return; // don't do the blur action
}
saving = true;
var actionPath = $form.attr('action');
autoSaveForm(actionPath);
});
});
</script>
The "blur" event is being triggered two times. Here's my console output:
POST: /user/profile VM1717:9
POST: /user/profile VM1717:9
I've updated my code from #samanime's suggestion to add a boolean to prevent the event being triggered 4 times. Now it is only triggered twice. Progress!
It appears the issue is with turbolinks and how I setup my JS.
Add a boolean to track if you're currently saving, and then don't do it again if you are.
var saving = false;
$form.on('blur', function () {
if (saving) {
return; // don't do the blur action
}
saving = true;
// do saving stuff
});
And then you'll need to watch when you're done saving, and then set saving back to false.
function autoSaveForm(actionPath) {
// other stuff
request.onreadystatechange = function () {
if(request.readyState === XMLHttpRequest.DONE && request.status === 200) {
saving = false;
}
};
}
If you wanted to, you could also take that a step farther and have another boolean (like needsToSave) which would get marked true if you tried to save while saving, and then immediately start saving the latest stuff as soon as the first saving was done.

Page doesn't respond to HREF until AJAX completes asynchronous fetch

I have an ajax request executing through the XMLHttpRequest() object
My AJAX method is called in this format:
var xmlhttp = new XMLHttpRequest();
$(document).ready(function () { LoadData(); });
function LoadData()
{
var parameters = "DisplayData=true";
var url = "default.aspx";
Send(url, parameters, "DisplayData");
CheckForAbort();
}
function Send(url, parameters, QueryType)
{
...
xmlhttp.open("POST", url, true);
xmlhttp.setRequestHeader("Content-type", "application/x-www-form-lencoded");
xmlhttp.send(parameters);
xmlhttp.onreadystatechange = function (){...}
}
There is also a timer on the page which refreshes the data by making a new request through the Send(...) method in intervals of 15 seconds. Every .3 seconds and it calls an Elipsis() method that displays and "blinks" the "loading message" (if appropriate to be displayed) and checks for the abort.
var Color = "red";
function Elipsis() {
if (ResponseMessage != "")
{
if (Color == "darkred") { Color = 'red'; } else { Color = 'darkred'; }
$("#StatusResponse").css("display", "block");
$("#StatusResponse").css("color", Color);
CheckForAbort();
}
}
function CheckForAbort()
{
console.log("MenuActivted: " + MenuActivted);
if (MenuActivted)
{
xmlhttp.abort();
ResponseMessage = "Aborting Request";
MenuActivted = false;
}
}
But when the user clicks the menu bar which is an anchor tag with the HREF set to another page. The browser doesn't respond until the ajax request has completed it's fetch.
The HTML HREF is called the following way on an ASPX page:
<%#Eval("Text")%>
the Ajax Abort sets the flag that is checked in the CheckForAbort() method:
var MenuActivted = false;
function AbortAjax()
{
MenuActivted = true;
return false;
}
I am running IE 11 on Win 7. I have called an abort() method in another section of the code. which executes the xmlhttp.abort(). The response status and ready state respond (console output below) but the page still waits to respond to the HREF
Console output:
HTML1300: Navigation occurred.
File: ChangePassword.aspx
MenuActivted: true
ReadyState: 4
Status: 0
Does anyone have a solution to this problem?
[Updated **********]
I thought I had the solution but I didn't.
I commented out the set header but although it allowed my HREF to execute it was because the xhr was throwing an error and the fetch was terminating.
xmlhttp.open("POST", url, true);
//xmlhttp.setRequestHeader("Content-type", "application/x-www-form-urlencoded");
xmlhttp.send(parameters);
Please read the entire post before responding.

HTML5 history API: cannot go backwards more than once

I have been trying to get my script working but apparently there is something wrong with it: when I try to go backwards with the browser back button, it stops at the first page backwards i.e. the second time I click the back button, does not work properly and instead updates the current page with itself.
Examples:
homepage -> second page -> third page -> second page -> second page -> second page (and so on)
homepage -> second page -> third page -> fourth page -> third page-> third page (and so on)
This instead works:
homepage -> second page -> homepage
Does anyone have a clue to what I am missing?
var domain = 'http://example.com/';
function updatePage(json){
var postData = JSON.parse(json);
// pushState
var url = domain + postData.url;
var title = postData.title;
document.title = title;
history.pushState({"page": url}, title, url);
// Clears some elements and fills them with the new content
// ...
// Creates an 'a' element that triggers AJAX for the next post
var a = document.createElement('a');
a.innerHTML = postData.next;
a.href = domain + postData.next;
document.getElementById('container').appendChild( a );
listenerAttacher( a );
// Creates another 'a' element that triggers AJAX for the previous post
a = document.createElement('a');
a.innerHTML = postData.previous;
a.href = domain + postData.previous;
document.getElementById('container').appendChild( a );
listenerAttacher( a );
}
function loadPost( resource ){
// Loads post data through AJAX using a custom function
loadHTML( resource, function(){
updatePage( this.responseText );
});
}
function listenerAttacher( element ){
// Adds a click listener to an element.
element.addEventListener('click', function(e){
e.preventDefault();
e.stopPropagation();
loadPost( this.href +'.json' );
return false;
},
false);
}
(function(){
history.replaceState({'page': window.location.href}, null, window.location.href);
// Adds the event listener to all elements that require it.
var titles = document.querySelectorAll('.post-title');
for (var i = 0; i < titles.length; i++){
listenerAttacher( titles[i] );
}
// Adds a popstate listener
window.addEventListener('popstate', function(e){
if ( e.state == null || e.state.page == domain ){
window.location = domain;
}
else {
loadPost( e.state.page + '.json' );
}
}, false);
})();
When you pressed back button, popstate event is fired and loadPost function is called. However in loadPost, history.pushState method is called again, which pushes the current page on the history stack again. Which explains why the first back button works and then it does not.
1) A quick fix is to check if the current state matches the state you are trying to push:
if (!history.state || history.state.page!=url)
history.pushState({ "page": url }, title, url);
2) Event better, you can add parameter to loadPost and updatePage functions to prevent unnecessary pushState calls:
function updatePage(json, disablePushState) {
...
// disablePushState is true when back button is pressed
// undefined otherwise
if (!disablePushState)
history.pushState({ "page": url }, title, url);
...
}
function loadPost(resource, disablePushState) {
// Loads post data through AJAX using a custom function
loadHTML(resource, function (responseText) {
updatePage(responseText, disablePushState);
});
}
...
window.addEventListener('popstate', function (e) {
if (e.state == null || e.state.page == domain) {
window.location = domain;
}
else {
loadPost(e.state.page + '.json', true);
}
return true;
});
Hope this help.

submit search query if conditions are met

How would I go about integrating these two functions together so that when submitting the search form, it will first check the http get response, then depending on whether there was an error or not, either submit the form, or display an error message?
All that I've tried has either made the form not work at all, or not take into account the 'http.get function'.
var http = require("http");
var url = 'http://examplepage.com/';
search.submit(function (event) { // submit search query function
if (searchBox.val().length < 2) {
searchBox.focus();
event.preventDefault();
}
});
http.get(url, function (res) {
res.resume();
// successful - so submit search query
}).on('error', function () {
// unsuccessful - display error message
});
You should probably subscribe on click event for you button that triggers search, the go check the url and inside success handler do
Sample code of Click handler
http.get(url, function (res) {
// successful
if (searchBox.val().length < 2) {
$('your form selector').submit();
}
}).on('error', function () {
// unsuccessful - display error message
});

Categories