ViktorG
ViktorG

Reputation: 515

Native date input ignores CSS

I have a problem with native inputs of type date. My case consists of a native form with multiple native inputs of different types (text, number, date, etc.) The application featuring the form has a sticky header which results in the following behaviour:

Whenever the form is submitted and the form validation encounters an invalid input for an input field, the form automatically scrolls so that the affected field shows up at the very top of the browser, while showing the validation error message. This field is obscured by the sticky header.

I solved this problem by using the scroll-margin CSS property for the input fields, which respects the height of the sticky header.

This works great for all input types, except for date input fields.

I was not able to find any official bug reports. Has anyone else encountered this behaviour? If so, how could I fix this, without the use of JQuery?

body {
  max-width: 500px;
  font-family: Roboto, sans-serif;
  line-height: 1.4;
  margin: 0 auto;
  padding: 4rem 1rem;
  font-size: 1.5rem;
}

header {
  position: fixed;
  top: 0;
  left: 0;
  text-align: center;
  color: white;
  width: 100%;
  padding: 1rem;
  background: #1976D2;
}

input[type=text] {
  scroll-margin-top: 150px;
}

input[type=date] {
  scroll-margin-top: 150px;
}

input[type=submit] {
  margin-top: 500px;
}
<header>
  Fixed Header.
</header>

<form>
  <input type="text" required/>
  <br/>
  <input type="date" required/>
  <br/>
  <input type="submit">
</form>

Upvotes: 5

Views: 1172

Answers (2)

Alexandre Elshobokshy
Alexandre Elshobokshy

Reputation: 10922

Who needs javascript when it can be done with a single line of CSS? Just add html { scroll-padding-top: 70px; } :)

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

body {
  max-width: 500px;
  font-family: Roboto, sans-serif;
  line-height: 1.4;
  margin: 0 auto;
  padding: 4rem 1rem;
  font-size: 1.5rem;
}

header {
  position: fixed;
  top: 0;
  left: 0;
  text-align: center;
  color: white;
  width: 100%;
  padding: 1rem;
  background: #1976D2;
}

input[type=submit] {
  margin-top: 500px;
}
<header>
  Fixed Header.
</header>

<form>
  <input type="text" required/>
  <br/>
  <input type="date" required/>
  <br/>
  <input type="submit">
</form>

https://jsfiddle.net/m4uxj321/

Upvotes: 4

jadinerky
jadinerky

Reputation: 100

I'm not sure I totally understand your objective but from the demo I can see that you maybe have a long form with a lot of inputs and you want to scroll back to the input with an alert, you can accomplish that with JavaScript easily using

document.querySelector('#element').scrollIntoView();

you can make the scrolling animation smooth by adding

document.querySelector('#element').scrollIntoView({behavior: "smooth"});

But I also think you want to use the browser's default validation, so this is how to detect the input that has an invalid value, and scroll to it.

const element = document.getElementById("element");
submitButton.addEventListener("click", function (event) {
  if (element.validity.typeMismatch) {
    // scroll to the invalid input
    document.querySelector(element).scrollIntoView({behavior: "smooth"});
   
    // Show a custom Error/Warning
    email.setCustomValidity("You need to fill this !!?!");
  } else {
    email.setCustomValidity("");
  }
});

if that's what you want, then I advice you to stay away from CSS and stick with JavaScript, you can also check the DOM Object ValidityState that is accessible through element.validity, it has all the browser's defalut validation info you need. you can also read Client-side form validation and Element.scrollIntoView()

EDIT: @ViktorG is right, scrollIntoView() would never get the job done without some complex trick, but we can still use the normal scroll and prevent the default browser behavior while making use of the browser's alerts using reportValidity(), sadly it's not possible to use preventDefault() just on the scroll and keeping the alert since it doesn't take parameters.

the snippet demo is working good enough but ofc you need to tweak it to fit your desired result.

const el = e => document.querySelector(e),
    ela = e => document.querySelectorAll(e),
    btn = el('#btn');

btn.addEventListener('click', () => {
    let len = ela('form input').length - 1; // -1 is meant to stop before we reach the button, if you're using button then remove it

    for (let i = 0; i < len; i++) {
        if (!el('form')[i].checkValidity()) {
            event.preventDefault();

            /*
             *   We're scrolling to the element position - the header height * 2, to always make the element visible.
             * */
            window.scroll({
                top: el('#input').offsetTop - (el('header').scrollHeight * 2),
                behavior: "smooth"
            });

            /*
             *   The Delay is meant to prevent reportValidity() from scrolling till the first scrolling fase is done.
             * */
            setTimeout(() => {
                el('form')[i].reportValidity(); // we manually trigger the browser's validation
            }, 800);

            break; // we break because we only show one alert at a time
        }
    }
});
body {
            max-width: 500px;
            font-family: Roboto, sans-serif;
            line-height: 1.4;
            margin: 0 auto;
            padding: 4rem 1rem;
            font-size: 1.5rem;
        }
        
        header {
            position: fixed;
            top: 0;
            left: 0;
            text-align: center;
            color: white;
            width: 100%;
            padding: 1rem;
            background: #1976D2;
        }
        
        input[type=text] {
            scroll-margin-top: 150px;
        }
        
        input[type=date] {
            scroll-margin-top: 150px;
        }
        
        input[type=submit] {
            margin-top: 500px;
        }
<header>
        Fixed Header.
      </header>
      
      <form>
        <input type="text" required/>
        <br/>
        <input type="text" name="sdsdsd" id="sdds" required/>
        <br>
        <input type="date" id="input" required/>
        <br/>
        <input type="submit" id="btn">
      </form>

Upvotes: 0

Related Questions