64

When trying to submit a form with missing required fields, my browser (Chrome), displays a message mentionning there is a field missing, and if it's out of my screen, it scrolls up to it.

My problem is that I have a 50px fixed header in my webpage, and as a result, the input field is hidden, and the message seems to come out of nowhere:

Input field hidden

Instead of

Input field shown

Is there a way around this?

I tried both applying the 50px margin to <html> and to <body>

Cheers


EDIT

Here's a fiddle of the problem: http://jsfiddle.net/LL5S6/1/

4
  • Had the exact same problem, shame nobody came up with a fix for this. Commented Jul 28, 2014 at 18:23
  • 1
    I think we need to consider the required="required" html attribute as something unstable at the moment... It is tricky to use, not highly compatible, and still has side effects like this one... Commented Aug 13, 2014 at 13:19
  • using Chrome Version 37.0.2062.120 it works like you want. have you tried already? Commented Sep 17, 2014 at 9:27
  • try this: jsfiddle.net/LL5S6/46 Commented Sep 17, 2014 at 10:11

9 Answers 9

42

I had the exact same problem and resolved it using jquery with this bit of code:

var delay = 0;
var offset = 150;

document.addEventListener('invalid', function(e){
   $(e.target).addClass("invalid");
   $('html, body').animate({scrollTop: $($(".invalid")[0]).offset().top - offset }, delay);
}, true);
document.addEventListener('change', function(e){
   $(e.target).removeClass("invalid")
}, true);

Offset should be the height of your header and delay is how long you want it to take to scroll to the element.

Sign up to request clarification or add additional context in comments.

2 Comments

Exactly what I needed! But be careful: If you use a delay > 0, then the browser message "Please fill out this field" will disappear as soon as scrolling starts (so it's only visible for a few milliseconds)
IMO this solution is wrong, as the 'invalid' event does not bubble according to developer.mozilla.org/en-US/docs/Web/Events/invalid
26

In modern browsers there is a new CSS property for that use case:

html {
    scroll-padding-top: 50px;
}

Your JSFiddle updated: http://jsfiddle.net/5o10ydbk/

Browser Support for scroll-padding: https://caniuse.com/#search=scroll-padding

2 Comments

This is amazing! Could you update your answer with compatibility caniuse.com/#feat=mdn-css_properties_scroll-padding-top and I'll accept it?
Also of interest is scroll-margin . Unlike scroll-padding, you wouldn't use this on the html element, you would use this on the <input> field instead.
26

The only way I found is adding an 'override' to the invalid handler. To implement this for every input in your form you can do something like this.

var elements = document.querySelectorAll('input,select,textarea');
var invalidListener = function(){ this.scrollIntoView(false); };

for(var i = elements.length; i--;)
    elements[i].addEventListener('invalid', invalidListener);

This requires HTML5 and this is tested on IE11, Chrome and Firefox.
Credits to @HenryW for finding that scrollIntoView works like expected.

Note that the false parameter for scrollIntoView aligns the input with the bottom, so if you have a large form it may be aligned with the bottom of the page.

jsfiddle

5 Comments

Thanks @HenryW I will add it to the answer.
Pretty nice! Though a browser debug would be even better!
scrollIntoView does not work with fix positioned headers at the top of the page.
@feeela It solved the problem for OP which had a fixed positioned header. Can you please specify what does not work?
This doesn't work if there are multiple invalid inputs because it will scroll to the last invalid input and then the browser will scroll back to the first at the top and hidden by the fixed navbar. There should be a way to execute scrollIntoView only for the first invalid input but I don't know how.
9

When there are several invalid inputs in the form, you only want to scroll to the first of them:

var form = $('#your-form')
var navbar = $('#your-fixed-navbar')

// listen for `invalid` events on all form inputs
form.find(':input').on('invalid', function (event) {
    var input = $(this)

    // the first invalid element in the form
    var first = form.find(':invalid').first()

    // only handle if this is the first invalid input
    if (input[0] === first[0]) {
        // height of the nav bar plus some padding
        var navbarHeight = navbar.height() + 50

        // the position to scroll to (accounting for the navbar)
        var elementOffset = input.offset().top - navbarHeight

        // the current scroll position (accounting for the navbar)
        var pageOffset = window.pageYOffset - navbarHeight

        // don't scroll if the element is already in view
        if (elementOffset > pageOffset && elementOffset < pageOffset + window.innerHeight) {
            return true
        }

        // note: avoid using animate, as it prevents the validation message displaying correctly
        $('html,body').scrollTop(elementOffset)
    }
})

JSFiddle

3 Comments

This answer worked best for me... I had a very long form, and without worrying about 'first error only', it would always scroll too far down.
Work fine here too, after having added the navbarHeight in this math: if ( ( elementOffset > ( pageOffset + navbarHeight ) ) && ( elementOffset < ( pageOffset + window.innerHeight ) ) ) {
Mind that form.find(':invalid').first() will return a fieldset if your input is within a fieldset.
5
+50

ok, i did a dirty test with a code snippet i found here on SO

As it is a code from someone else, i just alter it to scroll to the element that had a missing input requirement. I do not want any credit for it, and it maybe is not even what you have in mind, you or someone else could use it as a reference.

The goal was to get the id of the forgotten/wrong input element:

            var myelement = input.id;
            var el = document.getElementById(myelement);
            el.scrollIntoView(false);

Please keep in mind that this fiddle only works for your posted fiddle above, it not handles multiple forgotten or wrong input fields.I only wanted to show an alternative.

----->jSFiddle

5 Comments

You just solved a puzzle by putting the right pieces together ;) I've came up with an even shorter solution which I will post in a second.
See this fiddle. Because you specify false to scrollIntoView the element is at the bottom of the page. So the solution is not perfect (mine isn't too)
i think needs to have false, if true, it not work. i used THIS as my reference to give the answer
if can tweak the alignWithTop by adding the margin-top: 50px; or getting the fields position, both codes are good to go :) , it is up to the OP to fine-tune i think.
My first solution was getting the getBoundingClientRect and check if the top was less than the bottom of the header.. and then scroll the window accordingly. I think scrollIntoView is acceptable ;)
1

