Removing an ancestor during the event bubbling phase - javascript

In the following code (JSFiddle here):
<form>
<button>ok</button>
</form>
$(function(){
$('form').submit(false) ;
$('button').click(function(){ $('form').remove() }) ;
}) ;
When you click the button in Google Chrome 48, it triggers a form submission.
If you do it in Firefox 43, however, there is no form submission.
It seems to me that the Firefox behavior should be the correct one, but since I don't have such a deep knowledge of the standard, I don't really know.
Is either behavior wrong or buggy?
Follow up:
I just found out that the same test case but without using jQuery doesn't trigger a form submission in neither browser.
<form onsubmit="return false">
<button onclick="form.remove()">ok</button>
</form>
This could not be a timing issue because there's no thread concurrency in Javascript. Event threads will always run in sequence, so the button event handler must finish before the form event handler starts.
I'm blind here. jQuery must be doing some weird cumbersome stuff for this to happen in Chrome.
Follow up 2:
It's not a jQuery problem. At the jQuery bug tracker I was told that inline event handlers don't follow the same specification as those attached with addEventListener, so a true functionally equivalent code should be like this:
<form>
<button>ok</button>
</form>
<script>
document.querySelector('form').addEventListener('submit',function(){ return false }) ;
document.querySelector('button').addEventListener('click',function(evt){ evt.target.form.remove() }) ;
</script>
And this does behave like the jQuery version.

Your first code adds a returnFalse jQuery event listener:
$('form').submit(false);
In a jQuery event listener, return false is equivalent to
event.stopPropagation();
event.preventDefault();
The later should prevent the submission of the form.
However, before the submit event is fired, you use $.fn.remove. This not only removes the form from the document, it also cleans its jQuery data, including the event listener.
Therefore, when the browser fires the submit event to the form, it is not canceled.
Then browsers behave differently (demo):
Firefox doesn't submit removed forms
Chrome doesn't care whether the form has been removed and submits it anyway
If you don't want to remove jQuery data, you should remove the form using vanilla-js methods instead of $.fn.remove.
In your second code, you cancel the event in a vanilla-js event handler.
Since it's not jQuery data, $.fn.remove does not remove it, so the submit event is canceled and the form is not submitted.
In your third code, you you remove the form using vanilla-js methods, so its jQuery data is not cleaned.
This does not matter because the submit event listener is added with vanilla-js too.
However, the event is not canceled. That's because, unlike vanilla-js event handlers and jQuery event listeners, the value returned in a vanilla-js event listener is completely ignored.
So at the end the result is the same than in the first code, but they are not equivalent.
If you want to cancel an event using a vanilla-js event listener, you should use
event.preventDefault();
This would make it behave like the second code.

I think that it is implementation dependent and not guarantee identical behaviour

Both Chromium and Firefox does not call form.onsubmit handler installed by $('form').submit(...) when $('form').remove() called. So this mean that form is checked as destroyed (or realy destroyed) before to call onsubmit in both Chromium and Firefox.
I think that Chromium does not check that form marked as destroyed when process default submit action emitted by button pressing. But Chromium do checking preventDefault flag where default action of button is form submit. So it is possible to add event.preventDefault() before or after $('form').remove().
In opposite Firefox ignore preventDefault of button pressing event and pass control to form.onsubmit.
So behaviour of this two browsers concerning preventDefault and onsubmit is exactly opposite.
As far as fact of form submiting when form is marked as destroyed it is possible to imagine software design where object actually deleted after communication is finished and not before. But seems to me that it is bug. Need to know what developers think. Does they know about it and is this bug or feature.

Related

Listen to 'onInvalid' HTML event for a <form>

I would like to add an event listener for when a user tries to submit a <form> that has validation errors.
Is there a way to do that in a reliable and accessible way?
Solutions I considered (none of them are good):
Adding 'submit' event listener to <form>.
This doesn't work since 'submit' is only fired for valid forms.
An 'invalid' event is fired by HTML <input> elements that have validation errors when
the user tries to submit a form.
The event does not bubble, so I can't listen to it like this: form.addEventListener('invalid', callback);
Thus, listening for 'invalid' event requires adding 'invalid' event listener for each <input> inside the form when that field is created. It is easy to forget to add an event listener to a field, since fields are created dynamically in many different .js and .tsx files, thus this solution is error-prone and far from ideal.
Listening for 'click' event on the submit button and in the callback checking if form.checkValidity() is false.
This solution is also not ideal as screen reader software and other assistive technologies won't necessarily trigger the 'click' event when trying to submit the form.
Is there any good way to know if a user tries to submit an invalid form?
I am fine with a solution that involves React, jQuery or native JavaScript.
If there is no good to this problem, please consider my use case and tell if there is an alternative approach:
I need to add styling to invalid form fields only after the user tried to submit a form.

JS Submit not the same as HTML Submit

