22

HTML

<progress max="100" value="80" data-value="5"></progress>

CSS

progress { margin: 50px; width:250px; border:0; }

CSS (Attempt 1)

progress:before, progress:after { content: attr(data-value); }

CSS (Attempt 2)

progress::-webkit-progress-bar:before,
progress::-webkit-progress-bar:after { content: attr(data-value); }

progress::-moz-progress-bar:before,
progress::-moz-progress-bar:after { content: attr(data-value); }

CSS (Attempt 3)

progress::-webkit-progress-value:before,
progress::-webkit-progress-value:after { content: attr(data-value); }

progress::-moz-progress-value:before,
progress::-moz-progress-value:after { content: attr(data-value); }

None of the above attempts succeeded. Also tried each of the above versions with different CSS code blocks, for :before and :after.

OBJECTIVE

To inject CSS generated content before and after the HTML5 <progress> element. Is this possible?

JsFiddle Demo

http://jsfiddle.net/pankajparashar/MNL2C/

UPDATE

When I use the following CSS it works.

progress::-webkit-progress-bar:before,
progress::-webkit-progress-bar:after { content: '123'; }    

CONCLUSION

Apparently when we inject static content in the CSS, it works. But if we use the content from data-* it doesn't.

BoltClock
  • 700,868
  • 160
  • 1,392
  • 1,356