I tried to use the way of T.J. Moats, but it did not work as needed, because I often came back to the field, which was incorrect first.

So, I made it:

var navhei = $('header').height();
var navheix = navhei + 30;
document.addEventListener('invalid', function(e){
$(e.target).addClass("invalid");
   $('html, body').animate({scrollTop: $($(".invalid")[0]).offset().top - navheix }, 0);
 
setTimeout(function() {
$('.invalid').removeClass('invalid');
},0300);
}, true);
body {
    margin: 0;
    margin-top: 50px;
    text-align: center;
}

header {
    position: fixed;
    width: 100%;
    height: 50px;
    background-color: #CCCCCC;
    text-align:center;
    top: 0;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.4.1/jquery.min.js"></script>
<header>This is the header</header>
<div>
<form action="">
<br>
    <input id="text" type="text" required="required" /><br><br>
    <input id="text" type="text" required="required" /><br><br><br><br>
    <input id="text" type="text" required="required" /><br><br>
    <input id="text" type="text" required="required" /><br><br><br><br>
    <input id="text" type="text" required="required" /><br><br><br><br><br><br>
    <input id="text" type="text" required="required" /><br><br>
        <p>Click send (at the bottom of the page), without filling the input field.</p><br><br><br><br><br><br>
        <input id="text" type="text" required="required" /><br><br>
    <input type="submit" id="btnSubmit" />
    </form>
</div>

I hope it will be helpfull for people :)

Comments

1

You can use oninvalid event attribute of HTML5 and in your script's tag write a function for redirecting it.

Here is the example:

<input type="text" required oninvalid="scroll_to_validator(this)">

<script>
    function scroll_to_validator(input)
        {
        input.focus(); 
        }
</script>

And on clicking on your submit button it will scroll to the invalid field.

For radio button please add only on one radio with same name
Here is the example (jsfiddle)

1 Comment

Hello Abhilash. I see that you are a new user. For your information, I have formatted your code using {} for source code. This is better than using ` characters to highlight text and using a lot of <br/> to simulate new-line.
0

Two solutions:

  • One: apply padding to the body -->

    body {
     padding-top:50px;
    }
    
  • Two : apply margin to the main container -->

     #content {
       margin-top:50px;
     }
    

4 Comments

apparently you can't solve this .. it's a problem with the function of the send button who leads the page right to the input field. like an inside reference.
Couldn't you add a hidden element 50px below the text bar and make the send buttpn lead on there?
The problem here is you can't modfify the function ... is html5 or can you?
Funny thing is that (with Chrome), when you change focus with tab key, it scrolls correctly (respecting the 50px offset), whereas when scrolling on submit, it doesn't. Feels buggy...
-1

Here's an EASY and FAST way.

$('input').on('invalid', function(e) {
        setTimeout(function(){
            $('html, body').animate({scrollTop: document.documentElement.scrollTop - 150 }, 0);
        }, 0);
});

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.