Delayed form submit, ajax, & popup blocker - javascript

I have a simple signup form on my site where users enter their email and name.
To avoid having a double signup error, I have a simple Ajax call to see if the email is already there. If so, I do nothing, if not, I submit the form.
My problem is that the form has a target of _blank, so the delayed submit call triggers the popup blocker on browsers.
Now, I know why this is happening - I'm no longer in the trusted click event. I changed the Ajax call to be synchronous, but I'm getting a warning that sync Ajax calls on the main thread are deprecated.
So my current options seem to be:
Use sync Ajax call anyway, but eventually it'll stop working
Remove the target _blank on my form, so it redirects on the same page (bad UX)
???
Is there a better way to get around the popup blockers seeing my delayed form submit as a popup?
Code:
HTML:
<form action="//external-site.com" method="post" target="_blank" onsubmit="onSubmitSignupForm(event)">
...
</form>
JS:
function onSubmitSignupForm( e )
{
e.preventDefault();
var request = new XMLHttpRequest();
request.open( 'POST', 'checkEmail.php', true );
request.setRequestHeader( 'Content-Type', 'application/x-www-form-urlencoded; charset=UTF-8' );
request.onload = onLoad;
request.onerror = onError;
// NOTE: params = urlencoded params recovered from form
request.send( params );
}
function onLoad( e )
{
var request = e.target;
if( request.status == 200 )
{
var data = JSON.parse( request.responseText );
if( data != null && data.success )
{
onFinish( request, data );
return;
}
}
// if we get here, then it means that the request failed, or there was a problem
onError( e );
}
function onError( e )
{
var request = e.target;
var data = JSON.parse( request.responseText );
onFinish( request, data );
}
function onFinish( request, data )
{
var success = ( data != null ) ? data.success : false;
if( !success )
$form.submit(); // blocked by popup
}

