132

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.

BoltClock
  • 700,868
  • 160
  • 1,392
  • 1,356
animuson
  • 53,861
  • 28
  • 137
  • 147
  • Can you post the relevant code? – Virendra Dec 26 '11 at 21:41
  • 2
    Can I quote you part of the [API reference for the `:empty` selector](http://api.jquery.com/empty-selector/): "Some other elements, on the other hand, are empty (i.e. have no children) by definition: ``, ``, `
    `, and `
    `, for example."
    – David Thomas Dec 26 '11 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 Dec 26 '11 at 21:43

13 Answers13

190

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.

BoltClock
  • 700,868
  • 160
  • 1,392
  • 1,356
  • 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 Dec 26 '11 at 21:47
  • 18
    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 http://stackoverflow.com/questions/16952526/detect-if-an-input-has-text-in-it-with-css – Jukka K. Korpela Jun 06 '13 at 08:08
  • 2
    @JukkaK.Korpela unless you are also using pattern attribute. – WORMSS Sep 01 '16 at 05:19
  • 3
    input:not([value='']) will select an input with a value ;) – Chris Love Aug 08 '19 at 19:13
  • 3
    @Chris Love: Inputs with an *initial* (on page load) value. – BoltClock Sep 01 '19 at 18:54
68

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

Community
  • 1
  • 1
Gijs Erenstein
  • 1,080
  • 10
  • 13
67

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);" />
Mo.
  • 26,306
  • 36
  • 159
  • 225
  • 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. – Derk Jan Speelman Mar 21 '19 at 10:37
  • 1
    This is the only solution worked for me with input type email. – omegastripes Feb 01 '22 at 00: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 May 24 '22 at 18:56
19

.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>
Amit
  • 1,841
  • 1
  • 19
  • 36
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.

Eliran Malka
  • 15,821
  • 6
  • 77
  • 100
7

Since placeholder disappear on input, you can use:

input:placeholder-shown{
    //rules for not empty input
}
Luca C.
  • 11,714
  • 1
  • 86
  • 77
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

2

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>
vacsati
  • 528
  • 6
  • 7
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;
}
Eliran Malka
  • 15,821
  • 6
  • 77
  • 100
star
  • 19
  • 1
0

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
input:not([value=""])

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

Eliran Malka
  • 15,821
  • 6
  • 77
  • 100
Anton
  • 19
  • 1
-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.

Eliran Malka
  • 15,821
  • 6
  • 77
  • 100
  • 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 Apr 18 '13 at 23:18
-3

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

Simon Botero
  • 583
  • 5
  • 5