Form submitted twice in Chrome/Safari
Asked Answered
P

5

7

I am seeking to perform a basic form post, but the following submits to the server twice in Chrome and Safari (but behaves as expected in Firefox):

<form id="create-deck-form" action="/decks/create" method="post">
  <fieldset>
    <legend>Create new deck</legend>
    <label for="deck-name-field">Name</label>
    <input id="deck-name-field" name="deck-name-field" type="text" value="" maxlength="140" />

    <label for="tag-field">Tags</label>
    <input id="tag-field" name="tag-field" type="text" value="" maxlength="140" />

    <input class="add-button" type="submit" value="Create" />
  </fieldset>
</form>

I would like to use the onsubmit attribute to perform validation on the fields before submission, but wherever the return value is true, the form is submitted twice.

I have sought to bind a jQuery handler to the form, but here too, where the default behaviour is not prevented, the form is submitted twice, e.g.:

<script type="text/javascript">
 $(document).ready(function() {
     $("#create-deck-form").submit(function(event){
          if($("#deck-name-field").val() == "") {
            event.preventDefault();
            alert("deck name required");
          }
        });
 });
</script>

While I suppose there is something blindingly obvious wrong here to a fresh pair of eyes, I am deeply confused why submission, with or without validation, makes a duplicate post to the server in Chrome and Safari. I'd be grateful for any insight.

