August 2nd, 2007
cpetersen
Introduction
PNGs are great, having a full 8 bits of alpha transparency makes designing web pages so much easier. That being said, I'm sure anyone reading this post is well aware of the gross inadequacies of Internet Explorer 6 in terms to the rendering of PNGs. IE7 has remedied the situation, however, according to Google Analytics, 6% of the visitors to this website still use IE6, and the situation is much worse for
our company website where a full 37% of visitors still use IE6. My point is, if we want to take advantage of the features PNGs give us, we have to account for the IE6 users.
We are in luck,
koivi.com has a write up on how we can use the Microsoft specific "DXImageTransform.Microsoft.AlphaImageLoader" css filter to load PNGs properly. The short version is, our image tags that used to look like:
|
<img width="128" height="128" src="example.png" alt="Example"/> |
must now look like (for IE6 only):
|
<img alt="Example" width="128" height="128" src="spacer.gif" style="filter: progid:DXImageTransform.Microsoft.AlphaImageLoader(src='example.png', sizingMethod='scale');" /> |
To make this process easier, I've written a Ruby on Rails method that handles the details for us.
png_tag
png_tag is the little brother of image_tag. It is actually two functions, first a function (ie?) to determine if the client is using IE, and if so what version. The second function (png_tag) acts just like image_tag, except when passed a png it will automatically use the DXImageTransform.Microsoft.AlphaImageLoader filter if the client is using IE6.
The first function is as follows:
1
2
3
4
5
6
|
def ie?
m = /MSIE\s+([0-9, \.]+)/.match(request.user_agent)
unless m.nil?
m[1].to_f
end
end |
This is a pretty rudimentary check, it just looks for "MSIE" in the user agent, if it finds it it returns the number after it (the version) otherwise it returns nil. Now that we have the ability to see if our user is using IE, we can tackle the second function:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
|
def png_tag(source, options = {})
options.symbolize_keys!
options[:src] = image_path(source)
options[:alt] ||= File.basename(options[:src], '.*').split('.').first.capitalize
if options[:size]
options[:width], options[:height] = options[:size].split("x") if options[:size] =~ %r{^\d+x\d+$}
options.delete(:size)
end
if ie? && ie? < 7 && options[:src] =~ /png$/i
src = options[:src]
options[:style] = "filter: progid:DXImageTransform.Microsoft.AlphaImageLoader(src='#{src}', sizingMethod='scale');"
options[:src] = image_path('spacer.gif')
end
"<img #{tag_options(options) if options} />"
end |
If this code looks a lot like the code for "image_tag" that because it basically is "image_tag" with the IE6 check. If we find IE6, we simply change out the image source for a spacer and add the filter code. Since it also checks that the source image is a PNG before adding the filter, you can use png_tag everywhere you would have used image_tag.
My Image Doesn't Show Up in IE?!?
I forgot to mention, there is one caveat. The "DXImageTransform.Microsoft.AlphaImageLoader" doesn't automatically size your "img" tag. So your image is showing up, it just happens to be 1 pixel by 1 pixel (the size of our spacer). This just means you have to specify to the size of your image ahead of time (a good practice anyway).
In the case where the programmer doesn't specify the size, you could use
this code to determine it at run time. I decided not to for my code, but keep that in mind if it is a feature you really need.
Using png_tag
I thought about bundling this as a plugin, but it just seemed like overkill. If you want to use png_tag, simply copy the two functions above into your application_helper.rb file, and copy the following spacer into your images directory:
right click here to save the 1x1 pixel spacer image.
I hope you find this code useful!
August 13th, 2007 at 12:43 AM Hi, thanks for tip, but I doubt it will work with any kind of caching since first variant would be cached.
August 13th, 2007 at 05:56 AM Hi Bojan, I've been using this technique for over 3 years (I originally wrote this as a JSP tag library) and I haven't witnessed a caching problem. If anyone does see this problem, please post here, maybe we can help! I have seen one problem with this method, if the PNG file isn't available, IE6 blocks while trying to download it. So if you have a separate image server that happens to be down, your website will appear very slow to IE6 users... oh yeah and it won't have images either. Thanks for the comment, Chris
August 24th, 2007 at 07:00 PM Thanks for a very useful add-on. Have you also tried the Painless PNG rails plugin? Best regards Philipp
August 27th, 2007 at 01:33 AM I had to remove the $ from the line 12 in the second method if ie? && ie?
December 2nd, 2007 at 09:38 AM Hi Christopher, Love your fix for pngs! I have used it for several months and recently tried to use it for pngs generated dynamically out of a controller action. The problem is that I don't know what the image size will be ahead of time. So the png_tag method won't work and Sam Stephenson's clever/fast size function won't work either. Nor does the Painless PNG Plugin (same reason). There is another way to use Microsoft's AlphaImageLoader that does not require prior knowledge of the image size. I added the code to use it into your method. In case you or someone else ever needs to do sizeless png transparency here's the enhanced method:
def png_tag(source, options = {}) options.symbolize_keys! options[:src] = image_path(source) options[:alt] ||= File.basename(options[:src], '.*').split('.').first.capitalize if options[:size] options[:width], options[:height] = options[:size].split("x") if options[:size] =~ %r{^\d+x\d+$} options.delete(:size) end if ie? && ie? " else content_tag("span", image_tag(options[:src], :style=>"filter:progid:DXImageTransform.Microsoft.Alpha(opacity=0)"), :style=>"filter:progid:DXImageTransform.Microsoft.AlphaImageLoader(src=#{options[:src]}, sizingMethod='crop');", :CONTENTEDITABLE=>true) end else "" end endDecember 2nd, 2007 at 09:40 AM Arf! The code didn't show up very well, did it? If someone wants it email me at jim {at jimjames {dot org or I'll post on my blog at blog.MyTripScrapbook.com Thanks again for doing the heavy lifting in the first place! Jim.
December 2nd, 2007 at 09:43 AM ooops, wrong blog! Right one: blog.p.latyp.us Sorry about that.
December 2nd, 2007 at 10:47 AM Hi Jim, Thanks for the post! I think that code will be useful for a lot of people. Chris
December 11th, 2007 at 07:35 PM thank you! I'll try this out!