62

I've seen many questions with variations on this theme, but I'm looking for the straightforward solution:

HTML form, jQuery validation, multiple fields are required. When the form is submitted, validation jumps to the first error and highlights it. To increase usability, I want to scroll to that first error field. But it keeps blowing up the validation entirely or throwing scrollTo errors.

I need to use the standard validation plugin (http://docs.jquery.com/Plugins/Validation) but any scroller would be fine, tho I had been trying with scrollTo (http://flesler.blogspot.com/2007/10/jqueryscrollto.html).

Sample code is at http://jsfiddle.net/DtgKQ/1/, any help is appreciated.

Heraldmonkey
  • 2,111
  • 2
  • 19
  • 29

9 Answers9

140

Here's what you can do:

  • By default the validate plugin focuses the first erroneous element (in case there's any). Turn off the option focusInvalid by setting it to false.

  • The callback invalidHandler handler is executed when the form is invalid. You get access through the second parameter validator to the validator object and thus to the errorList array. You can then animate the scroll relatively to the first erroneous element.

Here's the code:

$("#commentForm").validate({
    focusInvalid: false,
    invalidHandler: function(form, validator) {

        if (!validator.numberOfInvalids())
            return;

        $('html, body').animate({
            scrollTop: $(validator.errorList[0].element).offset().top
        }, 2000);

    }
});

DEMO

Didier Ghys
  • 30,396
  • 9
  • 75
  • 81
  • Excellent stuff, thank you Didier. As it works now, it scrolls to the top of the element. How could I get it to scroll to 20px higher than that? – Heraldmonkey Feb 22 '12 at 16:14
  • 7
    figured it out: '$(validator.errorList[0].element).offset().top-20' – Heraldmonkey Feb 23 '12 at 12:25
  • I knew you could find it out all by yourself ;o) – Didier Ghys Feb 23 '12 at 13:05
  • good answer - I had to do the same, when my form logic prevented the validator from scrolling to the top. – Jason Jun 06 '12 at 13:01
  • 7
    Cool! I also recommend adding $(validator.errorList[0].element).focus(); so the field is focused afterwards – Dmitry Dzygin Oct 17 '12 at 08:49
  • 2
    ^ best done after the animation by adding it to an anonymous function after the 2000 – Mike Causer Jul 05 '13 at 03:42
  • 1
    You could also skip scrolling up if the invalid element is already visible. Less jerky imho – Mike Causer Jul 05 '13 at 03:43
  • 4
    (http://stackoverflow.com/a/7557433/5628) to skip scrolling if element is in the viewport @MikeCauser – James Harrington Jun 24 '14 at 14:14
  • What version of jQuery Validate does this work with? I have v1.5.5 and the `invalidHandler` doesn't catch my errors. – crmpicco Sep 09 '14 at 10:05
  • @crmpicco If you open the jsfiddle you'll see then it's the version 1.9 – Didier Ghys Sep 10 '14 at 11:03
  • Adding a anonymous function for focus just after animate would make it great. Try this - var applyFocus = function(){$(validator.errorList[0].element).focus();}; window.setTimeout(applyFocus, 2300); – Ashis Feb 09 '15 at 11:54
  • @Heraldmonkey In this way it will be more global $(validator.errorList[0].element).offset().top - $(validator.errorList[0].element).height() – Roy Shoa Dec 31 '15 at 14:50
  • Note that if your element is in some kind of modal or iframe you will need to set the scroll on that modal/iframe instead of 'html, body'. – levininja Nov 24 '16 at 07:00
13

I dont like all the jQuery extentions so here is my solution to this problem:

if ($('#MYID').valid()) {
      //dosomething();
} else {
    $('html, body').animate({
         scrollTop: ($('.error').offset().top - 300)
    }, 2000);
}
Thomas
  • 135
  • 1
  • 6
7

just add this code to your themes javascript:

(function($) {
$(document).ready(function(){
    //bmo scroll to not valid
    $(".wpcf7").on('invalid.wpcf7',function(e){
        $('html, body').animate({
                scrollTop: $(".wpcf7-not-valid").first().offset().top-30
            }, 2000);
    });

});
})(jQuery);
user3629980
  • 71
  • 1
  • 3
4

For Anyone Using HTML5 Validation + Vanilla JavaScript:

Not a direct answer to this question, but as this is the main post that comes up when searching for this question, I figured I'd post my solution here for anyone who's looking as well!

I'm using this in my ReactJS project, but it should work in just about any vanilla ES6 JavaScript setting using modern browsers, as well.

function scrollToInvalid(form) {
  const invalidInputs = Array.from(form.querySelectorAll(':invalid, .is-invalid [, .custom-invalid-selector]'));    // set up so you can use any custom invalid classes you're adding to your elements, as well
  invalidInputs.sort((a, b) => a.getBoundingClientRect().top - b.getBoundingClientRect().top);                      // sort inputs by offset from top of viewport (handles issues with multi-column layouts, where the first element in the markup isn't necessarily the highest on the page)
  invalidInputs[0].scrollIntoView({ block: 'center', behavior: 'smooth' });                                         // scroll first (top) input into center of view, using smooth animation
}

function handleSubmit(e) {
  const form = e.currentTarget;

  if (form.checkValidity() === false ) {
    // form is invalid, don't submit
    e.preventDefault();
    e.stopPropagation();
    // scroll first invalid input into view
    scrollToInvalid(form);
  }
  else {
    // form is valid, handle submit...
  }
}
<form onSubmit="handleSubmit">
  ...
</form>
Fateh Khalsa
  • 1,326
  • 1
  • 15
  • 19
  • doesn't work. if form is invalid it'll auto jump to invalid input, thus handleSubmit is never invoked. – darren z Feb 03 '23 at 18:58
  • @darrenz Not sure if something in browser implementation has changed, but I’ve had this exact code functioning in a production app for the past two years with no problems. Note that this is an answer for vanilla JS and default HTML5 form validation - NOT jQuery validation. – Fateh Khalsa Feb 04 '23 at 19:37
1

you can try the below which uses plain javascript

setTimeout(function () {
            document
                .querySelector(".field-validation-error")
                .scrollIntoView({ behavior: "smooth", block: "center" });
        }, 0);
fallin.1
  • 31
  • 5
1

Maybe you could check what input failed and take it's position (top) and use jQuery's scrollTop

$(window).scrollTop(errorPosition)

It seems that getting each error field isn't very easy to get (at least for me).

Search for errorPlacement in the Validation plugin documentation. There is an example how to get each error field.

naXa stands with Ukraine
  • 35,493
  • 19
  • 190
  • 259
Tx3
  • 6,796
  • 4
  • 37
  • 52
0

try this:

$( "input[type=submit]" ).click(function() {
    event.preventDefault();
    event.stopPropagation();
    //  console.log("test")
    var errorElements = document.querySelectorAll(".input-validation-error");
  for (let index = 0; index < errorElements.length; index++) {
      const element = errorElements[index];
    //  console.log(element);
      $('html, body').animate({
        scrollTop: $(errorElements[0]).focus().offset().top - 25
      }, 1000);      
      return false;
  }
});
BetoSEC
  • 31
  • 4
0
**Get the classname of input - get this div topoffset - move to this div**
beforeSubmit = function(){

setTimeout(scrollInput, 1000);
   
function scrollInput() {
   alert('hello');
    var list = document.getElementById("cpa-form").getElementsByClassName("has-error");
    if (list && list.length > 0) {
        var gimno = list[0].className;
        
        
        var classes = gimno.split(' ');
        
        var class2 = classes[1];
        
        console.log(class2);
        
        var topme = $('.'+ class2).offset().top;
        
        
        $('html, body').animate({scrollTop: '+='+topme+'px'}, 800);
        
    }
}
-1
  $("#create-form").validate({ // Set Focus on first invalid input
    focusInvalid: false,
    invalidHandler: function() {
      $(this).find(":input.error:first").focus();
      }
  });
Suraj Rao
  • 29,388
  • 11
  • 94
  • 103