Pen answered 6/7, 2010 at 12:53 Comment(16)
Is there anything else involved? Are there any "click" handlers that explicitly invoke "submit()" on the form, for example?Hayley
Thanks, Pointy. No, it is perfectly bare bones at the moment, with no other handlers bound to the form or its buttons.Pen
Do you have an example page or post the contents of your page somewhere (all HTML and JS), so we can have a look?Urdu
OK, well hmm. OK now my next question is this: What is it that has convinced you that the form has been submitted twice? (I'm not trying to deny what you're seeing, but the thing is that code all looks more-or-less OK to me, so I'm just trying to get more of the complete picture.)Hayley
The more I think about this the weirder it seems. I don't know how I'd make a form get posted twice if I wanted it to happen (well without wiring up the form to an AJAX submission mechanism).Hayley
@Marcel The live site is www.braineos.com. You'd need to log in (with OpenId) to create a new deck of flashcards. The form is on your profile page, though its code is slightly different as it is using the onsubmit attr (I'm 'fixing' this bug in Chrome and Safari today so the live code isn't the same as my dev stuff posted here)Pen
@Hayley I know the form is submitted twice because I can see the url being hit twice in my dev console and I can also see a phantom deck is created on dev (and live) apps when deck create is done through Chrome/Safari. My AJAX forms run pretty sweet especially as I'm moving over to jQuery form plugin this week, it is just this particular form that is killing me.Pen
Could it be that the enter key needs to be blocked from submitting?Red
@Pen is it possible that some Javascript file that the page is importing might be attaching something to the form or to the buttons, or whatever? Could you (experimentally) try omitting some of the script files to see if that makes a difference? Of course, it's not clear why that'd cause different behavior between Chrome and FF, but this is a weird issue anyway.Hayley
@fudgey Thanks! I'm not doing any keypress management JS/jQuery at the moment on this form and simply using the browser's default behaviour for input type='submit'. I have been wondering if submit inputs are fundamentally 'weird' in Chrome/Safari, but w3C says that submit inputs are supported in those browsers and Googling around the issue hasn't shown me that there are known probs in that regard. What gets me is that the most basic post doesn't behave right! It must be something so ridiculous that I'm just not seeing.Pen
@Pointy, my JS is concatenated and minified but I have confirmed that there are no other handlers associated with the form or any of its elements.Pen
Well you've apparently drifted off into the Twilight Zone somehow. I hope you find a solution.Hayley
@Pointy! I tore out all the concatenated JS and something, somewhere is doing something to my form, unbidden! I'll let you know when it is rolling. I just know it is something deeply stupid ;)Pen
That's good news. Isn't there a Sherlock Holmes quote about how the answer to a mystery is never that something impossible has happened? :-)Hayley
Hmmm, ok did you try disabling the submit button after the validation clears it? (e.g. #904050)Red
Thanks for that link @fudgey, could come in handy, unhappily this is not a case of multiple invocations of the submit input. @Hayley after a break at the gym and a shot of Mount Gay I have found the root of all evil. The page that is loaded by the server after the submission includes a button which opens a facebox. When the facebox initialisation happens onload ($("#megs-button").facebox();) a second request posts to the url which is pointing at my deck-creation server (I'm sorry if this lacks context and doesn't make sense). Removed all JS and worked backwards to here - thanks for your help!Pen
P
3

This issue is actually associated with Facebox (http://defunkt.github.com/facebox/). Facebox initialization causes a second set of requests after your page loads. So, if you post to /myaction/create, Facebox will start a second set of requests with the base URL set to /myaction/create. A fix is to redirect to a URL which handles get requests after doing a post, so you don't end up performing the post twice. Thanks very much for your help.

Pen answered 7/7, 2010 at 11:15 Comment(1)
I seem to have the same problem, do you have the code (or a global description) of your solution, how did you redirect the get after the post?Cotillion
P
1

I'm not 100% sure, but please try this one:

<script type="text/javascript">
   $(document).ready(function() {
      $("#create-deck-form").submit(function(event){
         if(!$("#deck-name-field").val().length) {
           return false;
         }
       });
   });
</script>

if that does not work, please have a look at

for(e in $("#create-deck-form").data('events'))
    alert(e);

This will alert you all events which are bound to your form. Maybe there are more event handlers? Otherwise also alert/log

$("#create-deck-form").attr('onclick')

and

$("#create-deck-form").attr('onsubmit')

just in case.

Percussion answered 6/7, 2010 at 13:5 Comment(4)
Yep pretty sure you need to return false to stop the browser default event happening as wellDotted
Thanks Andy and Tom. I think I may have confused the issue by mentioning the validation. Where validation fails I'm fine (event.preventDefault is equivalent to return false and both are fine) it is when the form submits with no validation issues that I get trouble. So, as I show above in the first code snippet, the basic form submission with no validation or JS involvement submits to the server twice. Great idea about checking for any rogue handlers associated with the form, Andy, I'll have a try.Pen
@meg: not exactly, returning false from an event handler triggers both, preventDefault plus stopPropagation.Percussion
@Andy, I can confirm that there are no unexpected handlers bound to the form. @jAndy, I see! Thanks very much. Not sure how that helps with my current problem though. In my case I'm getting the unexpected behaviour when validation passes and therefore neither code preventing default behaviour nor returning false is hit.Pen
C
1

Browsers (Safari Desktop for instance) mask form submit events as click events. So you might run into an issue of forms being submitted even if you have an evt.preventDefault() or evt.stopPropagation() set on your form's submit event.

Good example and scenario for this is submitting a form by hitting the ENTER key. In that case Safari emits a click event that might cause other event listeners to fire, etc.

Solution:

You can listen to those masked submit events like this:

$('[type="submit"]', $('form')).on('click', function(evt) {
    ...
}

Now you only want to catch those events that are actually masked; you don't want to catch a mouseclick on a submit button.

Therefore you have to distinguish real clicks from those rogue ones. You can do so by checking the evt.originalEvent object.

If you can find a pointerId (Chrome) key than rogue clicks come with a pointerId = -1 (some negative value); if you don't have a pointerId (eg. Safari) you can look for the evt.originalEvent.detail key (Safari) which in case of rogue clicks comes with the value of 0 (zero).

Full Example

$('[type="submit"]', $('form')).on('click', function(evt) {
    let do_not_submit = false
    // chrome
    if (typeof evt.originalEvent.pointerId !== 'undefined' && evt.originalEvent.pointerId < 0) {
        do_not_submit = true
    }
    // safari
    if (!do_not_submit && typeof evt.originalEvent.detail !== 'undefined' && evt.originalEvent.detail === 0) {
        do_not_submit = true
    }
    if (do_not_submit) {
        evt.preventDefault()
        evt.stopPropagation()
        return
    }
    // no worries ... 
    // submitting ...
}
Celisse answered 19/1 at 12:17 Comment(0)
F
0

Can you try return false; after your alert and remove event.preventDefault();. Maybe you also need return true; in your else-clause. I believe I have that working somewhere.

Fremitus answered 6/7, 2010 at 13:0 Comment(3)
Thank you, Koen. Where validation fails, the behaviour is all good (and yes, either event.preventDefault(); or return false; perform as expected). Wherever validation passes the form is submitted twice. This is the case where the default behaviour is explicitly called (using an else-clause to return true) or implicitly called by not preventing the default/returning false (as in the code snippets above).Pen
Have you tried using a non-anonymous method (defining it with a name) and using it as an attribute, e.g.: <form ... onsubmit="return validationMethod();">. If that also posts twice, the problem is elsewhere in your code...Fremitus
Sure did @Koen, thanks. Wasn't the form submission that cause the problem, just where the freakiness could be started from.Pen
K
0

Ok, not sure how much this will help, but try removing the submit input and submitting the form via javascript. So for instance

<input id="submit-button" class="add-button" type="button" value="Create" />

Then instead of listening to the onsubmit, you can try

$('#submit-button').click(function(evt) {
    if(validationSuccess(...)) {
        $("#create-deck-form").submit();
    } else {
        //display whatever
    }
});

Now you're way should work too... but this way you can debug where you're problem is.... This should only submit once... good luck!

Kurrajong answered 6/7, 2010 at 19:56 Comment(3)
Thanks JavaDrinker, I have indeed attempted this approach to no avail. I've been digging and it may be something much deeper in my app. I hope to resolve today and post the resolution.Pen
This maybe a dumb question but: how are you determining that your form is being submitted twice? I mean, what is the result of this "double submit"?Kurrajong
I can see in my console print out that there are two requests to the server and there is a second deck visible in the datastore. Turns out wasn't the form submission, but that's where the behaviour could be kicked off - red herring!Pen

© 2022 - 2024 — McMap. All rights reserved.