Pankaj Parashar
  • 9,762
  • 6
  • 35
  • 48
  • 4
    I don't think this is possible as content within the `progress` element is never displayed if the browser can already draw the progress bar, similarly to content within an `object` or `iframe`. – BoltClock Aug 10 '13 at 14:17
  • Yes this is a valid argument. But here we are not talking about the content inside the progress element, it is the content before and after the progress element. Not sure if this made sense! – Pankaj Parashar Aug 10 '13 at 14:26
  • It's just the same. ::after actually appends a pseudo-element inside the specified element – amakhrov Aug 10 '13 at 14:34
  • 3
    @Pankaj Parashar: Yes, we are. We're talking about content generated before and after the content within the `progress` element. – BoltClock Aug 10 '13 at 14:38
  • 3
    I beg to differ with your opinion. Because when we change this `content: attr(data-value);` to this `content: '213';` it works! So the problem is with using css attr in the `content` attribute. – Pankaj Parashar Aug 10 '13 at 15:31
  • Here's an interesting [writeup on progress bars](http://www.useragentman.com/blog/2012/01/03/cross-browser-html5-progress-bars-in-depth/) – dc5 Aug 10 '13 at 16:54
  • Yes I have been through this article before. But didn't help me in terms of what I am trying to do. – Pankaj Parashar Aug 10 '13 at 18:14
  • I'm gonna guess that your problem is the element 'progress'. I use this same sintax for my icons, and it works just fine. But messing with you JSFiddle, I absolutly couldn't make it work. – madagalbiati Sep 12 '13 at 16:19
  • @madaaah Yes the problem is with the 'progress' element. – Pankaj Parashar Sep 13 '13 at 05:58
  • 1
    Isn't this a similar issue as that of the `IMG` tag (regarding `:before` and `:after`)? – frozenkoi Sep 18 '13 at 01:47
  • See http://stackoverflow.com/questions/13870635/before-and-after-elements-on-an-img-element for the case of `IMG` and http://stackoverflow.com/questions/13055797/using-the-before-selector-on-an-iframe for `IFRAME` – frozenkoi Sep 18 '13 at 02:09
  • No it is not related to the problem for `IMG` and `IFRAME`. In that case, you cannot use `::before` and `::after` on them, whereas in this case, `::before` and `::after` works but `content: attr()` doesn't work.. – Pankaj Parashar Sep 18 '13 at 05:33
  • Do you have a visual of what you're trying to accomplish? The fiddle of your first example renders a progress bar with a 5 above and below it in Chrome Beta. – Matijs Sep 22 '13 at 11:14
  • What's the use case for this? This is what appears for [chrome](http://i.imgur.com/ZcGUmnC.png) and this is what shows up for [firefox](http://i.imgur.com/3ce3UJe.png) and this for [safari](http://i.imgur.com/s2GpLk5.png) without modifying your code. You have three different behaviors on each browser. At this point I would just build a progress bar the old fashioned way since IE has [spotty support](http://caniuse.com/#feat=progressmeter) for the progress element. – Kerry Liu Sep 22 '13 at 20:44
  • 3
    Reading further, the progress element does not follow the [void](http://dev.w3.org/html5/markup/syntax.html#content-model) content model. It uses the [phrasing content model](http://dev.w3.org/html5/spec-preview/the-progress-element.html#the-progress-element) so you should theoretically be able to do this with text. It still seems like an odd use case for me, but you should probably file a bug with mozilla and chromium. – Kerry Liu Sep 22 '13 at 21:12
  • @Matijs by Chrome beta do you mean Chrome Canary? @Kerry Liu Have you interchanged the screenshot for chrome with firefox, because I could see `-moz-progress-bar` in the Chrome screenshot. – Pankaj Parashar Sep 23 '13 at 08:19
  • @KerryLiu Maybe you did not read my question correctly. `::before` and `::after` pseudo elements work when you embed content directly, but they don't seem to work when you use data-* attibute. In your example, you embed '123' directly in the content, which obviously works! – Pankaj Parashar Sep 23 '13 at 08:27
  • Sorry I didn't see your last reply to me. My original comment still stands. I'll write an answer explaining why. – BoltClock Sep 23 '13 at 10:08
  • @Kerry Liu: While that is true, there's another factor (also somewhat theoretical) that prevents it or at least makes it infeasible. See my answer. – BoltClock Sep 23 '13 at 10:57
  • I'm on Chrome 29.0.1547.76 I still see the following, which is still a browser bug http://i.imgur.com/ZcGUmnC.png – Kerry Liu Sep 23 '13 at 14:52
  • @KerryLiu Are you sure that you are looking at the right screenshot becuase it says '-moz-progress-bar' ? – Pankaj Parashar Sep 23 '13 at 15:41
  • Sorry, I must have taken a snapshot when I was about to copy the text over to firefox. Here is a better pic of chrome rendering incorrectly: http://i.imgur.com/eDVSWFZ.png – Kerry Liu Sep 23 '13 at 17:24
  • @PankajParashar not Canary, [Beta](https://www.google.com/intl/en/chrome/browser/beta.html) – Matijs Sep 23 '13 at 18:26

3 Answers3

10

In my original comment, I said:

I don't think this is possible as content within the progress element is never displayed if the browser can already draw the progress bar, similarly to content within an object or iframe.

To put it another way, this classifies progress as a replaced element. Just as with the traditional input and other form elements that are replaced elements, as well as img, CSS2.1 doesn't have much to say about using generated content with it:

Note. This specification does not fully define the interaction of :before and :after with replaced elements (such as IMG in HTML). This will be defined in more detail in a future specification.

It's well-established that Gecko-based browsers choose not to support generated content for replaced elements, whereas WebKit-based browsers allow it to some extent, at least for form elements that are replaced elements. (Interestingly, progress::before and progress::after do not work in any browser.) So if you're asking if it's possible to do this cross-browser, the answer is no, and has always been no.


As for why WebKit browsers can insert strings and not attr() values, I'm not certain. Both CSS2.1 and CSS3 Units and Values state that attr() should take values from attributes of the actual element generating those pseudo-elements, since pseudo-elements can't have attributes themselves anyway. This is where I'm stumped.

Perhaps the browser is incorrectly trying to take the data-value attribute from ::-webkit-progress-bar and ::-webkit-progress-value and not the progress element, which is why content is failing when using attr() but working when using a string.

Perhaps the root of the problem lies in the very fact that you're trying to add generated content to other pseudo-elements, which again seems to work in WebKit browsers for whatever bizarre reason. Unlike the above issue with generated content within replaced elements, the current Selectors 3 spec and the upcoming Selectors 4 spec are both very clear on this: you are not supposed to have more than one pseudo-element per complex selector. Of course, WebKit has infamously flouted various rules when it comes to implementing pseudo-elements, so in hindsight it does not surprise me to see WebKit mess up doing so.

Either way, the real conclusion is that the implementation of CSS generated content is extremely poor beyond the scope of the current standard of CSS2.1 + Selectors, by which I mean generated content for replaced elements such as input and progress, and nesting of pseudo-elements in a single selector.

BoltClock
  • 700,868
  • 160
  • 1,392
  • 1,356
  • 1
    I think I'm beginning to see the outlines of your reasons for disliking WebKit. I suspect there is, though, quite a long blog post in any full explanation. – David Thomas Sep 23 '13 at 10:44
  • 1
    @David Thomas: I've been meaning to blog again. I really ought to do something about that landing page soon. But you're right: I don't usually write about *why* exactly I dislike WebKit, and I'm glad to have done just that for once. – BoltClock Sep 23 '13 at 10:50
  • Or, you know, that Twitter thing... Incidentally, catharsis is often a good thing, and worthwhile, even if only, in some cases, for letting others see reasoning. Having said that, while I like Chrome, I'm also beginning to become weary of WebKit's 'interpretations' of standards. Just not quite enough to switch back to Firefox, or move to Opera (not least of which because of their decision to adopt WebKit themselves). – David Thomas Sep 23 '13 at 10:58
  • The original fiddle does work on safari 6.0.5 (8536.30.1) http://i.imgur.com/s2GpLk5.png, while adding content completely breaks the [chrome](http://i.imgur.com/ZcGUmnC.png) rendering of the progress element. Thinking on it some more, you're right. Allowing this behavior should be the bug. Even though the progress element is allowed to have text children, it's children shouldn't be visible unless the element isn't supported, similar to the canvas or iframe element. – Kerry Liu Sep 23 '13 at 15:19
  • @KerryLiu You are right, Safari does show the `::before` and `::after` pseudo elements with CSS generated content but the point made by @BoltClock is valid enough. – Pankaj Parashar Sep 23 '13 at 15:37
  • The reason i accepted this as an answer, becuase @BoltClock really made a great point by saying that `data-value` is on the `progress` element and not on the pseudo element `::-webkit-progress-value` which is perhaps the reason for not showing the content. Initially i thought this is a bug but after your response realised that I was doing it the wrong way! – Pankaj Parashar Sep 23 '13 at 15:39
2
<progress></progress>

It doesn't accept text all you need to do is to tweak your css.

HTML:

<progress max="100" value="80" data-value="80"></progress>
<span class="percentage">80% Done</span>

CSS:

progress { margin: 0px; width:250px; border:0; }

/* CSS (Attempt 1) */

    progress:before, progress:after { content: attr(data-value); }

/* CSS (Attempt 2) */

    progress::-webkit-progress-bar:before,
    progress::-webkit-progress-bar:after { content: attr(data-value); }

    progress::-moz-progress-bar:before,
    progress::-moz-progress-bar:after { content: attr(data-value); }

/* CSS (Attempt 3) */

    progress::-webkit-progress-value:before,
    progress::-webkit-progress-value:after { content: attr(data-value); }
    progress::-moz-progress-value:before,
    progress::-moz-progress-value:after { content: attr(data-value); }

    .percentage{
        float: left;
        margin-left:100px;
        margin-top: -20px;
        position: absolute;
        display: block;
        color: #FFF;
    }
BoltClock
  • 700,868
  • 160
  • 1,392
  • 1,356
  • 1
    Thanks for the answer but you really missed my point! The question was about using css generated content with pseudo elements but your answer ends up mocking my attempt :) – Pankaj Parashar Sep 23 '13 at 15:40
0

It appears that @BoltClock is correct - the content: attr(value) is looking for the value attribute on the shadow DOM element of -webkit-progress-value, not on the actual <progress> element:

h4 { margin: 2em 0 0; }
progress {
  -webkit-appearance: none;
  appearance: none;
  position: relative;
}
progress::-webkit-progress-value:before {
  position: absolute;
  right: 0;
  bottom: -125%;
}
progress.show-value::-webkit-progress-value:before {
  content: attr(value);
}
progress.show-data-value::-webkit-progress-value:before {
  content: attr(data-value);
}
progress.show-style::-webkit-progress-value:before {
  content: attr(style);
}
progress.show-pseudo::-webkit-progress-value:before {
  content: attr(pseudo);
}
<h4><code>attr(value)</code>:</h4>
<progress class="show-value" max="100" value="80" data-value="5"></progress>

<h4><code>attr(data-value)</code>:</h4>
<progress class="show-data-value" max="100" value="80" data-value="5"></progress>

<h4><code>attr(style)</code>:</h4>
<progress class="show-style" max="100" value="80" data-value="5"></progress>

<h4><code>attr(pseudo)</code></h4>
<progress class="show-pseudo" max="100" value="80" data-value="5"></progress>
Sandwich
  • 475
  • 1
  • 8
  • 16