227

In Firefox 3 and Google Chrome 8.0 the following works as expected:

<style type="text/css">
    span:before { content: 'span: '; }
</style>

<span>Test</span> <!-- produces: "span: Test" -->

But it doesn't when the element is <input>:

<style type="text/css">
    input:before { content: 'input: '; }
</style>

<input type="text"></input> <!-- produces only the textbox; the generated content
                                 is nowhere to be seen in both FF3 and Chrome 8 -->

Why is it not working like I expected?

XML
  • 19,206
  • 9
  • 64
  • 65
Gui Prá
  • 5,559
  • 4
  • 34
  • 59
  • `` needs to self-close. If you want to give your text box a label, use ` – BoltClock Jan 01 '11 at 18:18
  • I realize this is an older thread, but... BoltClock's comment is semantically correct and is supported by the widely held opinion of CSS purists that CSS should be for design only (not for content). However, I am of the opinion that label elements and the like are design elements - so I feel that CSS is the right path for the desired outcome in this case. – Joshua Sep 18 '12 at 17:01
  • @Joshua: I'm not sure. I don't think there's a lot to earn from being a CSS purist; I heard some (all?) of them don't like variables and the like in CSS, too. That's absurd. I do agree, though that labels can well be considered content, so again, I'm not sure. Still, I don't think the limitation makes a lot of sense in practice... – Gui Prá Sep 18 '12 at 20:24
  • Agreed. I think it is best to apply the blanket, "purist-style" rules to those who are just learning the tool-set. But then once you have mastered them, and can understand things like the type of role that abstraction plays in CSS, then it's ok to start questioning and stretching those boundaries. If you do before then, you'll only get more confused on down the line. I suppose that's the difference between coding and programming.. "Look, it did something" vs "Look what I did". That's just my opinion. – Joshua Sep 18 '12 at 22:36

5 Answers5

342

With :before and :after you specify which content should be inserted before (or after) the content inside of that element. input elements have no content.

E.g. if you write <input type="text">Test</input> (which is wrong) the browser will correct this and put the text after the input element.

The only thing you could do is to wrap every input element in a span or div and apply the CSS on these.

See the examples in the specification:

For example, the following document fragment and style sheet:

<h2> Header </h2>               h2 { display: run-in; }
<p> Text </p>                   p:before { display: block; content: 'Some'; }

...would render in exactly the same way as the following document fragment and style sheet:

<h2> Header </h2>            h2 { display: run-in; }
<p><span>Some</span> Text </p>  span { display: block }

This is the same reason why it does not work for <br>, <img>, etc. (<textarea> seems to be special).

Andrew
  • 18,680
  • 13
  • 103
  • 118
Felix Kling
  • 795,719
  • 175
  • 1,089
  • 1,143
  • 11
    @Jason why, with [jquery](http://jquery.com) of course! edit: also, you can use thumbtacks, or spraypaint or summin. or fire. – stormdrain Jan 25 '12 at 20:21
  • 7
    use a button element instead: http://jsfiddle.net/svandragt/gRgcb/ – svandragt Mar 08 '12 at 09:44
  • 23
    This is what I really want: ``, then `input[required]::after { content: ' *'; color: red; }`. le sigh. – thirdender Aug 28 '12 at 04:41
  • 3
    @thirdender: Which is clearly a matter of design. Yeah, I know how you feel. – Gui Prá Sep 19 '12 at 14:01
  • 4
    The spec cited says: “This specification does not fully define the interaction of :before and :after with replaced elements (such as IMG in HTML)”, and INPUT can be interpreted as a replaced element. Thus, the spec does not say that `:before` does not work for such elements. Rather, that it’s unspecified. – Jukka K. Korpela Aug 26 '14 at 07:13
  • It doesn't matter what there is in "specification". What you write doesn't work in the browser. – Green Jun 25 '16 at 15:29
78

This is not due to input tags not having any content per-se, but that their content is outside the scope of CSS.

input elements are a special type called replaced elements, these do not support :pseudo selectors like :before and :after.

In CSS, a replaced element is an element whose representation is outside the scope of CSS. These are kind of external objects whose representation is independent of the CSS. Typical replaced elements are <img>, <object>, <video> or form elements like <textarea> and <input>. Some elements, like <audio> or <canvas> are replaced elements only in specific cases. Objects inserted using the CSS content properties are anonymous replaced elements.

Note that this is even referred to in the spec:

This specification does not fully define the interaction of :before and :after with replaced elements (such as IMG in HTML).

And more explicitly:

Replaced elements do not have ::before and ::after pseudo-elements

Jerome
  • 8,427
  • 2
  • 32
  • 41
SW4
  • 69,876
  • 20
  • 132
  • 137
21

Something like this works:

input + label::after {
  content: 'click my input';
  color: black;
}

input:focus + label::after {
  content: 'not valid yet';
  color: red;
}

input:valid + label::after {
  content: 'looks good';
  color: green;
}
<input id="input" type="number" required />
<label for="input"></label>

Then add some floats or positioning to order stuff.

Brook Jordan
  • 1,223
  • 10
  • 18
  • 1
    While this works, it's not a good idea if you need to keep your markup accessible. CSS content [should only be used for design](https://accessibleweb.com/question-answer/how-is-css-pseudo-content-treated-by-screen-readers/). In this case, using javascript to input helpful information into the label is a better way to go. – Nick Jun 01 '20 at 05:06
  • For sure! I was answering the question, not trying to make the code work for all situations. This should get people going in the right direction. Some ways to deal with accessibility would be to have multiple labels with the various texts and using `display: none` to hide the irrelevant ones. – Brook Jordan Jun 08 '20 at 10:07
4

fyi <form> supports :before / :after as well, might be of help if you wrap your <input> element with it... (got myself a design issue with that too)

  • 1
    `fieldset` is probably more appropriate than `form` if you want a simple wrapper for an input element. – andytuba Jan 21 '13 at 21:11
  • 3
    And `span` or `div` is probably more appropriate than `fieldset`, since for the latter you need to override all the user agent css (at the very least margin, padding, border) – Teodor Sandu Apr 03 '13 at 06:52
0

Use tags label and our method for =, is bound to input. If follow the rules of the form, and avoid confusion with tags, use the following:

<style type="text/css">
    label.lab:before { content: 'input: '; }
</style>

or compare (short code):

<style type="text/css">
    div label { content: 'input: '; color: red; }
</style>

form....

<label class="lab" for="single"></label><input name="n" id="single" ...><label for="single"> - simle</label>

or compare (short code):

<div><label></label><input name="n" ...></div>
Roman Bondar
  • 13
  • 1
  • 4