140

I'm having a heck of a time with this particular CSS selector which does not want to work when I add :not(:empty) to it. It seems to work fine with any combination of the other selectors:

input:not(:empty):not(:focus):invalid { border-color: #A22; box-shadow: none }

If I remove the :not(:empty) part, it works just fine. Even if I change the selector to input:not(:empty) it still won't select input fields which have text typed into them. Is this broken or am I just not allowed to use :empty within a :not() selector?

The only other thing I can think of is that browsers are still saying that the element is empty because it has no children, just a "value" per say. Does the :empty selector not have separate functionality for an input element versus a regular element? This doesn't seem probable though because using :empty on a field and typing something into it will cause the alternate effects to go away (because it is no longer empty).

Tested in Firefox 8 and Chrome.

3
  • Can you post the relevant code?
    – Virendra
    Commented Dec 26, 2011 at 21:41
  • 2
    Can I quote you part of the API reference for the :empty selector: "Some other elements, on the other hand, are empty (i.e. have no children) by definition: <input>, <img>, <br>, and <hr>, for example." Commented Dec 26, 2011 at 21:43
  • @Virendra: That is the relevant code, but I've added the actual CSS rules to it. If I remove the :not(:empty), the red border works as expected for an input that is not in focus but is invalid.
    – animuson
    Commented Dec 26, 2011 at 21:43

13 Answers 13

197

Being a void element, an <input> element is considered empty by the HTML definition of "empty", since the content model of all void elements is always empty. So they will always match the :empty pseudo-class, whether or not they have a value. This is also why their value is represented by an attribute in the start tag, rather than text content within start and end tags.

Also, from the Selectors spec:

The :empty pseudo-class represents an element that has no children at all. In terms of the document tree, only element nodes and content nodes (such as DOM text nodes, CDATA nodes, and entity references) whose data has a non-zero length must be considered as affecting emptiness;

Consequently, input:not(:empty) will never match anything in a proper HTML document. (It would still work in a hypothetical XML document that defines an <input> element that can accept text or child elements.)

I don't think you can style empty <input> fields dynamically using just CSS (i.e. rules that apply whenever a field is empty, and don't once text is entered). You can select initially empty fields if they have an empty value attribute (input[value=""]) or lack the attribute altogether (input:not([value])), but that's about it.

5
  • Hmm, I don't remember what I did to get the effects to go away with just the input:empty. Perhaps I typed something wrong, who knows.
    – animuson
    Commented Dec 26, 2011 at 21:47
  • 21
    Re last paragraph in the answer, input elements can be styled dynamically (in sufficiently modern browsers) if you can use the required attribute in HTML markup. Then you can use :valid and :invalid in CSS to test for nonempty vs. empty value of the control. See stackoverflow.com/questions/16952526/… Commented Jun 6, 2013 at 8:08
  • 2
    @JukkaK.Korpela unless you are also using pattern attribute.
    – WORMSS
    Commented Sep 1, 2016 at 5:19
  • 3
    input:not([value='']) will select an input with a value ;)
    – Chris Love
    Commented Aug 8, 2019 at 19:13
  • 3
    @Chris Love: Inputs with an initial (on page load) value.
    – BoltClock
    Commented Sep 1, 2019 at 18:54
74

You could try using :placeholder-shown...

input {
  padding: 10px 15px;
  font-size: 16px;
  border-radius: 5px;
  border: 2px solid lightblue;
  outline: 0;
  font-weight:bold;
  transition: border-color 200ms;
  font-family: sans-serif;
}

.validation {
  opacity: 0;
  font-size: 12px;
  font-family: sans-serif;
  color: crimson;
  transition: opacity;
}

input:required:valid {
  border-color: forestgreen;
}

input:required:invalid:not(:placeholder-shown) {
  border-color: crimson;
}

input:required:invalid:not(:placeholder-shown) + .validation {
  opacity: 1;
}

  
<input type="email" placeholder="e-mail" required>
<div class="validation">Not valid</span>

no great support though... caniuse

