34

I want to create buttons like these:
pseudo 3d button

In modern browsers the effect is created using inset box-shadow and filters.
For IE8 - pseudo-elements are chosen.
For IE7 - I use special tags wrapped in conditional comments.

Demo: (http://jsfiddle.net/8M5Tt/68/)

/**
 * Button w/o images
 */
html {
    font-size: 62.5%;
    }
body {
    font: normal 1em/1em Arial, Tahoma, Verdana, sans-serif;
    }
 
/* layout */
.btn {
    display: inline-block;
    height: 28px;
    border-width: 1px;
    border-style: solid;
    width: 170px;
    box-sizing: content-box;
    overflow: hidden;
    position: relative;
    z-index: 1;
    }
.btn {
    margin: 15px;
    }
.btn.btn_small {
    width: 130px;
    }

/* ie7 */
.lt-ie8 .btn .before,
.lt-ie8 .btn .after {
    position: absolute;
    right: -1px;
    left: -1px;
    display: block;
    height: 3px;
    }
.lt-ie8 .btn .before {
    top: -1px;
    filter: progid:DXImageTransform.Microsoft.gradient( startColorstr='#80ffffff', endColorstr='#00ffffff',GradientType=0 );
    }
.lt-ie8 .btn .after {
    bottom: -1px;
    filter: progid:DXImageTransform.Microsoft.gradient( startColorstr='#00ffffff', endColorstr='#80000000',GradientType=0 );
    }
/* /ie7 */

/* ie8 */
.ie8 .btn:before,
.ie8 .btn:after {
    content: ' ';
    z-index: 1;    
    position: absolute;
    right: -1px;
    left: -1px;
    display: block;
    height: 3px;
    }
.ie8 .btn:before {
    top: -1px;
    filter: progid:DXImageTransform.Microsoft.gradient( startColorstr='#80ffffff', endColorstr='#00ffffff',GradientType=0 );
    }
.ie8 .btn:after {
    bottom: -1px;
    filter: progid:DXImageTransform.Microsoft.gradient( startColorstr='#00ffffff', endColorstr='#80000000',GradientType=0 );
    }
/* /ie8 */

/* typo */
.btn {
    /* 28 / 14 = 2.57142857 */
    font: bold 14px/2 Arial, Helvetica, Tahoma, sans-serif;
    text-transform: uppercase;
    }
.btn:active {
    line-height: 2.4em;
    }

/* color */
.btn {
    background-color: #00cccc;
    color: #fff;
    border-color: #00a8a8;
    border-radius: 3px;
    cursor: pointer;
    box-shadow:
         1px  1px 4px rgba(255, 255, 255, 0.5) inset,            
        -1px -1px 4px rgba(000, 000, 000, 0.5) inset;
    }
.btn:hover {
    background-color: #00ebeb;
    }
.btn:active {
    box-shadow:
        -1px -1px 4px rgba(255, 255, 255, 0.5) inset,            
         1px  1px 4px rgba(000, 000, 000, 0.5) inset;
    }

/* green */
.btn_green {
    background-color: #009900;
    border-color: #009600;
    }
.btn_green:hover {
    background-color: #00c200;
    }

/* red */
.btn_red {
    background-color: #e00000;
    border-color: #c13d00;
    }
.btn_red:hover {
    background-color: #f00000;
    }
<!--
paulirish.com/2008/conditional-stylesheets-vs-css-hacks-answer-neither/
-->
<!--[if lt IE 7]> 
    <div class="no-js lt-ie9 lt-ie8 lt-ie7" lang="en">
<![endif]-->
<!--[if IE 7]>
    <div class="no-js lt-ie9 lt-ie8 ie7" lang="en">
<![endif]-->
<!--[if IE 8]>
    <div class="no-js lt-ie9 ie8" lang="en">
<![endif]-->
<!--[if gt IE 8]><!-->
    <div class="no-js no-ie" lang="en">
<!--<![endif]-->

<button class="btn btn_green btn_small ">
    Send
    <!--[if IE 7]> <span class="before"> </span><span class="after"> </span> <![endif]-->
</button>

<button class="btn">
    Buy
    <!--[if IE 7]> <span class="before"> </span><span class="after"> </span> <![endif]-->
</button>

<button class="btn btn_green">
    Activate
    <!--[if IE 7]> <span class="before"> </span><span class="after"> </span> <![endif]-->
</button>

<button class="btn btn_red">
    Delete
    <!--[if IE 7]> <span class="before"> </span><span class="after"> </span> <![endif]-->
</button>

</div>

Main Question: Why don't filters work on pseudo elements in IE8?


Update:

I guess that filters do not work on css-generated content, despite the fact that it is not mentioned on this MSDN page.

I solved my problem in IE8 by applying filters to conditional elements like I do for IE7.

Final demo: (http://jsfiddle.net/matmuchrapna/8M5Tt/73/)

/**
 * Button w/o images
 */
html {
    font-size: 62.5%;
    }
body {
    font: normal 1em/1em Arial, Tahoma, Verdana, sans-serif;
    }
 
/* layout */
.btn {
    display: inline-block;
    height: 28px;
    border-width: 1px;
    border-style: solid;
    width: 170px;
    box-sizing: content-box;
    overflow: hidden;
    position: relative;
    z-index: 1;
    }
.btn {
    margin: 15px;
    }
.btn.btn_small {
    width: 130px;
    }

/* ie78 */
.lt-ie9 .btn .before,
.lt-ie9 .btn .after {
    position: absolute;
    right: -1px;
    left: -1px;
    display: block;
    height: 3px;
    }
.lt-ie9 .btn .before {
    top: -1px;
    filter: progid:DXImageTransform.Microsoft.gradient( startColorstr='#80ffffff', endColorstr='#00ffffff',GradientType=0 );
    }
.lt-ie9 .btn .after {
    bottom: -1px;
    filter: progid:DXImageTransform.Microsoft.gradient( startColorstr='#00ffffff', endColorstr='#80000000',GradientType=0 );
    }
/* /ie78 */

/* typo */
.btn {
    /* 28 / 14 = 2.57142857 */
    font: bold 14px/2 Arial, Helvetica, Tahoma, sans-serif;
    text-transform: uppercase;
    }
.btn:active {
    line-height: 2.4em;
    }

/* color */
.btn {
    background-color: #00cccc;
    color: #fff;
    border-color: #00a8a8;
    border-radius: 3px;
    cursor: pointer;
    box-shadow:
         1px  1px 4px rgba(255, 255, 255, 0.5) inset,            
        -1px -1px 4px rgba(000, 000, 000, 0.5) inset;
    }
.btn:hover {
    background-color: #00ebeb;
    }
.btn:active {
    box-shadow:
        -1px -1px 4px rgba(255, 255, 255, 0.5) inset,            
         1px  1px 4px rgba(000, 000, 000, 0.5) inset;
    }

/* green */
.btn_green {
    background-color: #009900;
    border-color: #009600;
    }
.btn_green:hover {
    background-color: #00c200;
    }

/* red */
.btn_red {
    background-color: #e00000;
    border-color: #c13d00;
    }
.btn_red:hover {
    background-color: #f00000;
    }
<!--
paulirish.com/2008/conditional-stylesheets-vs-css-hacks-answer-neither/
-->
<!--[if lt IE 7]> 
    <div class="no-js lt-ie9 lt-ie8 lt-ie7" lang="en">
<![endif]-->
<!--[if IE 7]>
    <div class="no-js lt-ie9 lt-ie8 ie7" lang="en">
<![endif]-->
<!--[if IE 8]>
    <div class="no-js lt-ie9 ie8" lang="en">
<![endif]-->
<!--[if gt IE 8]><!-->
    <div class="no-js no-ie" lang="en">
<!--<![endif]-->

<button class="btn btn_green btn_small ">
    Send
    <!--[if lte IE 8]> <span class="before"> </span><span class="after"> </span> <![endif]-->
</button>

<button class="btn">
    Buy
    <!--[if lte IE 8]> <span class="before"> </span><span class="after"> </span> <![endif]-->
</button>

<button class="btn btn_green">
    Activate
    <!--[if lte IE 8]> <span class="before"> </span><span class="after"> </span> <![endif]-->
</button>

<button class="btn btn_red">
    Delete
    <!--[if lte IE 8]> <span class="before"> </span><span class="after"> </span> <![endif]-->
</button>

</div>

Update 2:

I solved my problem, but the main question is still unanswered:

“Why don't filters work on pseudo elements in IE8?”

Started a bounty.

Update 3: I created testcase only for filters(and also -ms-filter) on ie8:

enter image description here

But the filters still don't want to work on pseudo-elements.

Update 4: I think Scotts answer is closest to truth.

Community
  • 1
  • 1
Vladimir Starkov
  • 19,264
  • 8
  • 60
  • 114
  • Is it really worth going trhu all that trouble? IE filters are pretty buggy as well as pseudo elements. I would either use an image or just let IE do its thing. Users won't even notice. – elclanrs May 01 '12 at 20:59
  • I totally agree with you, but the problem in another thing – Vladimir Starkov May 02 '12 at 06:32
  • 1
    +1 for your good questioning style. – yunzen May 18 '12 at 14:37
  • 5
    Am I the only who keep seeing disappearing gray dots in the image's crossings everytime? – BalusC May 19 '12 at 01:43
  • @BalusC You mean the `:focus` state? – user110857 May 19 '12 at 02:10
  • 3
    The public IE8 bug database is no longer available as far as I know, so your bounty question is probably not going to be answerable past "It's a bug" unless someone on the IE team comes along and intends to share. – BoltClock May 19 '12 at 04:49
  • @BoltClock about what public ie8 bug database you talk? – Vladimir Starkov May 19 '12 at 20:14
  • 1
    During the IE8 beta, Microsoft said they would be maintaining a public bug database where anybody could submit bugs for Microsoft to look at. It was... quite effective, but some obscure bugs made it through anyway. – BoltClock May 19 '12 at 22:33

5 Answers5

42

The question is "Why don't filters work on pseudo elements in IE8?" The following is as close to a definitive answer as I can muster. It comes from the information on this page.

The gradient filter is a "procedural surface" (along with alphaimageloader). A procedural surface is defined so:

Procedural surfaces are colored surfaces that display between the content of an object and the object's background.

Read that carefully. It is essentially another "layer" you might say between the content of an object and that object's background. Do you see the answer to the question? What is created by :before and :after... Yes! Content. Specifically as MSDN notes:

The ::before and ::after pseudo-elements specify the location of content before and after an element in the document tree. The content attribute, in conjunction with these pseudo-elements, specifies what is inserted.

The generated content interacts with other boxes as if they were real elements inserted just inside their associated element.

Now, if it is content that is generated, then it is not an "object" containing content, but the content itself (which happens to have some behavior similar to an element object that might contain content).

Thus, there is no "object" containing "content" (since it is content) between which the filter can place a procedural surface for content generated by a pseudo-element (i.e. "false element"). A gradient must be applied to the object, and then the procedural surface is placed between it and the content.

ScottS
  • 71,703
  • 13
  • 126
  • 146
  • I noticed MSDN interchangeably use the words 'property' and 'attribute' to describe `content`. Okay. – user110857 May 20 '12 at 02:33
  • @matmuchrapna--Well, there is probably no way to prove or not that my explanation is truly the reason, but as I read the information it sure seemed like the most logical conclusion to me. I'm glad you agree and satisfied your quest for an answer. – ScottS May 20 '12 at 19:13
6

The documentation on -ms-filter -a synonym for filter- states:

An object must have layout for the filter to render.

My first guess was that the :before content doesn't have hasLayout set to true. And while it's probably not set to true, it's probably not set to false either. For starters, when I followed the hasLayout docs to force the content to get hasLayout = true (see jsfiddle) it didn't solve anything.

So I'd say it's neither true nor false. Instead, it's probably undefined. I noted in the same docs it says about the source of this property:

object.currentStyle.hasLayout

If we have a look at the W3 documentation on the content property it says:

Generated content does not alter the document tree. In particular, it is not fed back to the document language processor (e.g., for reparsing).

So, a possible conclusion would be that the generated content is not an object, as such it does not have a currentStyle property, and thus also doesn't have hasLayout set to true. This would be the reason that filters don't work on the generated content, and thus answer the question.


At first sight I thought I had found a hint in the console of the above fiddle:

document.querySelectorAll('div')[0].currentStyle.hasLayout; 
// true

document.querySelectorAll('div:before')[0].currentStyle.hasLayout
// Unable to get value of the property 'currentStyle': 
// object is null or undefined

But as mentioned in the comments by @BoltClock: querySelectorAll cannot access pseudo-elements.


Another hint (though -again- nothing more than a hint) that filter won't work on pseudo-elements can be found in this msdn introduction on filters, stating (emphasis mine):

Filters are applied to HTML controls through the filter property

Although I'm not sure what is meant by "HTML controls", I wouldn't expect content generated by the :before pseudo-element to be considered a "HTML Control".

Jeroen
  • 60,696
  • 40
  • 206
  • 339
  • 1
    The bit about `querySelectorAll()` is a red herring, as [it can never access pseudo-elements](http://www.w3.org/TR/selectors-api/#processing-selectors). Naturally, as pseudo-elements aren't part of the DOM. – BoltClock May 19 '12 at 22:54
  • @BoltClock Ahh yes of course, thx! Then there's probably no "clean" way to find out if there's an internal `hasLayout` property on the generated content. I'll edit the question and put the link you mention there as well. – Jeroen May 19 '12 at 22:58
  • Would one be able to trigger `hasLayout` by applying `zoom:1` for pseudoelements? This is a curious find but I don't have access to an IE box from my home machine to test it. – Oleg May 19 '12 at 23:47
  • @o.v. AFAIK there's no reliable way to find out what the `hasLayout` property for pseudoelements is, in my answer I speculate it may not even have the property. Either way, if you open [this updated version of the jsfiddle](http://jsfiddle.net/7w7j9/1/) with `zoom:1` the `filter` still doesn't work in IE8. – Jeroen May 19 '12 at 23:57
  • While I disagree that any of your other points have anything to do with it, I do think you were half way to the answer when you stated "So, a possible conclusion would be that the generated content is not an _object_." I believe that to be half the issue based on the answer I just posted. – ScottS May 20 '12 at 01:08
  • @Jeroen your answer is awesome. May be you are right. By the way, [searching html controls on msdn](http://bit.ly/KQXzsu) resulted no more related materials about **html controls**, that's why i can't talk about exception out of description `Html controls`. That's why i prefer to trust paragraph on this [page](http://msdn.microsoft.com/en-us/library/ms532847%28v=vs.85%29.aspx), describing elements which cannot have filters: `embed`, `applet`, `select`, `option`, `tr`, `tHead`, `tBody` and `tFoot`. – Vladimir Starkov May 20 '12 at 09:56
0

Rather than using IE's filter style for this, have you considered using CSS3Pie?

This is a script for IE that adds support for standard CSS box-shadow and gradients, so you can write the same code in all browsers rather than having to have all those IE-specific styles.

Spudley
  • 166,037
  • 39
  • 233
  • 307
  • it fails on inset box-shadow, and also not work without javascript – Vladimir Starkov May 04 '12 at 21:09
  • Personally, if I'm only concerned about gradients, I'd rather use [the Ultimate CSS Gradient Generator](http://www.colorzilla.com/gradient-editor/) (supports IE6-9) and not deal with CSS3Pie's extra .js and .htc files. – user110857 May 19 '12 at 01:52
  • @gmeben i also use this generator http://www.colorzilla.com/gradient-editor/#ff0000+0,00ff15+100;Custom – Vladimir Starkov May 20 '12 at 07:47
0

I already gave my preferred solution (use CSS3Pie), but I'll post this as a separate answer.

The likely reason why IE8 fails to work with filter where IE7 works is because IE8 changed the syntax for filter.

filter is an IE-specific proprietary style. When Microsoft released IE8, they made a big point of trying to be "standards compliant". The "standards compliant" way of supporting a non-standard style is to give it a vendor prefix, and that is what Microsoft did.

So therefore in IE8, you need to do the following:

-ms-filter: "progid:DXImageTransform.Microsoft.gradient( startColorstr='#80ffffff', endColorstr='#00ffffff',GradientType=0 )";

IE7 doesn't support this syntax, so you need them both.

IE8 does in fact work with the old syntax in some cases. The cases where it doesn't work tend to be the ones where you use the progid: syntax. The reason for this is that the colon after progid causes it to be invalid CSS syntax, which is why MS added quotes around the whole thin for the IE8 -ms-filter version.

So the short answer is, use both versions in your stylesheets, and you'll be fine.

Spudley
  • 166,037
  • 39
  • 233
  • 307
  • >"The likely reason why IE8 fails to work with filter where IE7 works is because IE8 changed the syntax for filter." no, because on this [demo](http://jsfiddle.net/matmuchrapna/8M5Tt/61/) selector `.ie8 .btn:before` use correct syntax for ie8, and filter not applied – Vladimir Starkov May 05 '12 at 12:44
  • I agree with @matmuchrapna that `filter` syntax is not the issue. I believe it is based off the nature of a pseudo-element and the `gradient` filter as I outlined in my answer. – ScottS May 20 '12 at 01:05
  • i made update3 to the post, that proofs that filters are working in both syntax – Vladimir Starkov May 20 '12 at 07:52
  • fair enough. there definitely are times when IE8 prefers the `-ms-filter` syntqx, but if it's ok for you here either way, then that's also good. I'll leave the answer in place though, for future reference. – Spudley May 20 '12 at 08:33
0

Wow, this is a tough one.

After reviewing this chart, confirming that IE8 only likes single colons on its pseudo-elements, reading this possibly related blog article, and doing a lot of testing in jsFiddle (although, it's little in comparison to your 73?? jsFiddles), I would have to conclude that this is a bug in IE8.

IE9 can do gradients on pseudo-elements (with base64 nonsense) but IE8 is stubbornly broken.

BoltClock
  • 700,868
  • 160
  • 1,392
  • 1,356
user110857
  • 2,023
  • 1
  • 21
  • 33
  • IE8 not supporting CSS3 pseudo-element syntax doesn't have much to do with filters not working in pseudo-elements in the first place, besides the whole "IE is buggy" mindset :) – BoltClock May 19 '12 at 04:50
  • @BoltClock Good catch on the element/selector edit. I just wanted to point out all the necessary parts when working with pseudo-elements in IE8. Theoretically, when all these parts are considered, filters should work. The fact that they don't is what leads me to believe this is a bug (not necessarily because I have a mindset ahh!). – user110857 May 19 '12 at 14:23
  • @gmeben--I agree this was a tough one. I tend to disagree that IE8 is broken on this point (see my answer for why). – ScottS May 20 '12 at 01:02
  • @ScottS Good work. Your answer was well explained. Surely, though, and not to beat a dead horse, but because IE8 doesn't consider pseudo-elements as objects within its DOM further reinforces the idea that IE8 was poorly designed. – user110857 May 29 '12 at 18:40
  • 1
    @gmeben--Since "pseudo-element" means "false-element" (literally), and since it is designed to be merely for _content_ insertion (it technically would be a logical contradiction for mere content to contain content itself; like an apple _containing_ an apple, rather than _being_ one), and since IE defines their procedural surfaces as going _between_ the content and its object (they have the right to define the procedural surface how they want; its not part of the HTML/CSS spec), I'm not sure I could level a charge of "poorly designed" at IE8 for this particular issue (now, other issues...). – ScottS May 29 '12 at 19:21
  • @ScottS --I see. Perhaps we should abandon the label 'pseudo-element' then. Anyhow, in their documentation, their interchangeable use of the words 'property' and 'attribute' (which are two totally different concepts, of course) still indicates a deeper level of faultiness. – user110857 May 29 '12 at 19:40
  • 1
    @gmeben--yes, there incorrect use of 'property' and 'attribute' is not right. I'm not sure abandoning 'pseudo-element' is called for. It is, in fact, not a real element. You cannot put html into it, nor does it exist in the DOM tree as far as the html itself is concerned, so it is not "real" in the sense of being an html tag. When generated, it really is for visual purposes, like an illusion--it appears to be there, but is not really there (in the same sense as "real" elements). – ScottS May 29 '12 at 20:15
  • @ScottS If the team at IE considered pseudo-elements purely decorative, then they should've given them procedural surfaces on which to generate visuals like gradients. It feels as though they simply didn't do the work to make them support such a feature rather than instituting a design choice. – user110857 May 29 '12 at 20:33
  • 1
    @gmeben--You miss the point. It is not just IE that considers them "decorative," that is the whole purpose of them. They are purely for _presentational_ use, which is why they are part of CSS, not HTML. The original intention was simply to do [typographical change](http://www.w3.org/TR/REC-CSS1-961217#pseudo-classes-and-pseudo-elements). Later, with addition of `:before` & `:after`, it was to allow [generated _textual_ content](http://www.w3.org/TR/CSS2/generate.html#before-after-content) also for presentation. Web designer's have since capitalized on "blank" content to do many neat effects. – ScottS May 30 '12 at 00:04
  • While procedural surfaces are presentational/visual, they are also explicitly defined as between content and containing object. Since pseudo-elements are content, not containing object, it makes sense to me why the surface cannot be applied to them. – ScottS May 30 '12 at 00:06
  • @ScottS --I understand their purpose. I still find it strange that IE8 will render _background colors_ on content and yet gradients are excluded entirely because they decided they're undeserving of a procedural surface based on archaic definition. – user110857 May 30 '12 at 00:11
  • But background color is _not_ a `filter`, and not a procedural surface. Remember, background gradients did not come out in Firefox till 3.6 (in Jan 2010), whereas gradients via `filter` in IE go back to version 5.5 (in 2005!), and IE8 was released in March 2009. If anything, IE was ahead of its time in allowing for gradients. Now they are moving away from `filter` to align with other browsers (a good thing). However, you are entitled to your opinion about it. IE8 is now an aging browser, so it won't be a major factor at all in a few years. – ScottS May 30 '12 at 00:41