Jay
Jay

Reputation: 792

jQuery Validation, scroll to error doesn't work correctly

so i have a 3 stage form which validates at each step, and again on submission. Im having some problems and i think it may possibly be due to the structure of my JS, but I can't be sure.

For some reason it only scrolls to the first error once, once that error is cleared when someone fills in the field it then just scrolls to the top of the page. despite the fact we could now be on a new page. I have copied all relevent code, and stripped out much of the JS as there is some 300 lines. I do have a live url if you wish to see the working copy its 'xxxx'

Here is the JS;

var form = $("#msform");
    form.validate({
        rules: {
            age: { 
                digits: true,
                minlength: 2
            },
            group: {
                required: true
            },
            product: {
                required: true
            },
            tan: {
                required: true
            },
            exfoliate: {
                required: true
            },
            advertising: {
                required: true
            },
            morizLove: {
                required: true
            },
            morizMoreProducts: {
                required: true
            },
            terms: {
                required: true
            },
            media: {
                required: true,
                minlength: 4
            },
            other: {
                required: true              
            },
            buyother: {
                required: true,
                minlength: 4
            },
        },
        messages: {
            fullname: { 
                required: "Please tell us your full name"
            },
            age: { 
                required: "Please tell us your age"
            },
            email: { 
                required: "Please enter a valid email"
            },
            address: { 
                required: "Please tell us your Address"
            },
            postcode: { 
                required: "Please tell us your Post Code"
            },
            product: { 
                required: "Please select a product"
            },
            tan: { 
                required: "Please tell us how often you self tan"
            },
            buymost: { 
                required: "Please tell us which product you use most"
            },
            group: {
                required: "Please select at least 1"
            },
            maintain: {
                required: "Please select at least 1"
            },
            advertising: {
                required: "Please select at least 1"
            },
            morizMoreProducts: {
                required: "Please tell us what products you would like to see from St. Moriz"
            },
            morizLove: {
                required: "Please tell us what you love most about St. Moriz"
            },
            terms: {
                required: "Please accept our Terms & Conditions"
            },
            media: {
                required: "Please tell us what media you have seen advertising this campaign"
            },
            other: {
                required: "Please tell us where you normally buy St. Moriz products from"
            },
            buyother: {
                required: "Please tell us which product you use most"
            },
        },
        errorPlacement: function(error, element) {
            switch(element.attr("name")) {
            case "product": 
                error.insertAfter( $(".product-error") );
                break;
            case "tan": 
                error.insertBefore( $(".tan-error") );
                break;
            case "group": 
                error.insertAfter( $("#other") );
                break;
            case "exfoliate": 
                error.insertAfter( $(".exfoliate-error") );
                break;
            case "maintain": 
                error.insertAfter( $(".maintain-error") );
                break;
            case "terms": 
                error.insertBefore( $(".terms-error") );
                break;
            case "advertising": 
                error.insertBefore( $(".advertising-error") );
                break;
            default:
                // the default error placement for the rest
                error.insertAfter(element);
            }
   }
    });

$(".submit").click(function(){
    $("#msform").validate();

    if (form.valid() == false){

        $('html, body').animate({
       scrollTop: ($('.error').offset().top - 60)
    }, 500);
    }
});


$(".next").click(function(){
    // if(animating) return false;
    animating = true;

    current_fs = $(this).parent();
    next_fs = $(this).parent().next();


        if (form.valid() == true){

            //show the next fieldset
            next_fs.show();
            //hide the current fieldset with style
            current_fs.animate({opacity: 0}, {
                step: function(now, mx) {
                    //as the opacity of current_fs reduces to 0 - stored in "now"
                    //1. scale current_fs down to 80%
                    scale = 1 - (1 - now) * 0.2;
                    //2. bring next_fs from the right(50%)
                    left = (now * 50)+"%";
                    //3. increase opacity of next_fs to 1 as it moves in
                    opacity = 1 - now;
                    current_fs.css({
                'transform': 'scale('+scale+')',
                'position': 'absolute'
              });
                    next_fs.css({'left': left, 'opacity': opacity, 'position': 'relative'});
                },
                duration: 800,
                complete: function(){
                    current_fs.hide();
                    animating = false;
                },
                //this comes from the custom easing plugin
                easing: 'easeInOutBack'
            });     

                //activate next step on progressbar using the index of next_fs
            $("#progressbar li").eq($("fieldset").index(next_fs)).addClass("active").find('img').attr("src","images/boardActive.png");

            // Scroll to top of next step form.

            $('html, body').animate({
         scrollTop: $("#progressbar").offset().top - 200
            }, 500);
            return false;

        }   
        else { // Scrolls to top error
            $('html, body').animate({
         scrollTop: ($('.error').offset().top - 60)
        }, 500);
        }

});

$(".previous").click(function(){
    // if(animating) return false;
    animating = true;

    current_fs = $(this).parent();
    previous_fs = $(this).parent().prev();

    //de-activate current step on progressbar
    $("#progressbar li").eq($("fieldset").index(current_fs)).removeClass("active").find('img').attr("src","images/board.png");;

    //show the previous fieldset
    previous_fs.show();
    //hide the current fieldset with style
    current_fs.animate({opacity: 0}, {
        step: function(now, mx) {
            //as the opacity of current_fs reduces to 0 - stored in "now"
            //1. scale previous_fs from 80% to 100%
            scale = 0.8 + (1 - now) * 0.2;
            //2. take current_fs to the right(50%) - from 0%
            left = ((1-now) * 50)+"%";
            //3. increase opacity of previous_fs to 1 as it moves in
            opacity = 1 - now;
            current_fs.css({'left': left});
            previous_fs.css({'transform': 'scale('+scale+')', 'opacity': opacity, 'position': 'relative'});
        },
        duration: 800,
        complete: function(){
            current_fs.hide();
            animating = false;
        },
        //this comes from the custom easing plugin
        easing: 'easeInOutBack'
    });

});

I know there is a lot of code there but I think it could be a structural issue. Thanks very much for taking the time to read this. Please see the IP address for a working copy of how the form reacts to validation.

Thanks very much for taking the time to read this.

EDIT: removed live link as question has been answered.

Upvotes: 4

Views: 4798

Answers (1)

Kashkain
Kashkain

Reputation: 523

$('html, body').animate({
    scrollTop: ($('.error:visible').offset().top - 60)
}, 500);

Your selector match with all error classes despite if is displayed or none. And in your case the animate function took the first matched item in the list.

When you add ":visible" you selected only the displayed element on your page.

Upvotes: 8

Related Questions