Adding hidden field in beforeunload - javascript

On my page I'm using a form which on submit should get some extra hidden fields. The problem however is that I can't know the name/id of the form nor the submit button so I can't use functions like onClick etc. Therefore I'm using the beforeunload event to trigger an event when the user tries to leave the page (this includes submitting a form). I'm using the document.activeElement to find out what triggered the event and if it's a form/submit I'm adding the hidden fields to the form.
Stripping down the code it comes down to something like this:
var javascriptData = ['val1','val2','val3']; // usually gets values dynamically and can differ in size
$(window).bind('beforeunload', function(e) {
var activeForm = document.activeElement.form;
for(var i = 0; i < javascriptData.length; i++){
$(activeForm).append('<input type="hidden" name="test-data[]" value="'+javascriptData[i]+'" />');
}
return false; // just for debug purposes.
}
The code works and the element is added to the form but it doesn't show up in the $_POST on the next page. However, the final line of code (return false) creates a pop-up asking if I want to leave the page, if I stay on the page, press the submit button again and then leave the page and check $_POST, the field there. My guess is that the data for the $_POST is collected before the beforeunload function is triggered.
My question is: "Is there a way around this so I can add a hidden field to a form in the beforeunload event?".
Help is much appreciated, thanks in advance.

Why not add it to all forms on the page?
$(document).ready(function() {
$("form").append('<input type="hidden" name="test-data[]" value="test" />');
});

Like the comments above mentioned, you can just hook onto the submit event.
$('form').submit(function() {
for(var i = 0; i < javascriptData.length; i++){
$(this).append('<input type="hidden" name="test-data[]" value="'+javascriptData[i]+'" />');
}
}

Related

How are jQuery event handlers queued and executed?

I have an input form, with a submit button. I don't want the user to be able to double click the submit button and double submit the form...
So I have added the following jQuery to my Form:
var prevSubmitTime = new Date('2000-01-01');
function preventFromBeingDoubleSubmitted() {
$('form').each(function () {
$(this).submit(function (e) {
if ($("form").valid()) {
var curSubmitTime = new Date($.now());
// prevent the second submit if it is within 2 seconds of the first submit
if (curSubmitTime - prevSubmitTime < 2000) {
e.preventDefault();
}
prevSubmitTime = new Date($.now());
}
});
});
}
$(document).ready(function () {
preventFromBeingDoubleSubmitted();
});
The above code stores the submit time and prevents the second submit, if it is too early (less than 2 seconds), I don't want to permanently disable the submit button, in case there is a server side error...
This code does what I want, but when debugging the code, I can never hit a break point on e.preventDefault();... even if I double click the submit button.
It looks like the second submit event is waiting for the first submit event to complete before firing.
But, if I remove preventFromBeingDoubleSubmitted() function, then I would be able to double submit the form, by double clicking the submit button.
Can anyone explain why sometimes the submit events are fired immediately one after the other... and sometimes it is not the case? Does putting the event handler inside .each(), affects their execution behavior?
Form's when submited by default navigate to the set action url. In the case it isn't explicitly set the url is the current page. The first submit call is going end up starting the navigation process. During this the currently loaded javascript code gets unloaded. This includes event handlers. Hence why you get the inconsistency of being able to double submit or not. If the network request, and other page processes, to the action url happens faster than the speed it takes you to click again the event handlers and your break point won't be called/reached again because they are already unloaded. And vise versa if the network request is slower you would be able to cause the handler to be called and the break point to be reached (if it hasnt already been unloaded).
You say you don't want to permanently disable the submit button, but even if you disable it the form submission is going to cause a page change, and in your example's case this will just load the same page with a new submit button which will not be disabled anymore because its a new page. Thus it is never really permanetly disabeled in the first place.
Now if your real form isn't actually doing a normal form submit, and you are using something like an ajax request, web socket connection, etc then you would set the button to disabled(or set a busy flag) before the request and unset it in the ajax request callback, web socket event,etc.
For example:
jQuery('form').on('submit',function(e){
e.preventDefault();
var fd = new FormData(this);
jQuery('yourbutton').prop('disabled',true);
fetch('url',{method:"post",body:fd}).then(()=>jQuery('yourbutton').prop('disabled',false));
});
In your snippet I've added a few logs that might be helpful. As you are asking more than one question, I'll answer one by one.
var prevSubmitTime = new Date('2000-01-01');
function preventFromBeingDoubleSubmitted() {
$('form').each(function () {
$(this).submit(function (e) {
console.log('I am going to submit form');
if ($("form").valid()) {
var curSubmitTime = new Date($.now());
console.log('I am a valid form')
// prevent the second submit if it is within 2 seconds of the first submit
if (curSubmitTime - prevSubmitTime < 2000) {
console.log('A small time difference. So I am stopping')
e.preventDefault();
}
prevSubmitTime = new Date($.now());
}
});
});
}
$(document).ready(function () {
preventFromBeingDoubleSubmitted();
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery-validate/1.19.0/jquery.validate.js"></script>
<form id="myform">
<input type="text" name="q" required="required" />
<br />
<input type="text" name="bar" required="required" />
<br />
<br />
<input type="submit" />
</form>
Can anyone explain why sometimes the submit events are fired
immediately one after the other... and sometimes it is not the case?
I think you've answered this question yourself. You are adding the code to check if there a difference between time you clicked the submit button the first time versus the second time. If the time difference exists, then you stop the second form submit.
I can never hit a break point on e.preventDefault();
The reason you're not able to get the console is, you're redirecting away from that page when you click the submit button. So the console is cleared. If you want to see the console, use an ajax function to submit the form. And on return, you can probably redirect the page somewhere.
Does putting the event handler inside .each(), affects their execution
behavior?
No. It is just an iterator. It will not affect the submit functionality.
I've added a link to the jsfiddle. Adding the alert before preventDefault will stop page from redirecting momentarily. This will prove that the execution happened.
http://jsfiddle.net/2vugwyfe/
You solution is way too overcomplicated. The easiest way to prevent a double submit would be to disable the submit button on submission.
Example:
var submittable = false;
$('form').submit(function (e) {
if (!submittable) {
e.preventDefault();
var $this = $(this);
var $submitButton = $this.find('button[type="submit"]');
$submitButton.attr('disabled', true);
if (CONDITION_SATISFIED) {
submittable = true;
$this.submit()
} else {
$submitButton.attr('disabled', false);
}
}
})
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<form>
<button type="submit">Submit</button>
</form>
If you add e.preventDefault(); just before doing $("form").valid(), you will see there's an error thrown.
script.js:7 Uncaught TypeError: $(...).valid is not a function
at HTMLFormElement.<anonymous> (script.js:7)
at HTMLFormElement.dispatch (jquery.min.js:2)
at HTMLFormElement.y.handle (jquery.min.js:2)
This error wasn't visible at first because the submit actually changes the page (refreshes the page in this case) if nothing else is implemented.
However, in general the practice is navigating to another page after a form submission.
If you still want to go with your approach and limit the number of submitting, I suggest keeping the submitted state in a local variable and change it according to the validation on the server side.
Last thing.. I don't understand the iteration through the forms since you have only one in your HTML -> $('form').each is useless.
I know what you want, but you made it very complicated. instead of inserting a submit button just add a simple div and add a click handler on that.
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<form id="myform">
<input type="text" name="myInput" />
<div id="submit" onclick="myform_submit()" />
</form>
and :
function myform_submit() {
if ($('#submit').hasClass('busy')) { return; }
$('#submit').addClass('busy');
// do the tasks then remove the `busy` class :
$('#submit').removeClass('busy');
}
I just show the idea, you can do better.

JavaScript & HTML Forms: Which button is the submitter?

I am writing a JavaScript function to pre-process form data. One thing I need to know is which of a number of submit buttons was used.
Roughly, the script has the following outline.
var form=document.querySelector('form#test');
form.onsubmit=processForm;
function processForm() {
// how did I get here
}
<form id="test">
<!-- usual stuff -->
<button type="submit" name="check">Check</button>
<button type="submit" name="doit">Do Stuff</button>
</form>
I know that I could attach the function to the individual submit buttons, but it would be more resilient if I attached it to the form itself.
Is there a form property or some other method for checking which submit button was used?
Thanks
You can attach click handler to each <button> element, create a variable to store clicked button name property, or reference to element, access property or element at submit event
function processForm(e) {
e.preventDefault();
console.log(curr) // `curr`: `name` of clicked `button"` element
}
var submit = document.querySelectorAll("button[type=submit]");
var curr;
for (var i = 0; i < submit.length; i++) {
submit[i].onclick = function(e) {
curr = e.target.name; // store reference to `name`, or element
}
}
Usually the clicked submit button is sent as it is considered active.
I believe that when there is only 1 submit button the form will submit on enter but when multiple it will only submit on click.
Thus in your backend, you can just check for the existence of either form button to know which button was used.
Relevant section of HTML rfc:
https://www.w3.org/TR/html401/interact/forms.html#h-17.13.2
However I wasn't able to find a good browser compatibility for this so your mileage may vary.
I believe the answer to my question is that you can’t. Once the submit event is activated, it doesn’t seem to know or care how it got there.
The only solution, as mentioned in my original question, is to attach a listener to the submit button[s].
Here is a stub of the processing code:
window.onload=init;
function init() {
var form=document.querySelector('form#test');
var submitButtons=form.querySelectorAll('button:not([type]),button[type="submit"],input[type="submit"]');
if(submitButtons.length) {
for(var i=0;i<submitButtons.length;i++) submitButtons[i].addEventListener('click',process);
}
function process(e) {
var submitter=this;
var form=this.form;
// etc
}
}
Oh well …

Specifying button clicked in multiple forms in javascript

I need help with some Javascript.
Inside my html I have a form that repeated twice.
<form method='post' action='php page'>
<input type=text id=something name=something value='' />
<input type="submit" id="final_submit" value="yes">
</form>
This exact form is repeated twice. In javascript when someone clicks on the submit button I need to be able to determine which button was clicked to get the value of something in that form. Right now its automatically just getting the value for the first form even though I click the button on the second form.
Any ideas please.
As mention in the comments, using duplicate id is wrong. Here is a way to get the value of the input that will work also in using older browsers document.forms and forms.element
var forms = document.forms;
for(var i=0, l= forms.length; i++){
forms[i].addEventListener('submit', function(e){
e.preventDefault();
var form = e.target;
var value = form.elements[0].value;
})
}
This will add a submit event listener to every form in your side and that stops the event and get the value from the first input field in this form.
Is that what you need?
function determineForm(e)
{
// This prevents the form from actually being submitted.
// Just remove it when you are done debugging.
e.preventDefault();
if (document.querySelectorAll("form")[0] === e.target)
{
alert("Form1");
}
else
{
alert("Form2");
}
}
document.addEventListener("submit", determineForm, false);

jQuery form submit: Any way to know what element triggered the submit?

I'm using asp.net MVC and when I submit a form, a previous developer had embedded some jQuery validation.
$('form').submit(function() {
...code done here to validate form fields
});
The problem is that both the "Save" and "Cancel" buttons on the form fire this submit jQuery function. I don't want the validation logic to fire if the "Cancel" input button was fired (id="cancel" name="cancel" value="cancel").
Is there a way that, within this submit function, I can retrieve the ID, name or value of which input button was pressed to submit the form?
I asked this same question: How can I get the button that caused the submit from the form submit event?
The only cross-browser solution I could come up with was this:
$(document).ready(function() {
$("form").submit(function() {
var val = $("input[type=submit][clicked=true]").val()
// DO WORK
});
$("form input[type=submit]").click(function() {
$("input[type=submit]", $(this).parents("form")).removeAttr("clicked");
$(this).attr("clicked", "true");
});
Not sure if its the answer you're looking for but you should change the "Cancel" button to an anchor tag. There's no need to submit a cancel unless you're doing work on the form values.
well this will only fire if the type of the input button is like so:
<input type='submit' ...
so make sure the cancel button does not have type='submit' and it should work
EDIT
This only works in FF and not in Chrome (and I so, I imagine, not in other WebKit based browsers either) so I'm just leaving this here as a browser specific workaround, an interesting note but not as the answer.
#Neal's suggestion of NOT making the cancel button of type submit is probably the cleanest way. However, if you MUST do it the way you are doing it now:
$('form').submit(function(e){
if(e.originalEvent.explicitOriginalTarget.id === 'cancel'){
//don't validate
}
else{
//validate
}
});
var myForm = $('form');
$('input[type="submit"]',myForm).click(function(e) {
var whoClickedsubmit = $(e.target); //further, you can use .attr('id')
//do other things here
});
EDIT
.submit(function(event){
var target = event.originalEvent.explicitOriginalTarget.value;
//But IE does not have the "explicitOriginalTarget" property
});

Change form values after submit button pressed

[EDIT] After a lot of digging around, I found out that the problem was in how I integrated the CKEditor into my page. The simple and obvious way does work in this case, as laid out in the accepted answer.
Hi,
I need to change the values of a form, after the submit button has been pressed, but before the actual submission has taken place.
I've tried hooking into the "submit" event of the form, and changing the text field values there manually, but it looks like that doesn't actually change the values submitted.
Any ideas?
I'm curious about your statement that the submit handler didn't work for you. It does for me. I've used it for years to fill in hidden fields before sending forms in; should work for other form fields as well.
Example (live copy):
HTML:
<form id='theForm'
action='http://www.google.com/search'
method='GET' target='_new'>
<label>Search for:
<input type='text' name='q' id='txtSearch'></label>
<input type='submit' id='btnSearch' value='Search'>
JavaScript:
window.onload = function() {
document.getElementById('theForm').onsubmit = function() {
var txt = document.getElementById('txtSearch');
txt.value = "updated " + txt.value;
};
};​
Tested and working on IE6 and IE7 on Windows, and Chrome, Firefox, and Opera on Linux.
Update: Based on your comment below, you're using jQuery. Works fine using jQuery for everything as well:
$('#theForm').submit(function() {
var txt = $('#txtSearch');
txt.val("updated " + txt.val());
});
Live example Tested and working on the same set of browsers. This version uses a more open search rather than an id, and also still works.
You need to prevent the default submit action and then resubmit the form yourself manually:
$('form').submit(function(e) {
e.preventDefault();
// do your processing
this.submit(); // call the submit function on the element rather than
// the jQuery selection to avoid an infinite loop
});
Did you try adding function on click JavaScript event on the submit button and changing the values?
It may work because client script will execute first
You could use formData event.
The formdata event fires after the entry list representing the form's
data is constructed. This happens when the form is submitted, but can
also be triggered by the invocation of a FormData() constructor.
formElem.addEventListener('formdata', (e) => {
const formData = e.formData;
//no need change document.getElementById().value = "";
formData.set('field1', 'foo');
formData.set('field2', 'bar');
//updated formData
});

Categories