OK, so I have a solution, though it may be a little specific to my situation and is helped by the fact that I redirect the user if they're already signed up.
I basically open a new, intermediary page when the form is initially submitted (thus getting around the popup issue as it's in a trusted event) and populate a hidden form on that page (with a link to manually submit it if the user wants). The ajax call is sent, and depending on the results, I either submit the form on page 2, or redirect if they're already signed up.
User submits form on Page1
Stop event, and open Page2, which has the same form, albeit hidden
Populate the form on Page2 with the values from the form on Page1
Show a message on Page2 saying we're working on it, along with a link to manually submit the hidden form (good UX)
On Page1, fire Ajax request to see if the user is already subscribed
When Ajax results come in, 2 things can happen
(a) The user isn't signed up - form on Page2 is submitted (can optionally redirect Page1 to the content)
(b) The user is already signed up - redirect Page2 to the content
The only things to watch out for is to make sure that Page2 is fully loaded before filling the form and the Ajax results come back. For that, I use a simple boolean system with an onload event.
The code for that looks something like this:
function onSubmitSignupForm( e )
{
e.preventDefault();
var $form = e.target;
var name = ...; // get name value from form
var email = ...; // get email value from form
// open a new window that we'll be able to control and redirect in (otherwise, we'll
// get stung with a trying to open a popup, as when we get the result, we'll no longer
// be in the trusted click event)
var win = window.open( '/signup/', '_blank' );
win.onload = function() {
win.setDetails( name, email ); // fill in the hidden form
}
// check if the email is already signed up
// NOTE: result might be null if we couldn't reach the server
checkEmail( name, email, function onFinish( result ){
// set the result in the other window
// NOTE: the window might not have loaded yet
if( typeof win.setResult == 'undefined' )
{
var f = win.onload; // replace the one we already have
win.onload = function(){
if( f )
f();
win.setResult( result );
}
}
else
win.setResult( result );
});
}
Both setDetails and setResult will try to call a third function handleResult that will only continue booleans set by both functions are true.

Related

Validate a Form Submit before submitting and writing a result into a field, that will then be submitted - using Prevent default and Ajax requests

When I press the Form Submit button, I want to action some validation (via an Ajax call), and change a screen value, before the Form is actually submitted.
My issue is that when I try this, and I manually action the Submit button, the screen value is not actually updated until the Form has been submitted. Too late!
Is there any way around this issue? I've tried to comment in the code exactly what I mean.
$("form").submit(function (event) {
// we prevent teh default action of the udpate button
event.preventDefault();
alert('In Jquery/JS.. button pressed / Prevented default');
// variables for "self" so we can access the form
// nativley using javascript and not jquery
var self = this,
root = 'https://jsonplaceholder.typicode.com';
// Now call Ajax, get results, and if good, manually call the
// submit button.
$.ajax({
url: root + '/posts/1',
method: 'GET',
success: function (data) {
alert('in ajax success');
}
}).done(function (data) {
alert('in ajax done - Title data = : ' + data.title);
if (data.title !== "") {
// We assign our Input Text field some data
$('#textfield').val(data.title);
// The on screen value actually hasn't updated :o(
alert('about to self submit');
// We go to submit... but our form isn't actually
// updated with the Ajax text yet...
// is that possible before we action the submit???
self.submit();
}
}).fail(function () {
alert('error');
});
});
See JSFiddle : https://jsfiddle.net/dave_pace/890zmj1f/
It is not what you think. JavaScript is single-threaded. Therefore, an alert statement is executed just after its previous statement. It doesn't wait for the previous statement to finish.
Adding to that, alert is quicker than setting the value in the textbox, and it actually blocks the thread until it is dismissed. That's why you cannot see the value set in the textbox, but can see the submit alert first.
Your fiddle is fine, and works as it should. If you want to test this, try to remove all the alert statements from your code, and try to submit your form to an actual URL, for example https://www.google.com.
Though submitting to Google will result in an error, you can actually see that the textbox is being populated before the submit happens.

Can't see AJAX call in proxy/chrome's dev tools

The following code snippet is used by a coworker to get an URL from a DB and then submit a "virtual" form to that URL.
$.ajax({
url: location.origin + location.pathname + "data/getURL.php",
method: "POST",
data: {
userName: user
},
success: function( data : any, textStatus : string, jqXHR : JQueryXHR){
console.log(data);
var url = (JSON.parse(data)).url;
if(url !== undefined && url !== null && url !== ""){
var sender : HTMLFormElement = document.createElement("form");
sender.setAttribute("action", `http://${url}/receive`);
sender.setAttribute("method", "POST");
var userSenderField = document.createElement("input");
userSenderField.setAttribute("type", "hidden");
userSenderField.setAttribute("name", "user");
userSenderField.setAttribute("value", user);
sender.appendChild(userSenderField);
var passSenderField = document.createElement("input");
passSenderField.setAttribute("type", "hidden");
passSenderField.setAttribute("name", "password");
passSenderField.setAttribute("value", password);
sender.appendChild(passSenderField);
document.body.appendChild(sender);
sender.submit();
Using either Burp Suite or just Chrome's Dev Tools, I can see the call to getURL.php but then I can't see the call to http://url/receive. Why?
For the sake of argument, let's say your ajax call to data/getURL.php succeeded, but delivered bad or unexpected data.
You then end up in your ajax call's success handler.
The success handler immediately creates a new form, populates it with (the bad) data, and submits the form.
This causes a postback to happen.
Chrome's dev tools clear the network panel upon postback by default, also clearing the call to "data/getURL.php", so you never actually saw the call succeed, and could not see in the net panel what it did. (ergo, you had no idea that data/getURL.php delivered the wrong data to you.
if you put a breakpoint in your ajax success handler before it submits the form, you can actually see what is going on.

how to send text value to server without the form submitting using ajax

basically i have a form and in that form i have a username textbox with a submit button.
now what i want is that before we submit the form i want to send the text value to server so the server could check if the username has not been taken by any other user and then submit the form, based on research i had, i found this tutorial useful https://scotch.io/tutorials/submitting-ajax-forms-with-jquery, altough this tutorial is using php for server coding and i am using java servlet but my ajax script never gets to execute.
here is my code:
<html>
<head>
<meta charset="utf-8" name="viewport" content="width=device-width, initial-scale=1">
<script src="//ajax.googleapis.com/ajax/libs/jquery/2.0.3/jquery.min.js"> </script>
<script>
$(document).ready(function() {
// process the form
$('form').submit(function(event) {
// get the form data
// there are many ways to get this data using jQuery (you can use the class or id also)
var formData = {
'username' : $('input[name=UserName]').val(),
};
alert('hello');
// process the form
$.ajax({
type : 'POST', // define the type of HTTP verb we want to use (POST for our form)
url : '../postr', // the url where we want to POST
data : formData, // our data object
dataType : 'json', // what type of data do we expect back from the server
encode : true
})
// using the done promise callback
.done(function(data) {
// log data to the console so we can see
console.log(data);
// here we will handle errors and validation messages
});
// stop the form from submitting the normal way and refreshing the page
event.preventDefault();
});
});
</script>
<form class="Registration_Form" id="Registration_Form" action="../postr" method="POST">
<div id="Registeration_Username_DIV" class="Registeration_Username_DIV">
<input type="text" id="Registeration_Username_box" class="Registeration_Username_box"
name="UserName" onblur="Usernameerrorfunc(this, 'Usernameerror_spn', 'Usernamenowallow_spn');" maxlength="30" onclick="textboxfocus(this)"/>
</div>
<div class="Registration_Submit_Div">
<input type="submit" value="submit" id="SumbitForm_btn" class="SumbitForm_btn" name="Submit_btn"/>
</div>
</form>
<script>function Usernameerrorfunc(field, errordiv, Notallowcharserror_SPN){
if (field.value == '') {
field.style.borderColor = "red";
document.getElementById(Notallowcharserror_SPN).style.visibility = "hidden";
document.getElementById(errordiv).style.visibility = "visible";
} else if(!field.value.match(/^[a-zA-Z0-9~`!##\(\.)]+$/)){
field.style.borderColor = "red";
document.getElementById(Notallowcharserror_SPN).style.visibility = "visible";
document.getElementById(errordiv).style.visibility = "hidden";
} else {
field.style.borderColor = "rgb(150,150,150)";
document.getElementById(errordiv).style.visibility = "hidden";
document.getElementById(Notallowcharserror_SPN).style.visibility = "hidden";
}
}</script>
as you can see in my ajax script i have an alert() which it should pop up hello but it never does
Good Morning!
I think there are several things to say about your code. First of all your submit function:
$('form').submit(function(event) { ... }
Here you want to catch the submit-event when the user hits the button. Everything good, but since your button is of type=submit the browser will also react on the click and handle the submit-process by itself. Your function won't get called properly. To prevent this you have to escape the default behaviour of your form on submitting:
$('form').submit(function(event) {
event.preventDefault();
$.ajax({ ... });
}
This will do the trick to let the browser do what you want instead of handling the submit by itself.
So now your browser can run your ajax call.
Next thing: The ajax-call.
You did many things right, but some important things wrong. Look at the following structure:
$.ajax({
url: 'your_url_to_send_data_to',
type: 'post', //the method to use. GET or POST
dataType: 'json',
data: data, //your data: {key1:value1, key2:value2}
success: function(data) { //handle a successfull response (200 OK)
alert(data);
//here you can do with your data whatever you want
},
error: function(jqXHR, textStauts, errorThrown){ //handle every error. e.g. 500
alert(textStatus + ': '+errorThrown);
}
}});
This will handle the sending and the recieving of your request. The success function will get called if the server returns an 200 OK. Otherwise the error function gets called.
Now you just have to handle the request on server side properly.
Third thing: What's about the real submit after the name-check?
Since you preventDefault() the default browsers action, sou have to do it manually. You could think of triggering the submit again, but you would ran another time in your own function you've written so far.
Therefore you have to do it by your own. But wait! You can combine the two things in one call!
Think about this:
let the user fill your form
let him hit the submit button
preventDefault behaviour of your browser and build a FormData and put all your values in it
prepare your ajax call
send the FormData with your ajax call to your server
Handle name-check and all other things on server-side
answer the request
evalutate the answer in your success function
Note: On server side you have to print the application/json header to let the browser and finally your ajax call handle your answer properly.
Since you want a dynamic check of the user name availability, I suggest you react to the keyup event (note: I also added support for other possible change-incurring events in my demo below) and schedule a check run after a fixed delay. Once the delay transpires, if the user hasn't typed anything in the interim, you can run the AJAX check and update the page; if the user did type something in the interim, you can simply not run the check (yet). This means a check will automatically be run after every flurry of typing, as long as the user ceased typing for at least the hard-coded delay.
With regard to submitting, I would just allow the user to submit the form in the normal way without any last-second AJAX check of user name availability. You're still going to have to perform a server-side check for availability, in case the user disabled JavaScript or somehow constructed their own submit HTTP query, so you may as well depend on that server-side check upon form submission. The dynamic AJAX check is really only beneficial as a quick notification to the user, and so should only be provided if the user edits the user name, and then does not submit the form immediately. Most of the time the user will not submit the form immediately after editing a field, and most users can be relied upon to not submit the form if it is clearly indicated on the page that there is a validation failure.
var USERNAME_CHECK_DELAY = 800;
var userInputValCount = 0;
var userInputVal = '';
window.handlePossibleUserInputChange = function() {
let $userInput = $('#userInput');
let $checkDiv = $('#userCheckLine');
// if this event doesn't reflect a value change, ignore it
if ($userInput.val() === userInputVal) return;
userInputVal = $userInput.val();
// update the value count
let userInputValCountCapture = ++userInputValCount; // closure var
// schedule a potential check run
setTimeout(function() {
// only check the current name if the user hasn't typed since the provoking event
if (userInputValCountCapture !== userInputValCount) return;
checkUsername();
},USERNAME_CHECK_DELAY);
// update the status message
if ($userInput.val().trim() === '') {
$checkDiv.text('');
} else {
$checkDiv.attr({'class':'checking'});
$checkDiv.text('checking...');
} // end if
};
$('#userInput')
// listen to all events that could cause a change in the input value
.on('keyup change',handlePossibleUserInputChange)
// drop is special; the drop event unfortunately fires before the text is changed
// so we must defer the call until after the text is changed
// same with mouseup; occurs when clicking the input box X button in IE
// same with paste via context menu, rather than shortcut (which would trigger keyup)
.on('drop mouseup paste',function() { setTimeout(handlePossibleUserInputChange); })
;
var lastTaken = true;
window.checkUsername = function() {
let $checkDiv = $('#userCheckLine');
let $userInput = $('#userInput');
// just reset the check line if the input value is empty
if ($userInput.val().trim() === '') {
$checkDiv.text('');
return;
} // end if
// ... send ajax call, get result ...
// (for demo purposes, just invert the previous result)
let taken = lastTaken = !lastTaken;
if (taken) {
$checkDiv.attr({'class':'taken'});
$checkDiv.text('user name is taken.');
} else {
$checkDiv.attr({'class':'notTaken'});
$checkDiv.text('user name is available.');
} // end if
};
.taken { color:red; }
.notTaken { color:green; }
.checking { color:grey; font-style:italic; }
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<form id="form1">
<div>
<input id="userInput" type="text" placeholder="username"/>
<span id="userCheckLine"></span>
</div>
<input type="submit" value="Submit"/>
</form>
I think you should use the "remote" of jquery validation (https://jqueryvalidation.org/remote-method/) this check the validation of the field in the server. You need jquery.
$("#Registration_Form").validate({
rules: {
Registeration_Username_box: {
required: true,
remote: {
url: "check-email.php",
type: "post"
}
}
}
});

ajax form data pass before the page reloads

I use a jQuery.get() request to send a form data. But often the page reloads/redirects too fast, before my JavaScript/jQuery code catches and sends the form data to where i need. I use alert() to get the ajax request done while the user clicks ok on alert. Now i need the form working as usual (with PHP post and redirect) and to send the form data using jQuery or JavaScript BEFORE the page reloads and NO alerts. Is there any elegant way to make the page wait until jQuery is done with the request (without using alert)?
jQuery('#form').live('submit', function() {
var inputValue = jQuery(this).find('#theInput').val();
jQuery.get('http://someurl.com/order?var=' + inputValue);
//alert('an unwanted alert');
});
UPD: I embed jQuery code through Google Tag Manager's iframe. So I can't change the way the form works. And I shouldn't prevent the form from submitting.
jQuery('#form').live('submit', function(e){
e.preventDefault(); // prevent default behaviour
var inputValue = jQuery(this).find( '#theInput' ).val();
jQuery.get('http://someurl.com/order?var=' + inputValue, function(){
// redirect
});
//alert('an unwanted alert');
});
I would take a look at [done][http://api.jquery.com/deferred.done/] which could probably do what you want it to do. .done() will wait for the entire ajax to finish, and then run whatever function you call within done.
You can bind callback to do redirect and return false; to prevent default redirect shown as below:
jQuery('#form').on('submit', function() {
var inputValue = jQuery(this).find( '#theInput' ).val();
jQuery.get('http://someurl.com/order?var=' + inputValue,
function(data){
//write redirect code here. In case if you want to check response, you can get it in data variable.
});
return false; //prevent default redirect action
});

Where to save JS callback function for retry after authentication?

I have an action that I want to allow only to an authenticated user. If a user who hasn't yet logged in clicks this link, the Webserver will return a not-logged-in response. I then present the login form in a floating "lightbox" div. If the user successfully logs in, I want the browser to retry the action.
I'm having trouble finding a good place to store the function that I want to retry. Currently, I'm attaching it to a global object, which seems very hacky. The jQuery "deferred" pattern seems to fit here, but I would still need to retain a reference to the deferred object somewhere, and I can't see the right place to do it.
Suggestions?
Here's some simplified code (in reality, the Ajax response determines whether we show the login form or a confirmation of the action):
$(function() {
$('li.secured').find('a').click(function() {
var link = this;
var action = function() { // captures 'link'
if(RootObj.IsAuthenticated() ) { // already logged in
window.location.assign( link.href );
} else {
// pass ourself in as a callback to be repeated after authentication
// Yes, this seems to work even in the midst of our own definition...
RootObj.ShowLoginForm( action );
}
return false; // don't go immediately to the new url
}
return action();
});
});
RootObj.ShowLoginForm = function( callback ) {
var lb = $('#lightbox');
if(lb.length == 0) { lb = $('<div id="lightbox" class="lightbox"></div>').appendTo('body'); }
lb.load('login.html');
RootObj.callback = callback; // is there a better place to store this callback function?
}

Categories

Resources