The HTMLFormElement DOM interface provides the .submit() and .reset() methods, which I'm heavily making use of in my single-page app.
Right now I'm trying to figure out the relation between these two methods and triggering the form element's onsubmit and onreset event handler methods.
Test case
So considering this perfectly valid HTML5 document:
<!DOCTYPE html>
<title>HTMLFormElement method-event relation test page</title>
<form style="border:solid">
<input name=foo>
<button>Submit</button>
<button type=reset>Reset</button>
</form>
<button id=js-submit>.submit()</button>
<button id=js-reset>.reset()</button>
And the following JS <script> content right below the form:
(function() {
var qs = function(s) {
return document.querySelector(s);
},
form = qs('form');
form.onsubmit = function(e) {
console.log('onsubmit fired');
return false;
};
form.onreset = function(e) {
console.log('onreset fired');
return false;
};
qs('#js-submit').onclick = function() {
form.submit();
};
qs('#js-reset').onclick = function() {
form.reset();
};
}());
Live demo
* Side-note: I know addEventListener may be preferred over onevent handlers, however it'd have the same effect and this is merely a simple, IE8-compatible illustration.
Clicking the Submit and Reset buttons inside the form fire the corresponding onsubmit and onreset methods just fine, as these are user actions the event handlers must be triggered.
The buttons outside of the form call the form element's .submit() and .reset() methods, that is, the methods which I'm programmatically calling in my real code.
Normally, as I'm programmatically calling simple form methods from code-side, I'd expect these to not trigger any form event.
However, clicking the .reset() button does dispatch an onreset event to the form, while .submit() does not dispatch any event to the form. Tested in Chrome, Firefox and IE8.
* The behavior is the same when using addEventListener instead of on* handlers.
Question
Is there a standard expected behavior for whether event handlers will be triggered when a form's .submit() and .reset() methods are called?
The .submit() method is not warranted to (and most often does not) trigger the onsubmit event due to historical reasons. It is worth noting that modern browsers (latest Chrome, Firefox, Opera, Safari and even IE8) do not trigger the onsubmit event when calling form.submit().
The .reset() method in the other hand, follows the spec and performs the same action as clicking a reset button, which includes dispatching the onreset event to the form. Tested in the same browsers as the paragraph above.
Reference
HTMLFormElement.submit MDN page:
The form's onsubmit event handler (for example, onsubmit="return
false;") will not be triggered when invoking this method from
Gecko-based applications. In general, it is not guaranteed to be
invoked by HTML user agents.
DOM Level 2 Recommendation:
Methods
reset
Restores a form element's default values. It performs the same action as a reset button.
submit
Submits the form. It performs the same action as a submit button.
[...]
Note: The onsubmit even handler is not guaranteed to be triggered when invoking this method. The behavior is inconsistent for historical reasons and authors should not rely on a particular one.
W3C mailing list:
submit
Submits the form. It performs the same action as a submit button.
I don't think the last sentence is correct. Using a submit input will
trigger onsubmit. The submit method will not. [...]
The accepted answer remains valid in 2022.
I just wanted to add a note, that there are still inconsistencies between browsers today.
For example if you dispatch a custom event named reset on a form element, chrome will ignore it, but firefox will actually reset the form as if you called form.reset() or clicked a reset button.
Related
Where we have js code that submits a form instead of just submitting the form like:
form.submit();
Should we instead dispatch a (bubbling, cancelable) event in order to allow other potential js event listeners the chance to handle the form submission:
form.dispatchEvent(new Event('submit', {bubbles: true, cancelable: true}));
It seems like this allows our code to play more nicely with others. If this is true, why isn't this pattern pushed more?
HTMLFormElement.requestSubmit() to the rescue!
In most cases in which a form is to be submitted programmatically via JavaScript, it's wise to use requestSubmit() rather than submit(). Doing so ensures submit handlers will have a chance to handle the submit event. Use submit() only when you want to explicitly submit the form ignoring any registered submit event listeners and form validation.
I was simply not aware of this newer form method.
See https://developer.mozilla.org/en-US/docs/Web/API/HTMLFormElement/submit
The HTMLFormElement.submit() method submits a given <form>.
This method is similar, but not identical to, activating a form's
submit <button>. When invoking this method directly, however:
No submit event is raised. In particular, the form's onsubmit event handler is not run.
Constraint validation is not triggered.
The HTMLFormElement.requestSubmit() method is identical to activating
a form's submit <button> and does not have these differences.
And usage notes from https://developer.mozilla.org/en-US/docs/Web/API/HTMLFormElement/requestSubmit
The obvious question is: Why does this method [requestSubmit()] exist, when we've had the submit() method since the dawn of time?
The answer is simple. submit() submits the form, but that's all it
does. requestSubmit(), on the other hand, acts as if a submit button
were clicked. The form's content is validated, and the form is
submitted only if validation succeeds. Once the form has been
submitted, the submit event is sent back to the form object.
I guess I have never tried to do this before ... I have a button on a page.
<input type="submit" id="btn" value="Submit">
And a javascript function that includes:
function clickit()
{
alert(document.getElementById('btn').value);
document.getElementById('btn').click();
}
Using Firefox, the button is not clicked - i.e. the form is not submitted. The alert shows, but the form does not get submitted. Why won't Firefox click a button?
Use a div or anything besides a INPUT element if you want to bind the click event to it. If <INPUT> is inside a form body, you might run into weird issues.
If you just need to submit a form with a button I would recommend that you just use a <div> element with a click handler rather than an input. It will give you a little more flexibility. If you do that then you should be able to just select your form and use the submit() API to submit the form.
If you really can't modify the code enough to do this and are having trouble selecting and submitting here is how you will need to do that using both jQuery and DOM.
The jQuery Way:
$("my selector").trigger("click")
You may run into issues around focus if you're running in PhantomJS or you've got a window like a test runner that is not in focus. In this case you can use:
$(<my selector>).triggerHandler(<my event>)
The DOM API way
This will just trigger the event (the equivalent of the first example)
// Create the event
var event = new CustomEvent("name-of-event", { "detail": "Example of an event" });
// Dispatch/Trigger/Fire the event
document.dispatchEvent(event);
You can also simulate a click with the actual DOM method
var button = document.getElementById('my-button');
button.click();
Why won't Firefox click a button?
I seem to recall that early versions of Firefox didn't allow calling of listeners that way for security reasons. However, I think those reasons have been addressed in other ways and now you should be able to call the click handler directly. The following submits the form in Firefox 34:
<form onsubmit="alert('submitted!!')">
<input name="foo" value="foo">
<input type="submit" id="aa">
</form>
<br>
<button onclick="document.getElementById('aa').click();">Click the submit button</button>
The form's submit listener is called and the form is submitted, so calling the submit button's click method is doing what it's supposed to.
This method doesn't work for all listeners though, click is a special case, see W3C DOM Level 3 Events Specification, §3.5 Activation triggers and behavior.
I'm looking for a cross browser compatible way to stop a form submission without returning false.
For example:
<form action="..." onsubmit="someFunc(this);">
...
</form>
<script type="text/javascript">
function someFunc(form){
// do stuff to stop form submission without return false
}
</script>
I've not found a good way to do this, anybody know if its possible?
Edit:
I've tried both form.preventDefault() and window.event.preventDefault() in someFunc(). It seems like neither prevent the form from submitting in firefox (on a mac).
Solution
It seems like window.event is not available in FF, while it is in Chrome and IE.. thus my previous attempts not working. I solved this by having the first parameter of the function being called in the form's onsubmit to be the event itself. That can then be cancelled.
New form onsubmit looks like so: onsubmit="someFunc(event, this);"
You can use the event.preventDefault() function to stop of form from submitting. Here's an of a form being caught before being submitted in Javascript (with some JQuery):
$("form").submit(function(event) { // You can change "form" to whatever ID/class your form has
event.preventDefault();
}
Done it! I've tested it in Firefox and this definitely works
Here's a link to the JSFiddle: http://jsfiddle.net/bZx3e/. Here's the HTML:
<form id="form"><input type="checkbox" id="checkbox"/><label for="checkbox">Checkbox</label>
<input type="button" onclick="check();" value="Submit"/></form>
And here's the Javascript:
function check(){
if(document.getElementById("checkbox").checked){
document.getElementById("form").submit();}
else{}
}
So, basically, if the checkbox isn't checked, do nothing and if it is, submit the form. This way you could have all your validation before submitting the form.
Each event has an Event object associated with it. In the W3C event model, calling event.preventDefaut will prevent the default action from occuring. However, the only way in the W3C model to get a reference to the related event object is to pass it to the function from the associated listener. For an in–line listener:
<form onsubmit="someFunc(event)" ...>
If you attach the listener using addEventListener, then the related event object will be passed to the listener function as the first argument:
someElement.addEventListener('click', foo, false);
function foo(eventObject) {
var target = eventObject.target; // element on which the click occured
var currentTarget = eventObject.currentTarget; // Element that called the listener
}
However, the listener in the OP is attached in–line and does not pass a reference to the event, so it can't be captured in browsers that only support the W3C model.
It the IE event model, the related event object is available as a property of the window object, so within the function you can do:
var event = window.event;
But the IE model doesn't support preventDefault, however does provide a returnValue property that, if set to false, cancels the default action.
So the most cross–browser way without using return false would be:
<form onsubmit="someFunc(event);" ...>
function someFunc(event) {
if (event.preventDefault) {
event.preventDefault();
} else {
event.returnValue = false;
}
}
However, it is very much simpler to use return false as it has been reliably supported by every browser since about NN/IE 2.
I'm writing some unit tests for an HTML 5 app that uses the HTML 5 form validation API. I've attached a submit event handler to the form that does some custom handling before serializing to JSON and passing it off to my server.
What I've discovered, though, is that if I initiate a jQuery submit() event on the form, even if it's invalid, my submit handler still gets called.
Instead, I'd expect my event handler not to have been called because the form is invalid.
I've created a JSFiddle to demonstrate (tested in Chrome 20):
http://jsfiddle.net/jonbca/SYg4h/22/
So, what gives?
Triggering the ".submit()" handler simply does not have anything to do with the HTML5 form validation mechanism. That mechanism is really quite independent of JavaScript, and in fact it's mostly unavailable from the DOM API. You can explicitly call "checkValidity()" on a form element, but that just returns a boolean result and does not do any of the visual form updates that happen when the user clicks a "submit" form control.
It's important to keep in mind that many of the fancy HTML5 "smart markup" behaviors are designed to allow things to happen without the need for JavaScript.
Try triggering the submit button:
$('#submitBtn').click();
If you don't have one, just do a hidden one, that replicates the action.
Fiddle: http://jsfiddle.net/SYg4h/30/
Try using a click handler on the button
$('#myform').submit(function (e) {
// check for validation here
var value = $('#foo').val();
if (!value || value == undefined)
$('#message').html('It did not submit');
else
$('#message').html("It submitted");
return false;
});
$('#submitBtn').click(function(){
$('#myform').submit();
});
Try this: http://jsfiddle.net/Cqzcu/4/
I have an HTML button that needs to check several conditions, and if they pass allow the default action to occur.
The following works in Firefox, but it fails in IE. I setup a click handler on the button:
Ext.get('send').on('click', handleSend, this, {
preventDefault: true
});
which pops up one of several message boxes if one of the conditions isn't met. If all conditions are met, I remove the click listener from the button and click the button again:
Ext.get('send').un('click', handleSend, this);
Ext.getDom('send').click();
As far as I can tell, it fails in IE (and possibly other browsers) because click() isn't a standard function for a DOM element.
If the default action were a simple form submit, I could just do that after the checks pass, but we're using Tapestry 4 with a listener, which doesn't get executed on a normal form submit.
I've tried submitting the form with
tapestry.form.submit('composeForm', 'doSend');
but the doSend listener isn't getting called.
Conditionally allowing the default event is the best solution I've come up with, but there are a couple of options that may be possible:
Is there some other way to cause a Tapestry 4 listener to be fired from within Javascript?
Is there any way to recognize the normal form submit in my Tapestry Page and thereby trigger the listener?
JSFiddle added
In this jsfiddle, the default action is to submit the form; this is prevented when the checkbox is unchecked. When checked it removes the handler, but the call to click() doesn't work in IE.
Is there a way to simulate a click in IE?
Update
Another snag in the problem is that I have to display an 'are you sure' dialog, so in order to give them time to answer, the event has to be stopped. If they click OK, the default action needs to occur. JSFiddle doesn't seem to have ExtJS widgets like MessageBox, so I'm not sure how to demo this behavior.
At #Ivan's suggestion I tried
Ext.getDom('send').fireEvent('onclick');
but it returns false, meaning the event is being cancelled somewhere. I then tried
var evt = document.createEvent("Event");
evt.initEvent('click', false, false);
var cancelled = Ext.getDom('send').fireEvent('onclick', evt);
but IE9 says that document.createEvent doesn't exist, even though this is how MSDN says to do it.
If all conditions are met, I remove the click listener from the button
and click the button again:
Don't.
You should rather check the conditions in the click handler and call stopEvent there like so:
Ext.get('send').on('click', handleClick);
function handleClick(e) {
if (condition) {
e.stopEvent();
}
}
Internet explorer does not support click. You should use fireEvent method instead e.g.
Ext.getDom('send').fireEvent('onclick');
That should work for IE. For other browsers I guess click is ok. Anyway If I should do similar task I'll try to write an adapter for tapestry and use tapestry javascript library.
There's a listener parameter on Form components; from the Tapestry 4 doc:
Default listener to be invoked when the form is submitted. Invoked
only if another listener (success, cancel or refresh) is not invoked.
Setting this parameter to my listener method like so:
<binding name="listener" value="listener:doSend" />
causes a Tapestry form submit
tapestry.form.submit('myFormId');
to trigger the listener.