# CSS :placeholder-shown
Use this pseudo-class to style an input that is currently displaying the placeholder text -- in other words, the user has not typed anything in the textbox 📭
It's great to apply some dynamic styling depending if your input is empty or not 👏
input:placeholder-shown {
border-color: pink;
}
# How does it work?
The :placeholder-show
is a CSS pseudo-class that allows you to apply styling to <input>
or <textarea>
that has a placeholder text.
<input placeholder="placeholder text" />
<textarea placeholder="placeholder text"></textarea>
- Pink if placeholder is shown, user has not typed anything
- Black if NO placeholder is shown, user has typed something
# :placeholder-shown
must have placeholder
This selector will not work if the element does NOT have a placeholder text.
<input /><!-- no placeholder -->
<!-- This is also considered no placeholder text -->
<input placeholder="" />
input:placeholder-shown {
border-color: pink;
}
No placeholder text displayed
# :placeholder-shown
vs ::placeholder
So we can use :placeholder-shown
to style the input element.
input:placeholder-shown {
border: 1px solid pink;
background: yellow;
color: green;
}
☝️Hmmm...notice something odd 🤔 -- We set color: green
but it didn't work. Well, that's because :placeholder-shown
will only target the input itself. But for the actual placeholder text, you have to use the pseudo-element ::placeholder
.
input::placeholder {
color: green;
}
⚠️ BUT! When I was playing around with this, I noticed there are some other properties that if applied at the :placeholder-shown
level will affect the placeholder text.
input:placeholder-shown {
font-style: italic;
text-transform: uppercase;
letter-spacing: 5px;
}
Now, I don't really know why this is the case 🤷♀️ Maybe because those properties get inherited by the placeholder. Anyways, if you know the real reason, please do submit a pull request in the "Community Input" section 😄
# :placeholder-shown
vs :empty
Although :placeholder-shown
is specifically made to target if an element is displaying the placeholder or not. We can essentially use it to check if the input is empty or not (of course, assuming, all your input has a placeholder). So maybe your next question, can't we use CSS empty? Well, let's check 👩🔬
<input value="not empty">
<input><!-- empty -->
input:empty {
border: 1px solid pink;
}
input {
border: 1px solid black;
}
❌Expect this to be black
- Pink if empty
- Black if NOT empty
Hmmm...from here you might assume that :empty
seem to be working because we are seeing the Pink border. But it actually isn't working 😔
The reason the pink is displaying because pseudo-class increases the specificity. Similar to how class selectors (ie. .form-input
) has a higher specificity than a type selector (ie. input
). Higher specificity selectors will always override the styles of those set at a lower specificity.
Here's the verdict! Don't use :empty
to check if an input element is empty or not 🙅♀️
# How to check input emptiness without a placeholder?
Okay, so our only way of checking if an input is empty or not is using :placeholder-shown
. But what happens if our input element doesn't have a placeholder. Well here's a clever way! Pass in an empty string, " "
.
<input placeholder=" "><!-- 👈 pass empty string -->
input:placeholder-shown {
border-color: pink;
}
No placeholder text displayed
Thanks @jeltehomminga for sharing this tip!
# Combined with other selectors
So it's cool we can target the input elements that are displaying the placeholder text. In other words, if the placeholder text is shown, then it must mean that element is empty. Using this knowledge, we can combine this pseudo-class with other selectors and do some really neat things! Let's have a look 🤩
# Inverse :placeholder-shown
with :not
We can do the inverse of something using the :not
pseudo-class. Here, we can target when the input is NOT empty.
<input placeholder="placeholder" value="not empty" />
input:not(:placeholder) {
border-color: green;
}
- Green if NOT empty, user has typed something
- Black if empty
# Floating Label
One of the problems of using a placeholder and not using a label is accessibility. Because once you're typing the placeholder text is gone. This can lead to confusion for the user. A really good solution is the floating label. Initially, the placeholder text is displayed without the label. And once the user starts typing, the label will appear. This way you can still maintain the cleanliness of your form without compromising the user experience or accessibility. Double win 🥳
And this is possible with pure CSS. We just have to combine placeholder-shown
with :not
and +
. Here's a super simplified version of the floating label.
<input name="name" placeholder="Type name..." />
<label for="name">NAME</label>
label {
display: none;
position: absolute;
top: 0;
}
input:not(:placeholder-shown) + label {
display: block;
}
And if you start typing, this is the result you will get:
# Browser Support
Support for :placeholder-shown
is excellent! This includes Internet Explorer (yes, I'm as surprised as you are 😆). But (of course there's a "but"), for IE, you will need to use the non-standard name :-ms-input-placeholder
.
Browser | placeholder-shown |
---|---|
Chrome | ✅ |
Firefox | ✅ |
Safari | ✅ |
Edge | ✅ |
Internet Explorer | ✅ (:-ms-input-placeholder ) |
# Community Input
@cilly_boloe: This works really well with adjacent labels you can move or highlight with "input:placeholder-shown + label" instead of having to add a custom class with JS on focus events like I had been doing 🙃
@xirclebox: You can take this a step further by adding an explicit or implicit label to the input. That way the users will always know what the field is for. This is great for UX and accessibility.
@robertgroves: I put up a quick CodePen to show how this could be used to include a visual for indicating that input from the user is still needed.
@jibijohndavid: CSS Only Floating Label: CodePen
@codedgar_dev: I use that along :valid and :invalid to validate natively inputs in my framework Puppertino
@nachocoloma: You can make it required and use :invalid, but that would be triggered by any failing validation, not just a missing value (and maybe the case is not an empty required field). Better use an empty placeholder instead.