3

I am using the jQuery validation plugin and am trying to adapt the function focusInvalid to my needs. On submit (with invalid fields) I want to:

  • Scroll to the first invalid element of the form
  • Focus on this element

The validation has to behave almost the same on an iPad/iPhone/... as on a Desktop, which is not trivial as one can only focus an element in iOS based on a user trigger. But that's a different story - see iPad HTML focus for more details.

My first approach was to modify jquery.validate.js directly in order to test my adaptations. This is what I came up with:

focusInvalid: function() {
    if ( this.settings.focusInvalid ) {
        try {
            var firstInvalidElement = $(this.errorList[0].element);
            $('html,body').scrollTop(firstInvalidElement.offset().top);
            firstInvalidElement.focus()
        } catch ( e ) {
            // ignore IE throwing errors when focusing hidden elements
        }
    }
}

Works as expected.

Now I would love to override the default behavior of focusInvalid externally (as in: not modify the original jQuery validation source code).

Here is what I tried so far:

So, for example:

invalidHandler: function(form, validator) {
    var errors = validator.numberOfInvalids();
    if (errors) {                    
        var firstInvalidElement = $(validator.errorList[0].element);
        $('html,body').scrollTop(firstInvalidElement.offset().top);
        firstInvalidElement.focus();
    }
}

Unfortunately I am now able to submit the form (even though there are still invalid fields) if I press the ENTER key instead of using the submit button. If I remove this custom invalidHandler, the validation does not have this behaviour.

Any ideas where this change in behaviour comes from?


Edit:

I have created a JSFiddle to demonstrate the issue. In order to reproduce:

  • Enter a valid email address
  • Press ENTER key

Edit:

Here are the code snippets as pasted in the JSFiddle.

HTML:

<div class="pre-login">
    <form action="login" method="post" id="login">
        <div class="form-group">
            <input type="email" class="form-control" name="email" id="email" placeholder="Email Address" autofocus="autofocus" required />
        </div>
        <div class="form-group">
            <input type="password" class="form-control" name="pass" id="pass" placeholder="Password" required />
        </div>
        <button type="submit" class="btn btn-success btn-block">Submit</button>
    </form>
</div>

JavaScript:

$(function () {
    $.validator.setDefaults({
        highlight: function (element) {
            $(element).closest('.form-group').addClass('has-error');
        },
        unhighlight: function (element) {
            $(element).closest('.form-group').removeClass('has-error');
        },
        errorElement: 'span',
        errorClass: 'error-message',
        errorPlacement: function (error, element) {
            if (element.parent('.input-group').length) {
                error.insertAfter(element.parent());
            } else {
                error.insertAfter(element);
            }
        }
    });

    $('#login').validate({
        invalidHandler: function(form, validator) {
            var errors = validator.numberOfInvalids();
            if (errors) {                    
                var firstInvalidElement = $(validator.errorList[0].element);
                $('html,body').scrollTop(firstInvalidElement.offset().top);
                firstInvalidElement.focus();
            }
        },
        rules: {
            email: {
                required: true,
                email: true
            },
            pass: {
                required: true
            }
        }
    });
});
Community
  • 1
  • 1
gartoffel
  • 33
  • 1
  • 5
  • If you can submit the form with errors, then you've broken the plugin. We cannot see the relevant HTML or your complete call to `.validate()` so the problem could be anywhere. Otherwise, if you're trying to override the `focusInvalid` handler, then why would you modify the `invalidHandler`? They're not the same. – Sparky Apr 12 '15 at 16:36
  • Not sure yet what else you can try. However, the `invalidHandler` would be the next logical place since it's fired on an invalid form. However, there is nothing about your function that would cause the form to submit when it's invalid, so you're going to have to show enough code to duplicate the issue... **the relevant HTML of the form and the entire call to `.validate()`... *reduce* the code to just enough that still reproduces the issue.** – Sparky Apr 12 '15 at 17:42
  • Okay, thanks. I created a [JSFiddle](https://jsfiddle.net/kfopux67/2/). That way it should be easier to get to the bottom of this. You can try and add either the invalidHandler or the focusInvalid function and should be able to reproduce the issue I'm facing. – gartoffel Apr 12 '15 at 18:02
  • I'll look at the jsFiddle, meanwhile, please edit your OP to include the relevant code. We do not accept jsFiddle as a substitute for posting the code. Thanks. See: [how-to-ask](http://stackoverflow.com/help/how-to-ask) ~ *"If it is possible to create a live example of the problem that you can link to (for example, on http://sqlfiddle.com/ or http://jsbin.com/) then do so - **but also include the code in your question itself**. Not everyone can access external sites, and the links may break over time."* – Sparky Apr 12 '15 at 18:10
  • Sure, I see your point. I pasted the HTML and JS code here. – gartoffel Apr 12 '15 at 18:19

2 Answers2

0

It's this line within your invalidHandler function that is causing the erroneous "submission on Enter" while you still have errors, and I don't know why.

firstInvalidElement.focus();

Setting focusInvalid to false has no effect. Perhaps it's a bug that you should report to the developer on his GitHub page. (The focusInvalid() function is used within the plugin's internal submit handling function.)

However, I don't know why you'd even need to do this here. The default focusInvalid handler is already bringing focus to the first invalid field. Even without the bug, it would be superfluous to also try to focus it from within the invalidHandler.

DEMO: https://jsfiddle.net/kfopux67/4/

Sparky
  • 98,165
  • 25
  • 199
  • 285
  • The focusInvalid handler does not bring focus to the first invalid field. As described on the developer's website: "focusInvalid(): Focus the last active or first invalid element on submit via validator.focusInvalid(). The last active element is the one that had focus when the form was submitted, avoiding stealing its focus. If there was no element focused, the first one in the form gets it, unless this option is turned off." – gartoffel Apr 12 '15 at 18:46
  • @gartoffel, How is that relevant? You need focus to happen in the `invalidHandler` (when the form is invalid)... and *when the form is invalid*, it already brings focus to the first invalid element. See demo above. – Sparky Apr 12 '15 at 18:48
  • Otherwise, you'll have to take this to the developer's GitHub site if you need to report a bug or complain about how it works. There is really nothing broken in your code that would need the help of the StackOverflow community. – Sparky Apr 12 '15 at 18:50
  • Unfortunately: No. Follow these steps: Do not enter an email address. Then put your cursor in the password field and submit. You will see that the password field will be focused even though the first invalid field is email. So the last active element is focused, not the first invalid element. That may be okay for a short form but it's very annoying for a longer one. – gartoffel Apr 12 '15 at 18:51
  • Okay, thanks for your help. Will mark this as "most helpful" and therefore resolved. Have a good one! – gartoffel Apr 12 '15 at 18:52
  • @gartoffel, I followed the steps in your last comment and the focus simply remains where I left my cursor. – Sparky Apr 12 '15 at 18:53
  • Exactly. Now imagine a long form and the first invalid field at the very top. You will not even notice that error because your cursor remains at the bottom. That's why I was trying to change the code and scroll to the location of the first invalid field. – gartoffel Apr 12 '15 at 18:55
0

I know this is a very old post, but it might help others. You just need to make focusInvalid to false and write a invalidHandler which will focus on the first error element (here is first textbox)

$("form").validate({
    focusInvalid: false,
    invalidHandler: function() {
      $(this).find(":input.error:first").focus();
    }
});
koolhuman
  • 1,581
  • 15
  • 25