5
  • 5
    pretty good support though...caniuse Commented Apr 10, 2017 at 3:39
  • Good old Internet Explorer (Y) Commented May 30, 2017 at 12:54
  • @rorymorris89 even latest version of EDGE doesn't support :-(
    – Mo.
    Commented Jun 11, 2017 at 21:24
  • @Mo it does today, I just found and used this solution :)
    – Marnes
    Commented Nov 5, 2021 at 12:09
  • Works perfect and if don't want to use a placeholder in that particular form, placeholder=" " works just fine. Commented Oct 4, 2023 at 12:43
69

It is possible with inline javascript onkeyup="this.setAttribute('value', this.value);" & input:not([value=""]):not(:focus):invalid

Demo: http://jsfiddle.net/mhsyfvv9/

input:not([value=""]):not(:focus):invalid {
  background-color: tomato;
}
<input type="email" value="" placeholder="valid mail" onchange="this.setAttribute('value', this.value);" />

3
  • Isn't the onchange event better in this case? Since you can edit input values with Right click > Cut as well (for example). Tested it: works fine. Commented Mar 21, 2019 at 10:37
  • 1
    This is the only solution worked for me with input type email. Commented Feb 1, 2022 at 0:36
  • If you're using React you don't even need the JS part as you're already most probably doing value={myInputValue} (which sets the value attribute itself not just the property)
    – iuliu.net
    Commented May 24, 2022 at 18:56
20

.floating-label-input {
  position: relative;
  height:60px;
}
.floating-label-input input {
  width: 100%;
  height: 100%;
  position: relative;
  background: transparent;
  border: 0 none;
  outline: none;
  vertical-align: middle;
  font-size: 20px;
  font-weight: bold;
  padding-top: 10px;
}
.floating-label-input label {
  position: absolute;
  top: calc(50% - 5px);
  font-size: 22px;
  left: 0;
  color: #000;
  transition: all 0.3s;
}
.floating-label-input input:focus ~ label, .floating-label-input input:focus ~ label, .floating-label-input input:valid ~ label {
  top: 0;
  font-size: 15px;
  color: #33bb55;
}
.floating-label-input .line {
  position: absolute;
  height: 1px;
  width: 100%;
  bottom: 0;
  background: #000;
  left: 0;
}
.floating-label-input .line:after {
  content: "";
  display: block;
  width: 0;
  background: #33bb55;
  height: 1px;
  transition: all 0.5s;
}
.floating-label-input input:focus ~ .line:after, .floating-label-input input:focus ~ .line:after, .floating-label-input input:valid ~ .line:after {
  width: 100%;
}
<div class="floating-label-input">
      <input type="text" id="id" required/>
      <label for="id" >User ID</label>
      <span class="line"></span>
</div>

1
  • Love this as a nice, CSS-only interactive effect. Though would want to point out to folks that this is dependent on 1) having your fields be marked required, even if that's not what the form calls for, and 2) having no other requirements like minlength or pattern, as tabbing away with a partially-filled field will cause the <label> to cover up your text. But for quick UI demo, it's pretty awesome.
    – Phil Tune
    Commented Oct 27, 2023 at 19:49
8

Since placeholder disappear on input, you can use:

input:placeholder-shown{
    //rules for not empty input
}
0
8

You may approach this differently; omit the use of the :empty pseudo-class, and utilize input events to detect a significant value in the <input> field, then style it accordingly:

var inputs = document.getElementsByTagName('input');

for (var i = 0; i < inputs.length; i++) {
  var input = inputs[i];
  input.addEventListener('input', function() {
    var bg = this.value ? 'green' : 'red';
    this.style.backgroundColor = bg;
  });
}
body {
  padding: 40px;
}
#inputList li {
  list-style-type: none;
  padding-bottom: 1.5em;
}
#inputList li input,
#inputList li label {
  float: left;
  width: 10em;
}
#inputList li input {
  color: white;
  background-color: red;
}
#inputList li label {
  text-align: right;
  padding-right: 1em;
}
<ul id="inputList">
  <li>
    <label for="username">Enter User Name:</label>
    <input type="text" id="username" />
  </li>
  <li>
    <label for="password">Enter Password:</label>
    <input type="password" id="password" />
  </li>
