I'm implementing a 'login to continue' functionality when submitting a form after the user's session has expired, but I've run into something I don't quite understand.
When I add the submit listener to the form, it requires 3 submit attempts before it submits (while logged in): the 1st time, nothing is printed to the console; the second submit prints 'Preventing form submission', and the 3rd finally allows the submit to proceed. After that, every 2 submits will function, with the first displaying the console line above.
However, if I pass 'this' to the onreadystatechange function, it works exactly as I expect each and every time - the only problem then is that I get a 'SyntaxError: missing formal parameter' error in the JS console, obviously because I am providing a value in place of a parameter name.
So my question is: how do I do this properly, and why doesn't it work if I don't provide 'this' (which is the current form element to which the event listener is being attached) to the function?
I don't want to use JQuery or any external libraries for this particular project, so please do not use any in your answers.
form.addEventListener('submit', function(e) {
var xmlhttp = new XMLHttpRequest();
// xmlhttp.onreadystatechange = function() { // doesn't work the way I want
xmlhttp.onreadystatechange = function(this) { // works the way I want
if (xmlhttp.readyState === 4 && xmlhttp.status === 200) {
if (xmlhttp.responseText === 'true') {
form.setAttribute('allow_submit', 'true');
form.submit();
} else {
form.setAttribute('allow_submit', 'false');
}
}
}
xmlhttp.open("POST", "xml_request_router.php", true);
xmlhttp.setRequestHeader("Content-type","application/x-www-form-urlencoded");
xmlhttp.send('route_id=active_session_check');
console.log("Preventing form submission");
if (form.getAttribute('allow_submit') !== 'true') {
e.preventDefault();
}
});
I've tried using a callback and passing parameters to that, but behaved exactly the same as the above. Here was my attempt at that:
function stateChanged(xmlhttp, form) {
if (xmlhttp.readyState === 4 && xmlhttp.status === 200) {
if (xmlhttp.responseText === 'true') {
form.setAttribute('allow_submit', 'true');
form.submit();
} else {
form.setAttribute('allow_submit', 'false');
}
}
}
// and here's how I called it from the event listener:
xmlhttp.onreadystatechange = function() { // adding 'this' results in correct behavior
stateChanged(xmlhttp, form);
}
EDIT: Here is how I am retrieving the form elements:
var forms = document.getElementsByTagName('form');
for (i = 0; i < forms.length; i++) {
var form = forms[i];
// rest of code follows from here
EDIT: Just in case anyone else comes here looking for a solution, the only way I was able to get the closures to work properly was to convert the NodeList to an Array and iterate over that:
function nodeListToArray(nl) {
var i = nl.length, arr = new Array(i);
for(; i--; arr[i] = nl[i]);
return arr;
}
var forms = document.getElementsByTagName('form');
nodeListToArray(forms).forEach(function(form) {
form.addEventListener('submit', function(e) {
// this way does NOT work:
// return function(e, this) {
// original logic here
// }
// but this way DOES:
return notSureWhyThisIsNecessary(e, this);
});
});
function notSureWhyThisIsNecessary(e, form) {
// original logic here, i.e. xmlhttp request etc.
}
So if like myself JavaScript closures (specifically in cases like the above) are still somewhat of a mystery to you even after reading loads of articles on them, perhaps the code snippet above will help. Cheers.