HTML5 input required, scroll to input with fixed navbar on submit
JavascriptCssHtmlJavascript Problem Overview
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:
Instead of
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/
Javascript Solutions
Solution 1 - Javascript
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.
Solution 2 - Javascript
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
Solution 3 - Javascript
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>
Solution 4 - Javascript
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)
}
})
Solution 5 - Javascript
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.
Solution 6 - Javascript
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 :)
Solution 7 - Javascript
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)
Solution 8 - Javascript
Two solutions:
-
One: apply padding to the body -->
body { padding-top:50px; }
-
Two : apply margin to the main container -->
#content { margin-top:50px; }
Solution 9 - Javascript
Here's an EASY and FAST way.
$('input').on('invalid', function(e) {
setTimeout(function(){
$('html, body').animate({scrollTop: document.documentElement.scrollTop - 150 }, 0);
}, 0);
});