- How to target non-empty but invalid input elements with CSS
- Related Topics
- Related Articles
- CSS :not and :empty selectors to apply styles when an element is empty and not empty
- tl;dr
- Applying styles to an empty element
- Applying styles to an element that is not empty
- Browser support
- Examples
- Try it yourself
- Check Out These Related posts:
- Checking if an input is empty with CSS
- The form
- Checking if the input is empty
- Further checks
- Invalidating the input
- The problem with pattern
- Update: Problem solved!
- Wrapping up
- Want to become a better Frontend Developer?
- Want to become a better Frontend Developer?
How to target non-empty but invalid input elements with CSS
Today a Twitter thread about forms by Arslan Khalid passed my timeline. The thread included a smart CSS rule that wasn’t on my radar so far. It targets invalid but not empty (!) input elements. So, why is that cool? Let’s take a step back.
Input elements support multiple HTML attributes that can enable input field validation. These attributes reach from defining an input element’s type to enable email validation, pattern to only allow particular characters or required to mark the element as mandatory to fill.
You could have an email field like the following.
input type="email" placeholder="susi@example.com" required>
How could you show that the field element is invalid after someone interacted with the field? CSS offers the :invalid pseudo-class for that use case (at least theoretically).
input:invalid border: 2px solid red; >
The catch with this pseudo-class is that it matches input elements even when they’re empty and/or the user didn’t interact with them. This is a huge bummer and hopefully the :user-invalid pseudo class gains some better support to solve this issue soon.
Nei | Non | Nope | 88 | Nö | 16.5 | 16.5 | Nein | Non |
Today, if you’re relying on the :invalid pseudo-class to visualize validation state, your users’ form editing experience starts with a bunch of red borders – not great! Unfortunately, the :empty pseudo-class isn’t a great help either, because it targets elements without children. What could you use instead?
:invalid in CSS matches empty fields
While not being the perfect solution either, Arslan’s proposed CSS rule is better than purely relying on :invalid . It uses :invalid in combination with :not(:placeholder-shown) . This way, elements only get marked as invalid when the placeholder is not shown (meaning that they’re not empty anymore).
input:not(:placeholder-shown):invalid border: 2px solid red; >
Input element invalid with placeholder-shown class
Edited: Amy Kapers pointed out that it would be great to combine the selector with :focus to not any validation state while someone is still typing. I agree, that’s a great idea. Thanks, Amy!
input:not(:placeholder-shown):not(:focus):invalid border: 2px solid red; >
Input example showing invalid state after leaving the input
Also, Koen Cornelis brought up the point that placeholders usually have more disadvantages than advantages. If you don’t want to define placeholder values, you can still go with a space to keep the same CSS functionality.
input type="email" placeholder=" " required>
Here’s a quick CodePen to see the selector in action. And also have a look at Aslan’s thread on great forms.
Was this post helpful?
Yes? Cool! You might want to check out Web Weekly for more WebDev shenanigans. The last edition went out 27 days ago.
Related Topics
Related Articles
CSS :not and :empty selectors to apply styles when an element is empty and not empty
There may be times you need to apply a style if an element is empty, or if it is not empty. Use the :not & :empty selectors to do this, as shown in this post. They are supported by all modern browsers, and Internet Explorer from 9 and up.
tl;dr
Applying styles to an empty element
You can use :empty to apply styles to element that contains no content. For example:
You can apply a style to this element if it contains no content like so:
This will put a blue border around the element. Note that because it contains no content, if the element has no padding then it will be more like a line than a bordered element, but it’s just an example.
Note that being empty means there is no content at all, including white space.
Applying styles to an element that is not empty
You can also :not in conjunction with :empty to do the opposite: apply a style when the element is not empty. Consider the following two paragraphs:
When the following style is applied, the first paragraph will have a red border and some padding, and the second paragraph with have no additional styling applied, unless it’s been defined elsewhere:
Browser support
- Chrome 4.0+
- Firefox 3.5+
- Internet Explorer 9.0+
- Safari 3.2+
- Opera 9.6+
I’ve personally tested it myself on the following, which it works on:
- Chrome 36 on Windows 7
- FF29 on Windows 7
- FF30 & FF32 on WindowsXP
- IE9 on Windows 7
- IE10 on Windows 8
- IOS7
- Default browser on Android 4.1.2 on a Sony Xperia Acro S
- Chrome 36 on Android 4.1.2 on a Sony Xperia Acro S
And of course I double checked that it doesn’t work in IE8, and it doesn’t.
Examples
Try it yourself
I given a couple of pretty basic examples but the world is your oyster; try it out where needed and see what you can do. I personally came across :not and :empty when needing to suppress the styling of some empty messages divs in a CMS system – I’d keep seeing blank yellow or blue boxes all over the place. Using :empty I was able to remove the existing styling when the element didn’t have a message, without having to hack the core stylesheet.
Check Out These Related posts:
Checking if an input is empty with CSS
Is it possible to know if an input is empty with only CSS?
I had that question when I tried to make an autocomplete component for Learn JavaScript. Basically, I wanted to:
- Hide a dropdown if the input is empty
- Show the dropdown if the input is filled
I found a way to do it. It’s not perfect. There are a few nuances involved, but I want to share it with you.
The form
First, let’s build a form so we’re on the same page. We’re going to use a simple form with one input.
form> label for="input"> Input label> input type="text" id="input" /> form>
When the input is filled, we want to change its border-color to green. Here’s an example of what we’re creating:
Checking if the input is empty
I relied on HTML form validation to check whether the input is empty. That meant I needed a required attribute.
form> label> Input label> input type="text" name="input" id="input" required /> form>
At this point, it works fine when the input is filled. Borders turned green.
But there’s a problem: If the user enters a whitespace into the field, the borders turn green too.
Technically, this is correct. The input is filled because the user typed something into it.
But I didn’t want whitespaces to trigger a blank dropdown menu (for the autocomplete component).
It wasn’t enough. I needed a more stringent check.
Further checks
HTML gives you the ability to validate inputs with regular expressions with the pattern attribute. I decided to test it out.
Since I didn’t want whitespaces to be recognized, I started with the \S+ pattern. This pattern meant: One or more characters that’s not a whitespace.
form> label> Input label> input type="text" name="input" id="input" required pattern="\S+" /> form>
Sure enough, it worked. If a user enters a whitespace into the field, the input doesn’t get validated.
But when a whitespace is entered (anywhere) into the input, the input gets invalidated.
Unfortunately, this pattern didn’t work in my use case.
In Learn JavaScript’s autocomplete component, I taught students how to complete a list of countries. The names of some countries had spaces…
I had to include whitespaces in the mix.
The next best alternative I could think of is \S+.* . This means 1 or more non-whitespace characters, followed by zero or more (any) characters.
form> label> Input label> input type="text" name="input" id="input" required pattern="\S+.*" /> form>
This worked! I can enter whitespaces into the mix now!
But there’s one more problem… the input doesn’t validate if you START with a whitespace…
And that’s the problem I couldn’t resolve. More on this later.
When I worked on this article, I came across another interesting question: Is it possible to style an invalid state when the input is filled incorrectly?
Invalidating the input
We don’t want to use :invalid because we’ll kickstart the input with an invalid state. (When the input is empty, it’s already invalid).
This is where Chris Coyier swooped in to the rescue with ” Form Validation UX in HTML and CSS“.
In the article, Chris talks about a :placeholder-shown pseudo-class. It can be used to check whether a placeholder is shown.
- You add a placeholder to your input
- If the placeholder is hidden, it means the user typed something into the field
- Proceed with validation (or invalidation)
Here’s the CSS (simplified version. For the complete version, check out Chris’s article)
/* Show red borders when filled, but invalid */ input:not(:placeholder-shown) border-color: hsl(0, 76%, 50%); >
Since I had both validation AND invalidation styles, I had to ensure the valid styles came after the invalid styles.
/* Show red borders when filled, but invalid */ input:not(:placeholder-shown) border-color: hsl(0, 76%, 50%); > /* Show green borders when valid */ input:valid border-color: hsl(120, 76%, 50%); >
Here’s a demo for you to play with:
Note: Edge doesn’t support :placeholder-shown , so it’s probably not a good idea to use it in production yet. There’s no good way to detect this feature.
Now back to the problem I couldn’t resolve.
The problem with pattern
The pattern attribute is wonderful because it lets you accept a regular expression. This regular expression lets you validate the input with anything you can think of.
But… the regular expression must match the text completely. If the text doesn’t get matched completely, the input gets invalidated.
This created the problem I mentioned above. (Reminder of the problem: If a user enters a whitespace first, the input becomes invalid).
I couldn’t find a regular expression that worked for all use-cases that I thought of. If you want to try your hand at creating a regular expression that I need, I’d be more than welcome to receive the help!
// Should not match '' ' ' ' ' ' ' // Should match 'one-word' 'one-word ' ' one-word' ' one-word ' 'one phrase with whitespace' 'one phrase with whitespace ' ' one phrase with whitespace' ' one phrase with whitespace '
(Then again, I might be overthinking it… 🙄).
Update: Problem solved!
Many readers were generous enough to email me their solutions. I want to thank everyone who helped. Thank you so much!
The cleanest solution I received is: .*\S.* by Daniel O’Connor. This means:
- .* : Any character
- \S : Followed one non-whitespace character
- .* : Followed by any character
Other regexes I received include:
Here’s a codepen with the updated solution by Daniel:
Wrapping up
Yes, it is possible to validate a form with pure CSS, but there are potential problems with validation when whitespace characters are involved.
If you don’t mind the whitespaces, it works perfectly. Have fun trying this pattern out! (Sorry, I can’t help it).
I hope you found this useful. If you did, I hope you’ll share it with someone else. And feel free to reach out if you have any questions or comments.
Thanks for reading, all the best, and happy coding!
Want to become a better Frontend Developer?
Don’t worry about where to start. I’ll send you a library of articles frontend developers have found useful!
I’ll also send you one article every week to help you improve your FED skills crazy fast!
Want to become a better Frontend Developer?
Don’t worry about where to start. I’ll send you a library of articles frontend developers have found useful!
I’ll also send you one article every week to help you improve your FED skills crazy fast!
© 2013 — 2023 Zell Liew. All rights reserved · Terms