I have a form that requires a Submit, now for design reasons I have had to relocate the code for the submit of these forms and use JS to actually submit them.
This has been workig fine, no problems until I hvae found a form that also does a further peice of JS that no longer fires...
My submit JS is as follows:
function submitform()
{
document.details.submit();
}
This works great. However this is another piece of JS that does work when using the HTML Button:
$('form').submit(function(){
$('#sortdata').val($( "#sortable" ).sortable("serialize"));
return true;
});
Any help/advice would be brilliant as am just going round in circles with changing the names of elements in the JS. This surely has to be possible no?
Use the jQuery submit method to trigger event handlers attached with jQuery:
$(document.details).submit(); //shorthand for .trigger('submit')
Demo
This will submit the form in the same fashion, but also trigger the submit event handlers bound to that element with jQuery before doing so.
The issue was that the native HTMLFormElement.submit() method is not warranted to trigger event handlers bound with jQuery (or any event handler at all):
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.
In other hand, jQuery's .submit()/.trigger('submit') will always¹ execute the event handlers attached through jQuery to that element.
jQuery .trigger() reference
¹ Except if event.stopImmediatePropagation() was called previously for the given event but that's a completely different topic.

Which method of binding a javascript event with jquery's on is more performant?

Let's say you have a web page with forms, and you want to trigger a handler whenever one of those forms is submitted, or whenever a form that is added to the page at a later point in time is submitted. Which method is better:
$('form').on('submit', handler);
or
$(document).on('submit', 'form', handler);
If you want it to apply to forms you will add to your page later dynamically, the latter is the only version that will work at all.
(Honestly, unless your page has thousands of forms, the performance difference shouldn't matter much anyway)
It's not which is better, it's which is more suitable for your needs, both of the methods are good and have their usage.
The first only listens to submits that occur inside of forms, the latter listens to all submits, wait for them to bubble to the document, and then fire the handler callback.
Note that with onsubmit, it doesn't have so much difference, as with mousemove or click which can occur on every place in the <body>, submit only happens in <forms>, but it bubbles all the way to the document.

Dojo 1.6.1 dijit form onSubmit event cancels others

I have a Dojo 1.6.1 (dijit) form with a connection on the submit event.
The form will be submitted with AJAX.
The problem is that when i use dojo.stopEvent on the submit event it stops the triggering of the onChange events for the other inputs (dijits).
In other words: after the submit event occurs I explicitly stop it and send the data with AJAX. After that the onChange event for the inputs cannot be triggered neither by changing the inputs value or programmatically calling dijit.byId('someDijit').onChange().
Any ideas what is the cause of this?
I'm sorry, my bad. The function was called correctly, however another function executed in the onChange wasn't firing.
EDIT:
And the fault was a function with the same name(as the function to be called inside onChange), introduced later in the script.

Using form submit handler in jQueryMobile for validation, but preventing sending

I'm building an app in jQueryMobile and PhoneGap. I have an approach to validate forms that works nicely while testing in Chrome and on my Nexus S Android phone, but that I'm not sure will work on all devices.
This is the situation:
I have a few simple forms. The data that the user enters is stored locally. So I don't need POST or GET requests (in fact, I want to avoid them like the plague). I could just create a few inputs and a button and add a click handler to the button for everything to work nicely.
Except I really like the HTML5 form validation stuff. And that validation only (mostly) takes place when the submit event is fired on the form. So I have chosen to put the inputs and the button inside a form element, make one button a submit button, and listen for the submit event being fired on the form.
Another approach (shown here for the 'Delete' button) is to simply listen for a click on the button without making any use of the submit process. After all, if a user wants to delete an item, there is no need to validate the form (in my case anyway).
<form id="oneExpenseForm" action="javascript:void(0);" method=''>
<button type="submit" id="submitExpense">Save</button>
<button id="deleteExpense">Delete</button>
</form>
<script>
$('#oneExpenseForm').on('submit',function(){
submitExpense();//This function takes care of everything I want
})
</script>
This seems to work fine. I've also tried setting the form action differently:
<form id="oneExpenseForm" action="submit" method=''>
But that caused the browser to start a GET request, which causes a page reload (which is a really bad and unwanted thing when working with jQueryMobile).
What I'm worried about is that I may be setting things up for failure in certain browsers. After all, some browsers might see this action attribute...
action="javascript:void(0);"
...as a reason to not fire the submit event at all. That would be bad, since I need the submit event to fire for validation and
Am I taking needless risks here? Or do all browsers fire the submit event even when the form action is set to null, void, etc?
One more thing:
I could of course just specify...
action="javascript:submitExpense();"
...in the HTML file. But this is something that I want to avoid, since I am protecting my Js code with obfuscation. That involves changing the names of functions to unreadable code like aR3df(); using a special piece of software. I would prefer not to have to look up the obfuscated names and enter them in the HTML every time I am publishing an update.
EDIT:
After the first answer came in to use e.preventDefault() and that did not work, I figured it might be relevant that I am binding the submit event handler inside another function. That's because jQueryMobile wants you to only do your custom handling stuff after the "page" has been initialized.
In previous jQuery versions it didn't work to use the .live() binder within other functions. But this is not the problem either. Even when I bind the handler the ".live()" way using the new .on() binder...
$(document).on('pageinit','#oneExpensePage',function(event){
$('#oneExpenseForm').on('submit',function(){
submitExpense();//This function takes care of everything I want
})
})
... the page reload (i.e. form submit) persists.
Leave action empty action="" and prevent the event's default action on submit:
<script>
$(function() {
$('#oneExpenseForm').on('submit',function(e){
e.preventDefault();
submitExpense();
});
});
</script>

Categories

Resources