</ul>

Related


Disclaimer: note that input events are currently experimental, and probably not widely supported. nope! forget about it - it's a living standard now.

6

If you want to check whether the input field is empty or not you can try this:

HTML

<input type="text" placeholder='Placeholder'/>

CSS

input:not(:placeholder-shown){
   background-color: red;
}

Since Placeholder get vanishes when something is typed in the input field so we can check whether Placeholder is visible or not through CSS

3

Another pure CSS solution

.form{
  position:relative;
  display:inline-block;
}
.form input{
  margin-top:10px;
}
.form label{
    position:absolute;
    left:0;
    top:0;
    opacity:0;
    transition:all 1s ease;
}
input:not(:placeholder-shown) + label{
    top:-10px;
    opacity:1;
}
<div class="form">
    <input type="text" id="inputFName" placeholder="Firstname">
    <label class="label" for="inputFName">Firstname</label>
</div>
<div class="form">
    <input type="text" id="inputLName" placeholder="Lastname">
    <label class="label" for="inputLName">Lastname</label>
</div>

2

You can do trick by adding placeholder to input with empty value then add input:not(:placeholder-shown)

<input type="text" placeholder=" ">
input:not(:placeholder-shown){
 color: red;
}
1
  • 1
    +1 for pointing out that placeholder doesn't have to be filled. So far, this seems to be the best solution I can find, as an empty placeholder is completely semantically valid. Using required attribute is only valid if your form calls for requiring all fields. Hopefully, the working groups will fix the shortcomings of :empty or create a separate, purely-CSS option for this very common use-case.
    – Phil Tune
    Commented Oct 27, 2023 at 19:56
1

pure css solution

input::-webkit-input-placeholder {
    opacity: 1;
    -webkit-transition: opacity 0s;
    transition: opacity 0s;
    text-align: right;
}
/* Chrome <=56, Safari < 10 */
input:-moz-placeholder {
    opacity: 1;
    -moz-transition: opacity 0s;
    transition: opacity 0s;
    text-align: right;
}
/* FF 4-18 */
input::-moz-placeholder {
    opacity: 1;
    -moz-transition: opacity 0s;
    transition: opacity 0s;
    text-align: right;
}
/* FF 19-51 */
input:-ms-input-placeholder {
    opacity: 1;
    -ms-transition: opacity 0s;
    transition: opacity 0s;
    text-align: right;
}
/* IE 10+ */
input::placeholder {
    opacity: 1;
    transition: opacity 0s;
    text-align: right;
}
/* Modern Browsers */

*:focus::-webkit-input-placeholder {
   opacity: 0;
   text-align: left;
}
/* Chrome <=56, Safari < 10 */
*:focus:-moz-placeholder {
    opacity: 0;
    text-align: left;
}
/* FF 4-18 */
*:focus::-moz-placeholder {
    opacity: 0;
    text-align: left;
}
/* FF 19-50 */
*:focus:-ms-input-placeholder {
    opacity: 0;
    text-align: left;
}
/* IE 10+ */
*:focus::placeholder {
    opacity: 0;
    text-align: left;
}
/* Modern Browsers */

input:focus {
    text-align: left;
}
-1
input:not([value=""])

This works because we are selecting the input only when there isn't an empty string.

1
  • works for me, thnx Commented Jan 3, 2023 at 10:12
-2

This should work in modern browsers:

input[value]:not([value=""])

It selects all inputs with value attribute and then select inputs with non empty value among them.

1
  • 11
    This would not be dynamic, though. It would only select input elements which have the attribute defined as value="". Typing/removing something in the box would not cause any changes.
    – animuson
    Commented Apr 18, 2013 at 23:18
-3

You can use &:valid on your input and that make the trick.

Not the answer you're looking for? Browse other questions tagged or ask your own question.